Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Class member functions as event callbacks

I'm trying to add a simple messaging system to my project, where events can be invoked by a function, which will lead to all callbacks registered to that event being called.

Now, the logical way to do this is using function pointers. It would be easily possible to pass the pointer to the desired callback function to the events manager, for registering. An event callback function would always return an int and take a void* as argument.

However I don't want to register static global functions as my event callbacks - I'd like to do it with class member functions.

  • Is it even possible to accomplish this with C++? Storing and calling pointers to member functions of different classes but with the same function header.

  • If this is not possible, do you have any suggestions on how I could work around this? I'd really like to add event listeners directly to my classes.

like image 957
Jarx Avatar asked Mar 03 '11 12:03

Jarx


2 Answers

Yes it is possible. C++0x has the function class that handles this, and as others have pointed out Boost has similar facilities.

You can also roll your own, but the syntax is not for the faint of heart:

#include <iostream>

class Callable
{
    public:

        virtual ~Callable() {}
        virtual int operator() (void* args) = 0;
};

class CallableFreeFunction  : public Callable
{
    public:

        CallableFreeFunction(int (*func)(void*)) : func_(func) {}

        virtual int operator() (void* args) { return (*func_)(args); }

    private:

        int (*func_)(void*);
};

template <typename tClass>
class ClassMemberCallable : public Callable
{
    public:

        ClassMemberCallable(tClass* instance, int (tClass::*memberfunction)(void*)) : instance_(instance), memberfunc_(memberfunction) {}

        virtual int operator() (void* args) { return (instance_->*memberfunc_)(args); }

    private:

        tClass* instance_;
        int (tClass::*memberfunc_)(void*);
};

class Foo
{
    public:

        int derp(void* args)
        {
            std::cout << args << '\n';
            return 2;
        }
};

int freefunctionfoo(void* args)
{
    std::cout << "free" << args << '\n';
    return 2;
}

int main(int argc, char* argv[])
{
    Foo myfoo;

    Callable* callable = new ClassMemberCallable<Foo>(&myfoo, &Foo::derp);

    (*callable)(0);

    delete callable;

    callable = new CallableFreeFunction(freefunctionfoo);

    (*callable)(0);

    delete callable;

    std::cin.get();

    return 0;
}

This demonstrates a way of handling both free functions, and member functions in an opaque way. This is a simple example, and can be made more generic and robust in a number of ways. I'd refer you to these pages for syntax help:

http://www.newty.de/fpt/index.html

http://www.parashift.com/c++-faq-lite/pointers-to-members.html

I'd also recommend looking at this for more ideas:

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

like image 89
luke Avatar answered Sep 24 '22 07:09

luke


Of course it's possible ! Have a look at Boost.Signal2 and Boost.Bind.

Boost.Signal2 basically implements a signal and slots system which is exactly what you need. Then, you can use boost::bind which is a generalization of std::bind1st and std::bind2nd to get function object wrappers to basically anything you can think of (in your case, member methods). It's really powerful.

See this official boost tutorial.

like image 40
fouronnes Avatar answered Sep 21 '22 07:09

fouronnes