[comp.lang.c++] Message dispatching

roberts@lobot.Tymnet.COM (Michael Roberts) (05/09/90)

Appologies if this has been covered or is particularly brain damaged.

----Synopsis:
C++ seems ill-suited to handling a certain type of message passing.
An example is presented.
A possible extension to C++ is offered.
Hopefully someone can point out an easier way that presently exists!
(Power readers may skip down to "----Extension to C++")

----Overview (real-world motivation):

I have a database of objects (all members of classes derived from a common
base class).  Some of these objects are members of a text class.  That
text class has methods to respond to editing messages.  Also in the
database are version class objects.  Version objects each contain a
pointer to a text class object, as well as data and methods for
responding to versioning commands.  My application uses version class
objects to provide the interface to the text class objects, and so
protect them from arbitrary editing which does not adhere to versioning
standards.

The natural impulse would be to say that since a version (pun intended ;-)
class object contains more information than a text class object, and since
we want the version class object to inhibit, override or modify the
behavior of the text class object it points to, let the version class be
derived from the text class, with "virtual" methods in the text class.
The problem is that there are many different classes besides the text
class that version class objects can "contain" (point to) and hence
protect.  I do not want to have distinct version classes derived from
each of those myriad classes; I want a single version class to handle all
such buffering.

----My present ugly solution:

The version class performs protective services that amount to message
routing and manipulation.  I have achieved this via a laborious process of
duplicating each and every message that a contained object can respond to
with a like-named method in the version class.  Almost all these version
methods are identical in basic form: they check and update certain version
data, then if conditions are met, send the appropriate editing message to
the contained object.  By design, that message always has the same name as
the version method itself.

----Extension to C++: default methods

What I think I want is a mechanism for imitating late binding in C++, that
operates outside the boundaries of inheritence.  (I hear the groans
already...)  Consider the following scenario.  Let the version class be
independent of the other classes.  Let it have one or more public methods
called default.  This would include default(...), and possibly overloaded
alternatives such as default(int).  The compiler would then resolve all
calls to unidentifiable methods in the version class as calls to these
default methods.  

The new semantics (and the compilier that reads it) would have to solve
the following problems:
  - identification of the actual method called
  - simple branching logic based on that identifier
  - means for calling a method with that same name for another object
  - means for returning the return value from that chained call

To identify which method is actually being called, a special function
would be made available inside the default methods (only):  default().
That routine would return a pointer-to-function.  The achievement of
simple branching logic would require allowing switch and case statements
to accept such pointers.  To pass the original arguments that were in the
incoming call to the outgoing call (if desired) and to return whatever it
is that that outgoing call returns, we might use the following
(incomplete) scheme:

class version
{
  private:
    data* pObj;                    // pointer to "contained" object
  public:
    // various explicit methods...
    default default(...);
}

default version::default(...)      // returns whatever it should!!!
{
    switch (default())             // default() returns int to match
    {                              // cases of the following style:

    case version::whatzit:         // equivalently, just "case whatzit:"
        //...                      // perform some desired side effect
        return;                    // whatzit NOT allowed to call through

    case version::foo:
        // Might read variable length arglist and do something particular
        // to this call, perhaps changing, removing or adding an argument.
        char c = 'b';
        return pObj->default(c);

    case version::bar:
        // Might even change call entirely:
        return someOtherPointer->someOtherMethod(someOtherArg);

    default:                       // its usual meaning: unknown case
        return pObj->default(...); // call "passed through", WITH ARGLIST
    }
}

main()
{
    char c = 'a';
    version v;
    v.foo(a);                      // ends up calling some Obj.foo('b');
}

It might be that such constructs are useful for other purposes, such as
pre- and -post routines to place around some methods in a class.

So, the questions are...
  - Does C++ already provide other mechanisms to do what I need?
  - Has someone else already suggested this?
  - Does anybody out there like the basic intent of what I am suggesting?
  - Is there a better way, or a more general way to express this?

To those of you who got this far, THANK YOU for your kind attention!

-Mike
----
Mike Roberts (roberts@rocky.tymnet.com) (408) 922-7931
Holding down my end of the bell curve... whichever end I'm on at the moment.

sdm@cs.brown.edu (Scott Meyers) (05/09/90)

In article <3550@tymix.UUCP> roberts@lobot.Tymnet.COM (Michael Roberts) writes:
: I have a database of objects (all members of classes derived from a common
: base class).  Some of these objects are members of a text class.  That
: text class has methods to respond to editing messages.  Also in the
: database are version class objects.  Version objects each contain a
: pointer to a text class object, as well as data and methods for
: responding to versioning commands.  My application uses version class
: objects to provide the interface to the text class objects, and so
: protect them from arbitrary editing which does not adhere to versioning
: standards.

