home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #30 / NN_1992_30.iso / spool / comp / lang / cplus / 17849 < prev    next >
Encoding:
Text File  |  1992-12-12  |  8.6 KB  |  240 lines

  1. Path: sparky!uunet!spool.mu.edu!sdd.hp.com!cs.utexas.edu!tamsun.tamu.edu!tamsun.tamu.edu!news
  2. From: pinky@tamu.edu (The Man Behind The Curtain)
  3. Newsgroups: comp.lang.c++
  4. Subject: Criticisms Wanted:Callbacks&Runtime methods
  5. Date: 12 Dec 1992 02:05:05 -0600
  6. Organization: Texas A&M University
  7. Lines: 227
  8. Sender: ski8032@tamsun.tamu.edu
  9. Message-ID: <1gc6fhINN6nr@tamsun.tamu.edu>
  10. NNTP-Posting-Host: tamsun.tamu.edu
  11. Keywords: callbacks C++ Runtime methods
  12.  
  13.  
  14. A few days ago, I posted asking how one could model calling a method
  15. of an arbitary object without knowing whether or not that method
  16. existed for that object.  Basically, I wanted to know if one could
  17. have a runtime determination of an object's methods.
  18.  
  19. Two responses pointed out the errors in the code I presented -- while
  20. I thank them for this, I understood why it wouldn't work; the code
  21. was an attempt to show what exactly I was trying to do in case my
  22. verbose explanation was insufficient.  A third response via email
  23. was most helpful.  It explained why this wasn't possible in C++,
  24. though one could code a class with this capability.  In addition,
  25. in the December 92 C++ supplement to Dr. Dobb's Journal of Orthodontia,
  26. some articles applied directly to what I was asking for:
  27.  
  28. p.36s
  29. "By requiring that every member function in a class first be declared
  30. in the class statement, C++ prevents requests from being made of an
  31. object that can't handle them.  Smalltalk and Lisp are less able to
  32. detect errors like these at compile time and provide mechanisms for
  33. dealing with inappropriate object requests during program execution."
  34.  
  35. But these capabilities are useful, and even necessary when you try
  36. to model the real world.  For instance, might double click a key, and
  37. then select from the screen any object to try to unlock with the key,
  38. be it a bear, bush, or door.  These classes are mostly unrelated, but
  39. in order to be able even attempt the operation, the base class of such
  40. objects must at least have method for unlocking.  If objects can
  41. interact in a wide variety of ways (a large number of verbs), then
  42. the base class would contain an unweildy number of virtual functions.
  43.  
  44. The second thing that I wanted was for an object to be able to
  45. query the object which calls it, or to act upon the agent verbing it.
  46. (ie. the door could getColor() the key).  The article Using Multiple
  47. Inheritance in C++ was fairly useful in this regard, but it seemed
  48. that the call backs could only be those callbacks anticipated at
  49. compile time, rather than handling such interactions to occur at runtime.
  50.  
  51. Now, the reply I received suggested that I might impliment these
  52. features using a static table of functions within my objects.
  53. While this might be one way, I believe that the
  54. following impliments both callbacks and runtime methods in
  55. an acceptable way, adding the overhead of an additional switch and
  56. function call to a normal message.  More controversially, I structured
  57. the main program itself to be an object subject to callbacks for
  58. greater consistency in calling methods, with the beneficial side
  59. affect that it becomes easier to model forking concurrent copies of
  60. your program.
  61.  
  62. What I would like are criticisms, not of the code itself (which is
  63. meant to be illustrative, not documented or structured as it will be
  64. nor with real classes) but of the model which I am using, the way of
  65. representing how objects are interacting.
  66.  
  67. // callback.cpp - sam inala dec 92
  68. //
  69.  
  70. #include <iostream.h>
  71. #include <assert.h>
  72.  
  73. // Macros for declaring verbs available for class
  74.  
  75. // Example of declaring syntax for class LockedDoor, with verbs Open & Lock
  76. // if include file is missing, it will generate an error.
  77. //
  78. // DECLARE(LockedDoor) verb(Lock); verb(Open); noverb;
  79.  
  80. #define DECLARE(c) int c::perform(verb act, Object *agent) { switch(act) {
  81. #define verb(v) case Can##v: return v(agent);
  82. #define noverb default: return 0; } }
  83.  
  84. // Macro: call(obj, v)
  85. // Purpose: to verb an object from within another object
  86. //
  87. // Parameters: obj -> must be a Object * or pointer to a derived class of
  88. //                    Object
  89. //             v   -> A member of the verbs listed in enum verb with the
  90. //                    prefix "Can" removed.  Otherwise, 'undefined symbol'
  91. //                    error generated.
  92. //
  93. // Returns: 0 -> method DNE (Does not Exist)
  94. //         -1 -> method exists; call failed for some reason
  95. //         else  return value of call
  96. //
  97. // Warnings: 1. call(obj,verb) will not work when called from a
  98. //                 function or main, since the "this" pointer DNE.
  99. //
  100. //           2. infinite mutual recursion between two objects is possible
  101.  
  102. #define call(obj,v) ((obj->perform((Can##v), (this))))
  103.  
  104. // Verb List
  105.  
  106. enum verb {    // the token 'verb' is overloaded w/ macro verb(v),
  107.                 // but this does not appear to be a problem
  108.  
  109.     DoNothing, // This verb will never be called.
  110.     CanOpen,
  111.     CanLock,
  112.     CanBurn,
  113.     CanMove
  114. };  // end verb list
  115.  
  116. class Object {
  117. public:
  118.     virtual int perform(verb, Object *) = 0;
  119. };
  120.  
  121. // Note: it may be necessary to make perform() take multiple parameters,
  122. // since some verbs require information implicitly (ie. burn at what
  123. // temperature?, move to where?)  This might be avoided by giving the
  124. // calling object methods like getTemperature() which the direct object
  125. // could call back when needing the temperature.
  126.  
  127. class LockAble : public virtual Object {
  128. public:
  129.     int perform(verb, Object *);
  130.     int Lock(Object *agent) { cout << "Locking..."; return 1; }
  131. };
  132.  
  133. DECLARE(LockAble) verb(Lock); noverb;
  134.  
  135. class OpenAble : public virtual Object {
  136. public:
  137.     int perform(verb, Object *);
  138.     int Open(Object *agent) { cout << "Opening..."; return 1; }
  139. };
  140.  
  141. DECLARE (OpenAble) verb(Open); noverb;
  142.  
  143. class LockedDoor : public LockAble, public OpenAble {
  144. public:
  145.     int perform(verb, Object *);
  146.     int Open(Object *agent) { cout << "The door creaks open...";
  147.  
  148.     // if (agent != NULL) // calls should not need to check for NULL ptrs,
  149.     assert(agent != NULL) // since World may receive call backs; and the
  150.                               // call(obj,v) interface should only allows
  151.                               // valid calls.
  152.  
  153.     call(agent,Move); // example of call-back (Should check return values)
  154.     return 1;
  155.     }
  156. };
  157.  
  158. DECLARE(LockedDoor) verb(Lock); verb(Open); noverb;
  159.  
  160. class World : public Object { // No classes should be derived from this.
  161.                               // nor multiple instantiations - no enforcement
  162.                               // of this as of yet.
  163. private:
  164.     LockAble *lobj;
  165.     OpenAble *oobj;
  166.     LockedDoor *ldobj;
  167.     Object *obj;
  168. public:
  169.     World() {
  170.         lobj = new LockAble;
  171.         oobj = new OpenAble;
  172.         ldobj = new LockedDoor;
  173.         obj = (Object *) ldobj;
  174.     }
  175.     int perform(verb, Object *);
  176.     int main(void);           // in a concurrency model, this might be
  177. };                            // the method heartbeat()
  178.  
  179. inline DECLARE(World) noverb;  // Inline b/c frequent use and simple func.
  180.  
  181. int World::main(void) {  // Here is the main program -- weirdness indeed!
  182.  
  183.     call(obj, Open);
  184.  
  185.     return 0;
  186. }
  187.  
  188. int main(void) {
  189.         return  // Brave
  190.                 (new World) -> main();  // Note: if multiple instantiations
  191.                                         // of World, each with independent
  192.                                         // input device, then a concurrency
  193.                                         // model (simulation) is possible
  194. }
  195.  
  196. /* Other limitations: no construct similar to
  197.  
  198.     if method Lock() exists for object x
  199.         /* x -> Lock() */
  200.     else
  201.         say 'Nothing Happens.'
  202.  
  203.   Because one may only call methods, not simply check their existance,
  204.   except after the fact.  This could be implimented by a macro parallel
  205.   to DECLARE..verb..noverb.  But this seems clumsy.
  206.  
  207.   Also, how to model an object moving itself from one container
  208.   to another using this framework.
  209. */
  210.  
  211. Using this, objects may do things like:
  212. MagicMetalDoor::Unlock() {
  213.     if (BLUE == call(agent, getColor)) // Range of Blue:1..whatever.
  214.     {
  215.         DrawOpeningDoor();
  216.         solidity = PASSABLE;
  217.         ..
  218.         return 1; // Unlock called: success
  219.     }
  220.     else return -1; // Unlock called: failure
  221. }
  222.  
  223. A couple of things might have been useful while developing this, and
  224. I was curious whether or not they might be incorporated in C++ at
  225. any time.  For instance, macros with scope might have been useful,
  226. or macros which could truly handle variable # or parameters.  Something
  227. like #macro mymacro(a,b,...) or any more sophisticated macro processing.
  228. A macro language would be incredibly useful, but that'd be dreaming.
  229.  
  230. In addition, is there a standardized convention yet for persistent
  231. objects & runtime type identification?  What about transmitting objects
  232. from a defined set of classes over a network?
  233.  
  234.  
  235. -- 
  236. Till next time,                \o/   \o/
  237.                                 V \o/ V     email:pinky@tamu.edu
  238. <>  Sam  Inala  <>                 V
  239.  
  240.