Object-Oriented Callbacks for C++?
Asked Answered
C

6

9

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!

Clearness answered 19/8, 2010 at 8:52 Comment(2)
I'm not sure I completely understand your syntax but boost::bind can be used to package both functions and member functions with an appropriate interface into an object. boost.org/doc/libs/1_44_0/libs/bind/…Lyell
hey! yes i think that's what i'm looking for. unfortunately i can't compile boost for the iPhone (developing on the iPhone). i read that this is also possible to do with stl. can someone maybe explain how?Clearness
L
18

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();
}
Larine answered 19/8, 2010 at 10:1 Comment(0)
K
6

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.

Ketch answered 19/8, 2010 at 14:36 Comment(0)
I
2

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).

Interdiction answered 19/8, 2010 at 9:0 Comment(4)
yes but only static member functions. but i want the object itself being notifiedClearness
No, any member function can be a pointer to a member function.Interdiction
@Clearness I insist because Mat doesnt seem to grasp it: C++ ALLOWS Member non-static function pointers. However the smartest way of doing what you want is a functor ( indeed it's simply defining the parenthesis operator so as your objects can behave like a functions) like MeThinks have answered.Dysphagia
@Clearness here is a litte example: class A{ public: void Foo(){};}; typedef (void) (A::*)() PFuncNonStatic; A a; PFuncNonStatic func = A::Foo; a.(*func)(); I may have done some mistakes, I haven't tried to compile, but it is more or less the spirit of the syntax for nonstatic function pointers.Dysphagia
A
1

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();
}
Audit answered 19/8, 2010 at 10:43 Comment(0)
C
1

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

Croissant answered 19/8, 2010 at 11:15 Comment(0)
F
0

There are many possibilities in C++, the issue generally being one of syntax.

  • You can use pointer to functions when you don't require state, but the syntax is really horrid. This can be combined with boost::bind for an even more... interesting... syntax (*)
  • I correct your false assumption, it is indeed feasible to have pointer to a member function, the syntax is just so awkward you'll run away (*)
  • You can use Functor objects, basically a Functor is an object which overloads the () operator, for example void Functor::operator()(int a) const;, because it's an object it has state and may derive from a common interface
  • You can simply create your own hierarchy, with a nicer name for the callback function if you don't want to go the operator overloading road
  • Finally, you can take advantage of C++0x facilities: std::function + the lambda functions are truly awesome when it comes to expressiveness.

I would appreciate a review on lambda syntax ;)

Foo foo;
std::function<void(std::string const&,int)> func =
  [&foo](std::string const& s, int i) {
    return foo.say(s,"Hi from Foo",i);
  };

func("Hi from Bar", 2);
func("Hi from FooBar", 3);

Of course, func is only viable while foo is viable (scope issue), you could copy foo using [=foo] to indicate pass by value instead of pass by reference.

(*) Mandatory Tutorial on Function Pointers

Freestyle answered 19/8, 2010 at 14:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.