Topic: Generic callback member-function handling


Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 23 May 1994 09:05:27 GMT
Raw View
hugp@actrix.gen.nz (Peter Hug) writes:

>Since I am using multiple independant class hierarchies in my project,
>I find it difficult to implement generic classes who's instances act
>as servers to potentially any type of client object. Client objects
>typically create server objects, register themself and install callbacks
>(client member-functions). Although my generic class knows the address
>of an object and an address of one of the objects non-static member-
>functions, I cannot send the message like:
>
>typedef    void (CouldBeAnyClass::*MEMBERFUNC) (void);
>void *     pObject;      // a pointer to some object
>MEMBERFUNC pfnObject;    // one of pObject's memberfunction addresses
>pObject->(*pfnObject)(); // does not work (struct req. left of ->)

Instead of registering an object plus a member function pointer,
register a callback object with a virtual member function:

 class CallBack {
 public:
  virtual void call_me_back() = 0;
 };

 template <class T>
 class ObjectCallBack : public CallBack {
 public:
  T* object;
  void (T::*member_func)();
  ObjectCallBack(T* p, void (T::*pmf()) :
   object(p), member_func(pmf) {}
  call_me_back() { (object->*member_func)(); }
 };

>In fact, the typedef in the above sample cannot even be made such that
>it is generically applicable. To get around this problem, I am making
>use of the fact that C++ calls like "pObject->foo()" translate into
>"foo_magled_name(pObject)".
>
>a: Is this safe, or is it possible that future releases of C++ no
>   longer support this?

No, this is not safe.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: hugp@actrix.gen.nz (Peter Hug)
Date: Thu, 19 May 1994 00:39:30 GMT
Raw View
Since I am using multiple independant class hierarchies in my project,
I find it difficult to implement generic classes who's instances act
as servers to potentially any type of client object. Client objects
typically create server objects, register themself and install callbacks
(client member-functions). Although my generic class knows the address
of an object and an address of one of the objects non-static member-
functions, I cannot send the message like:

typedef    void (CouldBeAnyClass::*MEMBERFUNC) (void);
void *     pObject;      // a pointer to some object
MEMBERFUNC pfnObject;    // one of pObject's memberfunction addresses
pObject->(*pfnObject)(); // does not work (struct req. left of ->)

In fact, the typedef in the above sample cannot even be made such that
it is generically applicable. To get around this problem, I am making
use of the fact that C++ calls like "pObject->foo()" translate into
"foo_magled_name(pObject)". Thus, instead of storing the address of
a member-function, I store the address of a function who's first argument
is a void* to the object. While this works fine, there are two questions
which I hope someone out there can answer:

a: Is this safe, or is it possible that future releases of C++ no
   longer support this?

b: I find it extremely difficult in C++ to cast a non-static member
   function to an ordinary C function, something clients of the generic
   server class need to do. e.g.:

   typedef void (MyClass::*MEMBERFUNC) (void);
   typedef void (*NON_MEMBERFUNC) (void *);
   MEMBERFUNC     pfnMF = &MyClass::foo;
   NON_MEMBERFUNC pfnN_MF;
   pfnN_MF = (?????) pfnMF;   // how to cast???

Thanks for your comment.

Peter
--
Peter Hug             |                               |  Lieber
Maxi Solutions Ltd    |   Voice: (+64) 4 298 3441     |  arm dran
Paraparaumu Beach     |   Fax:   (+64) 4 297 2288     |  als
New Zealand           |   email: hugp@actrix.gen.nz   |  arm ab




Author: jason@cygnus.com (Jason Merrill)
Date: Thu, 19 May 1994 18:12:07 GMT
Raw View
>>>>> Peter Hug <hugp@actrix.gen.nz> writes:

> pObject-> (*pfnObject)(); // does not work (struct req. left of ->)

This should be

  pObject->*pfnObject(), using the ->* operator.

> To get around this problem, I am making use of the fact that C++ calls
> like "pObject->foo()" translate into "foo_magled_name(pObject)".

See section 18.3.4 of the ARM (Anachronisms: Cast of Bound Pointer) on this
subject.

Jason




Author: hugp@actrix.gen.nz (Peter Hug)
Date: Fri, 20 May 1994 03:31:41 GMT
Raw View
So far I got two responses solving my problem:

a) Use a static-member function as the call-back, e.g.:

   #include <iostream.h>

   class A   {
          void cb  ()  { cout << "A::cb()" << endl; }
   static void cbh (void * pa) { ((A*)pa)->cb(); }
   }

   class B   {
      void * px;
      void (*pfnx)(void);
      B(void * p, void (*pfn)(void)) : px(p), pfnx(pfn) {};
      void Notify () { (*pfnx)(px); };
   }

   int main () {
      A a;
      B b(&a, &A::cbh);
      b.Notify(); // invokes A::cbh(&a) which does a.cb()
      return 0;
   }

Solution a) is the way I implement generic server classes. The static-mf is
not realy to my liking (unwanted overhead!). However, for those who mix C
and C++ code, it has the advantage that standard C functions can be used as
the callback also.


b) Make the server class a template class, e.g.

   #include <iostream.h>

   class A  {
      void cb  ()  { cout << "A::foo()" << endl; }
   }

   template <class T>
   class B  {
      T * px;
      void (T::*pfnx)(void);
      B(T * p, void (T::*pfn)(void)) : px(p), pfnx(pfn) {};
      void Notify () { px->(*pfnx)(); };
   }

   int main () {
      A    a;
      B<A> b(&a, &A::cb);
      b.Notify(); // invokes a.cb()
      return 0;
   }

Apart of the standard limitations applying to the use of template classes,
solution b) has it's own limitations. Just imagine an object of class B
in solution b) was required to remember a collection of clients!
--
Peter Hug             |                               |  Lieber
Maxi Solutions Ltd    |   Voice: (+64) 4 298 3441     |  arm dran
Paraparaumu Beach     |   Fax:   (+64) 4 297 2288     |  als
New Zealand           |   email: hugp@actrix.gen.nz   |  arm ab