Topic: Q: Generic Callbacks -- "Object->*func(...)


Author: hickeyr@ibm.net (Rich Hickey)
Date: 1996/02/21
Raw View
[Moderator's note: my apologies for letting this thread continue,
 since it has strayed from the topic of C++ standardization.  I
 approved this article since I think it would be unfair to deny the
 poster a right of reply in this case (perhaps I shouldn't have
 approved the article to which he is replying).  Anyway, please try to
 make our job as moderators easier by making sure that articles
 submitted are appropriately focussed for this group.  -fjh.]

In message <312495AD.757A@ebc.ericsson.se> - Bjorn Fahller
<ebcbear@ebc.ericsson.se> writes:
:>
:>David Byrden <100101.2547@compuserve.com> wrote:
:>>
:>> >> I'd like to be able to call "DoIt" with any type of object and a
:>> >> pointer to any of that object's member functions that match the
:>> >> 'parmlist'
:>>
:>>  In the C++ Report, February 1995, Richard Hickey suggested a library of
:>> universal callback functors which would encapsulate both an object
:>> reference and a pointer to a member function of that object.
:>>
:>>  The caller, and the provider of the callback, would not have to share
:>> any type information other than the callback library itself.
:>
:>Unfortunately the implementation is all but clean, and depends
:>heavily on the compiler used. Internally, everything is
:>casted to void*. When I tried the implementation on Sun SparcWorks,
:>it failed, since pointer to member functions cannot be casted
:>to void* without severe loss of precision (they're twise
:>as long as void*.)
:>
:>The ideas are still usable, though. Those who are interested
:>can e-mail me for a simple example (not quite as slick as the
:>original, but not compiler dependant since it doesn't use
:>ugly casts.)
:>   _
:>/Bjorn.

Unfortunately you must not have understood the implementation.
I admit it is complex, difficult-to-read C++ code, yet it is
quite clean, and at no point are ptrs-to-members cast to void*.
If it doesn't work with your compiler I suggest your compiler
is broken.

I suggest you re-read the source quite carefully and make sure
you understand it before you criticize it. The callback library
is being used successfully by many experienced C++ developers.
No one has reported a language-level problem with it.

Anyone interested in the library source can decide for
themselves. It is freely available at:
http://ourworld.compuserve.com/HomePages/RichHickey

Rich Hickey
---
[ To submit articles: try just posting with your news-reader.
                      If that fails, use mailto:std-c++@ncar.ucar.edu
  FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html
  Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu.
]





Author: jones@cais.cais.com (Ben Jones)
Date: 1996/02/21
Raw View
brian (b.c.) white (bcwhite@bnr.ca) wrote:
: Does the C++ standard allow for a generic callback to be specified?

: Basically, I'd like to be able to pass an arbitrary object and function of
: that object to be called at some later time.  For example:

: void DoIt(ANYOBJECT* Object, void (ANYOBJECT::*CallBack)( ...parmlist... ))
: {
:  [...]
:  Object->*CallBack( ...parms... );
:  [...]
: }


: I'd like to be able to call "DoIt" with any type of object and a pointer to
: any of that object's member functions that match the 'parmlist'.  As it
: stands, any object passed in would have to be in the same class hierarchy
: as ANYOBJECT, thus basically requiring a unified class hierachy in order to
: do this genericly.

: [snip snip]

: So...  Is this possible?


The following kluge works on all platforms that I have tried it on
including Sun, SGI, Alpha, Macintosh, PC:

    #include <stdarg.h>

    class Callback
    {
    public:
      typedef void (Callback::*MF)();
      typedef void (*F)();

      Callback(F ff=0)
 {
   p = 0;
   f = ff;
 }
      Callback(void *pp,...)
 {
   va_list ap; va_start(ap,pp);
   p=(Callback*)pp;
   mf = va_arg(ap,MF);
 }
      virtual void operator()()
 {
   if (p)
     (p->*mf)();
   else if (f)
     f();
 }

    private:
      Callback *p;
      union { MF mf; F f; };
    };