[stuff deleted]

: The version class performs protective services that amount to message
: routing and manipulation.  I have achieved this via a laborious process of
: duplicating each and every message that a contained object can respond to
: with a like-named method in the version class.  Almost all these version
: methods are identical in basic form: they check and update certain version
: data, then if conditions are met, send the appropriate editing message to
: the contained object.  By design, that message always has the same name as
: the version method itself.

I think you're going about this backwards.  Instead of having class Version
police the interface to class Text (and other classes), have class Text
(and the other classes) police its own interface, drawing on services
provided by class Version.  The easiest way to do this is to have Text be a
derived class of Version.  Then the only functions you have to write for
Version are those that truly apply to Version objects -- you don't need to
duplicate the functions of the Text objects.  Each of the Text functions,
in turn, is responsible for calling the appropriate Version function to
validate its actions.  For example:

    class VersionObject {
      protected:
        Boolean verifyEdit();
        ...
    };

    class TextObject: public VersionObject {
      public:
        void edit();
        ...
    };

    void TextObject::edit() {
      if (this->verifyEdit())
        // do the editing
      else
        // issue an error
    }

Class Version shouldn't have too many member functions if, as you imply,
most of the checking is pretty standard -- you just write a function for
each type of checking required.  An additional advantage of this approach
is that you can separate out the data in class Text that is primarily used
for validation and migrate it up into class Version.

Scott

      
        

robins@keyboard.esd.sgi.com (Robin Schaufler) (05/10/90)

Mike Roberts proposes a language extension for C++ late binding.
I have a similar need for late binding, although I have very different
problems to solve.  First a description of the problems I'd like to
have late binding for, then a different idea for a language extension.

The first problem is that if the code for the methods for an object is
local to my process, I'd like to just do a function call to a member function,
but if it's in another process, I'd like its member functions to perform
rpc to the other process.

The second problem is that if I have an object in shared memory, I'd like to
invoke a virtual member function, and have it look up the address of
the function based on both the member function index and the pid.  Current
ugly solution is to require the code to be in a shared library, and therefore
at the same address in both processes, but that's excess mechanism, and
shared libraries at SGI don't support breakpoints.

About Mike Roberts' problem...  I don't think database clients want to
know about version objects.  It seems more "natural" to call member functions
of the text object, and want the version function to be automatically
interposed on it.

So I'd rather see a runtime interface to virtual function tables.
For instance, suppose the text class in Mike's example declares the
version class a friend.  A version object might save the old member
functions as data, and replace them with new member functions that
do their thing and then call the old member functions.  There would
have to be a way to map from the text object to the version object,
but that could be done either by sticking a pointer in the base class
or with a static table.

This doesn't quite solve the problem with the object in shared memory
because it doesn't change the algorithm for vtable lookup.  But given
that Dewhurst and Stark publishes a _new_handler to override error
handling behavior of new, it seems to me that there could be an analogous
mechanism for overriding vtable lookup.  It could even be done as a
member function, so long as it isn't virtual.

Our issues are as burning for us as this version control issue is for
Mike Roberts, so I'd appreciate answers to his questions, too.
To reiterate his questions, they are:
  - Does C++ already provide other mechanisms to do what I (we) need?
  - Has someone else already suggested this?
  - Does anybody out there like the basic intent of what I am suggesting?
  - Is there a better way, or a more general way to express this?

To those of you who got this far, THANK YOU for your kind attention!
And thank you, Mike, for breaking the ice.

Robin Schaufler
Standard: the personal flag of the ruler of a state; loosely, a banner.
- Webster's New Practical Dictionary

olson@anchor.sgi.com (Dave Olson) (05/10/90)

In article <7484@odin.corp.sgi.com> robins@keyboard.esd.sgi.com (Robin Schaufler) writes:


| invoke a virtual member function, and have it look up the address of
| the function based on both the member function index and the pid.  Current
| ugly solution is to require the code to be in a shared library, and therefore
| at the same address in both processes, but that's excess mechanism, and
| shared libraries at SGI don't support breakpoints.

I don't have any comments on the rest of your message, since I'm
not really a C++ person, but we DO support breakpoints in shared
library routines, at least in 3.3.


	Dave Olson

Life would be so much easier if we could just look at the source code.