home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!spool.mu.edu!sdd.hp.com!cs.utexas.edu!tamsun.tamu.edu!tamsun.tamu.edu!news
- From: pinky@tamu.edu (The Man Behind The Curtain)
- Newsgroups: comp.lang.c++
- Subject: Criticisms Wanted:Callbacks&Runtime methods
- Date: 12 Dec 1992 02:05:05 -0600
- Organization: Texas A&M University
- Lines: 227
- Sender: ski8032@tamsun.tamu.edu
- Message-ID: <1gc6fhINN6nr@tamsun.tamu.edu>
- NNTP-Posting-Host: tamsun.tamu.edu
- Keywords: callbacks C++ Runtime methods
-
-
- A few days ago, I posted asking how one could model calling a method
- of an arbitary object without knowing whether or not that method
- existed for that object. Basically, I wanted to know if one could
- have a runtime determination of an object's methods.
-
- Two responses pointed out the errors in the code I presented -- while
- I thank them for this, I understood why it wouldn't work; the code
- was an attempt to show what exactly I was trying to do in case my
- verbose explanation was insufficient. A third response via email
- was most helpful. It explained why this wasn't possible in C++,
- though one could code a class with this capability. In addition,
- in the December 92 C++ supplement to Dr. Dobb's Journal of Orthodontia,
- some articles applied directly to what I was asking for:
-
- p.36s
- "By requiring that every member function in a class first be declared
- in the class statement, C++ prevents requests from being made of an
- object that can't handle them. Smalltalk and Lisp are less able to
- detect errors like these at compile time and provide mechanisms for
- dealing with inappropriate object requests during program execution."
-
- But these capabilities are useful, and even necessary when you try
- to model the real world. For instance, might double click a key, and
- then select from the screen any object to try to unlock with the key,
- be it a bear, bush, or door. These classes are mostly unrelated, but
- in order to be able even attempt the operation, the base class of such
- objects must at least have method for unlocking. If objects can
- interact in a wide variety of ways (a large number of verbs), then
- the base class would contain an unweildy number of virtual functions.
-
- The second thing that I wanted was for an object to be able to
- query the object which calls it, or to act upon the agent verbing it.
- (ie. the door could getColor() the key). The article Using Multiple
- Inheritance in C++ was fairly useful in this regard, but it seemed
- that the call backs could only be those callbacks anticipated at
- compile time, rather than handling such interactions to occur at runtime.
-
- Now, the reply I received suggested that I might impliment these
- features using a static table of functions within my objects.
- While this might be one way, I believe that the
- following impliments both callbacks and runtime methods in
- an acceptable way, adding the overhead of an additional switch and
- function call to a normal message. More controversially, I structured
- the main program itself to be an object subject to callbacks for
- greater consistency in calling methods, with the beneficial side
- affect that it becomes easier to model forking concurrent copies of
- your program.
-
- What I would like are criticisms, not of the code itself (which is
- meant to be illustrative, not documented or structured as it will be
- nor with real classes) but of the model which I am using, the way of
- representing how objects are interacting.
-
- // callback.cpp - sam inala dec 92
- //
-
- #include <iostream.h>
- #include <assert.h>
-
- // Macros for declaring verbs available for class
-
- // Example of declaring syntax for class LockedDoor, with verbs Open & Lock
- // if include file is missing, it will generate an error.
- //
- // DECLARE(LockedDoor) verb(Lock); verb(Open); noverb;
-
- #define DECLARE(c) int c::perform(verb act, Object *agent) { switch(act) {
- #define verb(v) case Can##v: return v(agent);
- #define noverb default: return 0; } }
-
- // Macro: call(obj, v)
- // Purpose: to verb an object from within another object
- //
- // Parameters: obj -> must be a Object * or pointer to a derived class of
- // Object
- // v -> A member of the verbs listed in enum verb with the
- // prefix "Can" removed. Otherwise, 'undefined symbol'
- // error generated.
- //
- // Returns: 0 -> method DNE (Does not Exist)
- // -1 -> method exists; call failed for some reason
- // else return value of call
- //
- // Warnings: 1. call(obj,verb) will not work when called from a
- // function or main, since the "this" pointer DNE.
- //
- // 2. infinite mutual recursion between two objects is possible
-
- #define call(obj,v) ((obj->perform((Can##v), (this))))
-
- // Verb List
-
- enum verb { // the token 'verb' is overloaded w/ macro verb(v),
- // but this does not appear to be a problem
-
- DoNothing, // This verb will never be called.
- CanOpen,
- CanLock,
- CanBurn,
- CanMove
- }; // end verb list
-
- class Object {
- public:
- virtual int perform(verb, Object *) = 0;
- };
-
- // Note: it may be necessary to make perform() take multiple parameters,
- // since some verbs require information implicitly (ie. burn at what
- // temperature?, move to where?) This might be avoided by giving the
- // calling object methods like getTemperature() which the direct object
- // could call back when needing the temperature.
-
- class LockAble : public virtual Object {
- public:
- int perform(verb, Object *);
- int Lock(Object *agent) { cout << "Locking..."; return 1; }
- };
-
- DECLARE(LockAble) verb(Lock); noverb;
-
- class OpenAble : public virtual Object {
- public:
- int perform(verb, Object *);
- int Open(Object *agent) { cout << "Opening..."; return 1; }
- };
-
- DECLARE (OpenAble) verb(Open); noverb;
-
- class LockedDoor : public LockAble, public OpenAble {
- public:
- int perform(verb, Object *);
- int Open(Object *agent) { cout << "The door creaks open...";
-
- // if (agent != NULL) // calls should not need to check for NULL ptrs,
- assert(agent != NULL) // since World may receive call backs; and the
- // call(obj,v) interface should only allows
- // valid calls.
-
- call(agent,Move); // example of call-back (Should check return values)
- return 1;
- }
- };
-
- DECLARE(LockedDoor) verb(Lock); verb(Open); noverb;
-
- class World : public Object { // No classes should be derived from this.
- // nor multiple instantiations - no enforcement
- // of this as of yet.
- private:
- LockAble *lobj;
- OpenAble *oobj;
- LockedDoor *ldobj;
- Object *obj;
- public:
- World() {
- lobj = new LockAble;
- oobj = new OpenAble;
- ldobj = new LockedDoor;
- obj = (Object *) ldobj;
- }
- int perform(verb, Object *);
- int main(void); // in a concurrency model, this might be
- }; // the method heartbeat()
-
- inline DECLARE(World) noverb; // Inline b/c frequent use and simple func.
-
- int World::main(void) { // Here is the main program -- weirdness indeed!
-
- call(obj, Open);
-
- return 0;
- }
-
- int main(void) {
- return // Brave
- (new World) -> main(); // Note: if multiple instantiations
- // of World, each with independent
- // input device, then a concurrency
- // model (simulation) is possible
- }
-
- /* Other limitations: no construct similar to
-
- if method Lock() exists for object x
- /* x -> Lock() */
- else
- say 'Nothing Happens.'
-
- Because one may only call methods, not simply check their existance,
- except after the fact. This could be implimented by a macro parallel
- to DECLARE..verb..noverb. But this seems clumsy.
-
- Also, how to model an object moving itself from one container
- to another using this framework.
- */
-
- Using this, objects may do things like:
- MagicMetalDoor::Unlock() {
- if (BLUE == call(agent, getColor)) // Range of Blue:1..whatever.
- {
- DrawOpeningDoor();
- solidity = PASSABLE;
- ..
- return 1; // Unlock called: success
- }
- else return -1; // Unlock called: failure
- }
-
- A couple of things might have been useful while developing this, and
- I was curious whether or not they might be incorporated in C++ at
- any time. For instance, macros with scope might have been useful,
- or macros which could truly handle variable # or parameters. Something
- like #macro mymacro(a,b,...) or any more sophisticated macro processing.
- A macro language would be incredibly useful, but that'd be dreaming.
-
- In addition, is there a standardized convention yet for persistent
- objects & runtime type identification? What about transmitting objects
- from a defined set of classes over a network?
-
-
- --
- Till next time, \o/ \o/
- V \o/ V email:pinky@tamu.edu
- <> Sam Inala <> V
-
-