Essentially, the "Callback" class consists of an object pointer and a
function pointer.  The constructors provide for two cases: a global
function (which causes a null object pointer to be stored) or a paired
object and member function pointer.  The parenthesis operator is
overloaded so that a Callback object may be executed as though it were
a function pointer.  It is declared virtual so that virtual callback
functions will work properly.

Now we can set up a callback very easily:

    Callback mycallback;
        ...
    mycallback = function;
        ...
    mycallback = Callback(&object,&classname::memberfunction);
        ...
    mycallback();

The above class can be defined with a template or macro such that
you can make variations on it for callback functions with various
argument types.

It is not typesafe.  It is your responsibility to make sure that
"object" and "memberfunction" are compatible.  However, as I said
before, it works everywhere that I've tried it.

    ------------

A simple extension to C++ would allow for a perfectly typesafe
callback scheme.  All that is necessary is that the following
expressions:

    object.function
    pointer->function

be LEGAL without having to be followed by ().  For them to be
legal there needs to be a container which can hold the values
of those expressions.  I would suggest:

    type (::*callback)(type1,type2,...)

That is, if you specify "::*" without a qualifying classname,
then "callback" can accept an expression like "object.function"
provided that "function" has a prototype of "type(type1,type2,..)".
Simple as that.

ANSI commitee please take note.

Ben Jones
Hughes Information Technology
bjones@eos.hitc.com
jones@cais.com
---
[ To submit articles: Try just posting with your newsreader.  If that fails,
                      use mailto:std-c++@ncar.ucar.edu
  FAQ:    http://reality.sgi.com/employees/austern_mti/std-c++/faq.html
  Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: phalpern@truffle.ultranet.com (Pablo Halpern)
Date: 1996/02/21
Raw View
"brian (b.c.) white" <bcwhite@bnr.ca> wrote:

>Does the C++ standard allow for a generic callback to be specified?
>
>Basically, I'd like to be able to pass an arbitrary object and function of
>that object to be called at some later time.  For example:

This is a problem I'd like to see addressed directly in the language or
in the standard library (although I doubt it will be).

A former collegue of mine suggested to me that the semantics of taking
the address of a member function in C++ could be much more powerfull
than they are. In C++, a function is not bound until it is invoked using
the object.function() or pointer->function() construct. What he
suggested is that there be a special type of pointer that refers to a
bound function. (He called it a closure, although I'm not sure it meets
the precise definition of closure, since parameters are not specified.)
Usage would be something like this:

  // The use of the "bound" keyword, below, is for illustrative purposes
  // only and is not intended as a syntax proposal.
  typedef bound void (*boundfunc)(args);  // Pointer to bound function

  class firstClass
  {
    public:
      void func1(args);
      void func2(args);
  };

  class secondClass
  {
    public:
      void func3(args);
  };

  f()
  {
    firstClass firstObj;
    secondClass secondObj;
    boundfunc pf = &(firstObj.func1);
    pf(actual args);  // calls firstObj.func1(actual args)
    pf = &(secondObj.func3);
    pf(actual args);  // calls secondObj.func3(actual args)
  }

The address of functions will be either a normal pointer to function, a
pointer to member function, or a pointer to bound function as follows:

   &globalFunc         // pointer to global/static function
   &firstClass::func2  // pointer to member function (of firstClass)
   &firstObj.func2     // pointer to bound function (bound to firstObj)
   &pointer->func1     // pointer to bound function (bound to *pointer)

I believe that the above could be a reasonable addition to the language:

1. It is useful
2. I think it is easy to implement. The internal structure of a bound
pointer is basically an object pointer and a pointer to the actual code
of the member function (no need for offsets or vtbl references, since
the binding has already been done). All object offset calculations are
done before the object pointer is stored. Global and static function
pointers can be stored in a bound pointer by setting the object pointer
to NULL.
3. It will not break existing code (the &(object.function) notation is
currently illegal).
4. I'm not sure that this cannot be achieved any other way, but I think
that it may not be achievable efficiently any other way.

-------------------------------------------------------------
Pablo Halpern                   phalpern@truffle.ultranet.com

I am self-employed. Therefore, my opinions *do* represent
those of my employer.
---
[ To submit articles: try just posting with your news-reader.
                      If that fails, use mailto:std-c++@ncar.ucar.edu
  FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html
  Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu.
]





Author: "brian (b.c.) white" <bcwhite@bnr.ca>
Date: 1996/02/14
Raw View
Does the C++ standard allow for a generic callback to be specified?

Basically, I'd like to be able to pass an arbitrary object and function of
that object to be called at some later time.  For example:

void DoIt(ANYOBJECT* Object, void (ANYOBJECT::*CallBack)( ...parmlist... ))
{
 [...]
 Object->*CallBack( ...parms... );
 [...]
}


I'd like to be able to call "DoIt" with any type of object and a pointer to
any of that object's member functions that match the 'parmlist'.  As it
stands, any object passed in would have to be in the same class hierarchy
as ANYOBJECT, thus basically requiring a unified class hierachy in order to
do this genericly.

Here is a more complex example that actually stores the object and member
function pointer so it can be called at a later time:

typedef void (ANYOBJECT::*CallBackFunc)( ...parmlist... );
ANYOBJECT* MyClass::CallObj;
CallBackFunc MyClass::CallFunc;

void MyClass::Set(ANYOBJECT* Object, CallBackFunc* Func)
{
 CallObj = Object;
 CallFunc= Func;
}

void MyClass::Call( ...parms... )
{
 CallObj->*Func( ...parms... );
}


Templates do not work well because the type of object to be stored could
be based on the internal state of the program and not know until run-time,
long after the actual instance of "MyClass" has been created.

So...  Is this possible?


                                        Brian
                                 ( bcwhite@bnr.ca )

-------------------------------------------------------------------------------
    In theory, theory and practice are the same.  In practice, they're not.
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]





