Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object-Oriented Callbacks for C++?

Is there some library that allows me to easily and conveniently create Object-Oriented callbacks in c++?

the language Eiffel for example has the concept of "agents" which more or less work like this:

class Foo{
public:
    Bar* bar;

    Foo(){
        bar = new Bar();
        bar->publisher.extend(agent say(?,"Hi from Foo!", ?));
        bar->invokeCallback();
    }

    say(string strA, string strB, int number){
        print(strA + " " + strB + " " + number.out);
    }

}

class Bar{
public:
    ActionSequence<string, int> publisher;

    Bar(){}

    invokeCallback(){
        publisher.call("Hi from Bar!", 3);
    }
}

output will be: Hi from Bar! 3 Hi from Foo!

So - the agent allows to to capsule a memberfunction into an object, give it along some predefined calling parameters (Hi from Foo), specify the open parameters (?), and pass it to some other object which can then invoke it later.

Since c++ doesn't allow to create function pointers on non-static member functions, it seems not that trivial to implement something as easy to use in c++. i found some articles with google on object oriented callbacks in c++, however, actually i'm looking for some library or header files i simply can import which allow me to use some similarily elegant syntax.

Anyone has some tips for me?

Thanks!

like image 368
Mat Avatar asked Aug 19 '10 08:08

Mat


5 Answers

The most OO way to use Callbacks in C++ is to call a function of an interface and then pass an implementation of that interface.

#include <iostream>

class Interface
{
  public:
  virtual void callback() = 0;
};

class Impl : public Interface
{
  public:
  virtual void callback() { std::cout << "Hi from Impl\n"; }
};

class User
{
  public:
  User(Interface& newCallback) : myCallback(newCallback) { }

  void DoSomething() { myCallback.callback(); }

  private:
  Interface& myCallback;
};

int main()
{
  Impl cb;
  User user(cb);
  user.DoSomething();
}
like image 179
stefaanv Avatar answered Nov 11 '22 19:11

stefaanv


People typically use one of several patterns:

Inheritance. That is, you define an abstract class which contains the callback. Then you take a pointer/reference to it. That means that anyone can inherit and provide this callback.

class Foo {
    virtual void MyCallback(...) = 0;
    virtual ~Foo();
};
class Base {
    std::auto_ptr<Foo> ptr;
    void something(...) {
        ptr->MyCallback(...);
    }
    Base& SetCallback(Foo* newfoo) { ptr = newfoo; return *this; }
    Foo* GetCallback() { return ptr; }
};

Inheritance again. That is, your root class is abstract, and the user inherits from it and defines the callbacks, rather than having a concrete class and dedicated callback objects.

class Foo {
    virtual void MyCallback(...) = 0;
    ...
};
class RealFoo : Foo {
    virtual void MyCallback(...) { ... }
};

Even more inheritance- static. This way, you can use templates to change the behaviour of an object. It's similar to the second option but works at compile time instead of at run time, which can yield various benefits and downsides, depending on the context.

template<typename T> class Foo {
    void MyCallback(...) {
        T::MyCallback(...);
    }
};
class RealFoo : Foo<RealFoo> {
    void MyCallback(...) {
        ...
    }
};

You can take and use member function pointers or regular function pointers

class Foo {
    void (*callback)(...);
    void something(...) { callback(...); }
    Foo& SetCallback( void(*newcallback)(...) ) { callback = newcallback; return *this; }
    void (*)(...) GetCallback() { return callback; }
};

There are function objects- they overload operator(). You will want to use or write a functional wrapper- currently provided in std::/boost:: function, but I'll also demonstrate a simple one here. It's similar to the first concept, but hides the implementation and accepts a vast array of other solutions. I personally normally use this as my callback method of choice.

class Foo {
    virtual ... Call(...) = 0;
    virtual ~Foo();
};
class Base {
    std::auto_ptr<Foo> callback;
    template<typename T> Base& SetCallback(T t) {
        struct NewFoo : Foo {
             T t;
             NewFoo(T newt) : t(newt) {}
             ... Call(...) { return t(...); }
        };
        callback = new NewFoo<T>(t);
        return this;
    }
    Foo* GetCallback() { return callback; }
    void dosomething() { callback->Call(...); }
};

The right solution mainly depends on the context. If you need to expose a C-style API then function pointers is the only way to go (remember void* for user arguments). If you need to vary at runtime (for example, exposing code in a precompiled library) then static inheritance can't be used here.

Just a quick note: I hand whipped up that code, so it won't be perfect (like access modifiers for functions, etc) and may have a couple of bugs in. It's an example.

like image 20
Puppy Avatar answered Nov 11 '22 21:11

Puppy


C++ allows function pointers on member objects.
See here for more details.
You can also use boost.signals or boost.signals2 (depanding if your program is multithreaded or not).

like image 27
the_drow Avatar answered Nov 11 '22 19:11

the_drow


There are various libraries that let you do that. Check out boost::function.

Or try your own simple implementation:

template <typename ClassType, typename Result>
class Functor
{
 typedef typename Result (ClassType::*FunctionType)();
 ClassType* obj;
 FunctionType fn;
public:
 Functor(ClassType& object, FunctionType method): obj(&object), fn(method) {}

 Result Invoke()
 {
  return (*obj.*fn)();
 }

 Result operator()()
 {
  return Invoke();
 }
};

Usage:

class A
{
 int value;
public:
 A(int v): value(v) {}

 int getValue() { return value; }
};


int main()
{
 A a(2);

 Functor<A, int> fn(a, &A::getValue);

 cout << fn();
}
like image 1
Grozz Avatar answered Nov 11 '22 20:11

Grozz


Joining the idea of functors - use std::tr1::function and boost::bind to build the arguments into it before registering it.

like image 1
rkellerm Avatar answered Nov 11 '22 21:11

rkellerm