views:

3322

answers:

10

I have a function pointer defined by:

typedef void (*EventFunction)(int nEvent);

Is there a way to handle that function with a specific instance of a C++ object?

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne() { handler(1); }
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(EventFromA); }  // What do I do here?

    void EventFromA(int nEvent) { // do stuff }
};

Edit: Orion pointed out the options that Boost offers such as:

boost::function<int (int)> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

Unfortunately Boost is not an option for me. Is there some sort of "currying" function that can be written in C++ that will do this kind of wrapping of a pointer to a member function in to a normal function pointer?

+1  A: 

Unfortunately, the EventFunction type cannot point to a function of B, because it is not the correct type. You could make it the correct type, but that probably isn't really the solution you want:

typedef void (*B::EventFunction)(int nEvent);

... and then everything works once you call the callback with an obhect of B. But you probably want to be able to call functions outside of B, in other classes that do other things. That is sort of the point of a callback. But now this type points to something definitely in B. More attractive solutions are:

  • Make B a base class, then override a virtual function for each other class that might be called. A then stores a pointer to B instead of a function pointer. Much cleaner.
  • If you don't want to bind the function to a specific class type, even a base class (and I wouldn't blame you), then I suggest you make the function that gets called a static function: "static void EventFrom A(int nEvent);". Then you can call it directly, without an object of B. But you probably want it to call a specific instance of B (unless B is a singleton).
  • So if you want to be able to call a specific instance of B, but be able to call non-B's, too, then you need to pass something else to your callback function so that the callback function can call the right object. Make your function a static, as above, and add a void* parameter which you will make a pointer to B.

In practice you see two solutions to this problem: ad hoc systems where you pass a void* and the event, and hierarchies with virtual functions in a base class, like windowing systems

Mark Santesson
+4  A: 

You may find this information helpful to what you're trying to accomplish.

Onorio Catenacci
+2  A: 

Read about pointers to members. To call a method on the derived class, the method has to be declared in the base class as virtual and overriden in the base class and your pointer should point to the base class method. More about pointers to virtual members.

Franci Penov
A: 

It's somewhat unclear what you're trying to accomplish here. what is clear is that function pointers is not the way.

maybe what you're looking for is pointer to method.

shoosh
Second that point. If you provide more information we could provide more help.
Martin York
+6  A: 

Run away from raw C++ function pointers, and use boost::function instead

Orion Edwards
Or tr1::function and tr1::bind.
luke
...which are the same thing :)
jkp
+2  A: 

You can use function pointers to index into the vtable of a given object instance. This is called a member function pointer. Your syntax would need to change to use the ".*" and the "&::" operators:

class A;
class B;
typedef void (B::*EventFunction)(int nEvent)

and then:

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(&B::EventFromA); } // note: "&::"

    void EventFromA(int nEvent) { /* do stuff */ }
};
David Citron
A: 

I have a set of classes for this exact thing that I use in my c++ framework.

http://code.google.com/p/kgui/source/browse/trunk/kgui.h

How I handle it is each class function that can be used as a callback needs a static function that binds the object type to it. I have a set of macros that do it automatically. It makes a static function with the same name except with a "CB_" prefix and an extra first parameter which is the class object pointer.

Checkout the Class types kGUICallBack and various template versions thereof for handling different parameters combinations.

#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}
KPexEA
A: 
Adam Rosenfield
+2  A: 

I highly recommend Don Clugston's excellent FastDelegate library. It provides all the things you'd expect of a real delegate and compiles down to a few ASM instructions in most cases. The accompanying article is a good read on member function pointers as well.

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

Ron Warholic
Lots of good information there.
fryguybob
+1  A: 

You mention that boost isn't an option for you, but do you have TR1 available to you?

TR1 offers function, bind, and mem_fn objects based on the boost library, and you may already have it bundled with your compiler. It isn't standard yet, but at least two compilers that I've used recently have had it.

http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702.aspx

Corey Ross
Good recommendations, but my specific project is legacy VC6 code. That's also why I can't really solve the problem with better C++ design choices *sigh*.
fryguybob