Author: David Byrden <100101.2547@compuserve.com>
Date: 1996/02/15
Raw View

>> I'd like to be able to call "DoIt" with any type of object and a
>> pointer to any of that object's member functions that match the
>> 'parmlist'


 In the C++ Report, February 1995, Richard Hickey suggested a library of
universal callback functors which would encapsulate both an object
reference and a pointer to a member function of that object.

 The caller, and the provider of the callback, would not have to share
any type information other than the callback library itself.

                         David


[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  summarized in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
]





Author: Bjorn Fahller <ebcbear@ebc.ericsson.se>
Date: 1996/02/19
Raw View
David Byrden <100101.2547@compuserve.com> wrote:
>
> >> I'd like to be able to call "DoIt" with any type of object and a
> >> pointer to any of that object's member functions that match the
> >> 'parmlist'
>
>  In the C++ Report, February 1995, Richard Hickey suggested a library of
> universal callback functors which would encapsulate both an object
> reference and a pointer to a member function of that object.
>
>  The caller, and the provider of the callback, would not have to share
> any type information other than the callback library itself.

Unfortunately the implementation is all but clean, and depends
heavily on the compiler used. Internally, everything is
casted to void*. When I tried the implementation on Sun SparcWorks,
it failed, since pointer to member functions cannot be casted
to void* without severe loss of precision (they're twise
as long as void*.)

The ideas are still usable, though. Those who are interested
can e-mail me for a simple example (not quite as slick as the
original, but not compiler dependant since it doesn't use
ugly casts.)
   _
/Bjorn.
--
Bjorn Fahller                  Tel: +46 8 4220898 /
NA/EBC/F/SNM/T                 -------------------            ^
Ericsson Business Networks AB /                               |
S-131 89 Stockholm/SWEDEN    / Clever quote goes right here --+
---
[ To submit articles: Try just posting with your news-reader.
                      If that fails, use mailto:std-c++@ncar.ucar.edu
  FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html
  Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]