views:

221

answers:

4

Hi,

I have a C++ library that should expose some system\ resource calls as callbacks from the linked application. For example: the interfacing application (which uses this library) can send socket management callback functions - send, receive, open, close etc., and the library will use this implementation in stead of the library's implementation. (This way enables the application to manage the sockets by itself, can be useful). This library has to expose also more callbacks, like, for example, a password validation, so I wonder if there is a preferred method to expose the callback sending option in one API. Something like:

int AddCallbackFunc (int functionCallbackType, <generic function prototype>, <generic way to pass some additional arguments>)

Then within my library I will assign the callback to the appropriate function pointer according to the functionCallbackType parameter.

Is there any way to implement it in a generic way which will fit ANY function prototype and ANY additional arguments?

Your help will be more than appreciated... Thanks!

+2  A: 

Why not have it accept a 0 argument functor and just have the user use boost::bind to build the arguments into it before registering it? Basically example (calls instead of stores, but you get the point):

#include <tr1/functional>
#include <iostream>

void callback(const std::tr1::function<int()> &f) {
    f();
}

int x() {
    std::cout << "x" << std::endl;
    return 0;
}

int y(int n) {
    std::cout << "y = " << n << std::endl;
    return 0;
}

int main(int argc, char *argv[]) {
    callback(x);
    callback(std::tr1::bind(y, 5));
}

EDIT: There is an option B, which is to basically implement what bind does under the hood with structures to store all the needed info and inheritance for polymorphism... it becomes a mess real quick. I would not recommend it, but it will work. You can also save from grief by forcing a return type of int, but that only saves you a little.

#include <iostream>

struct func_base {
    virtual int operator()() = 0;
};

// make one of these for each arity function you want to support (boost does this up to 50 for you :-P
struct func0 : public func_base {
    typedef int (*fptr_t)();

    func0(fptr_t f) : fptr(f) {
    }

    virtual int operator()() { return fptr(); }

    fptr_t fptr;
};

// demonstrates an arity of 1, templated so it can take any type of parameter
template <class T1>
struct func1 : public func_base {
    typedef int (*fptr_t)(T1);

    func1(fptr_t f, T1 a) : fptr(f), a1(a) {
    }

    virtual int operator()() { return fptr(a1); }

    fptr_t fptr;
    T1 a1;
};

void callback(func_base *f) {
    (*f)();
}

int x() {
    std::cout << "x" << std::endl;
    return 0;
}

int y(int n) {
    std::cout << "y = " << n << std::endl;
    return 0;
}

int main(int argc, char *argv[]) {
    // NOTE: memory leak here...
    callback(new func0(x));
    callback(new func1<int>(y, 5));
}
Evan Teran
Thanks - but I don't want to use external libraries.
rursw1
unfortunately, this is the best by a long shot way to do this, you could always use tr1 as well. Short of implementing basically what bind does manually (a massive undertaking) I don't see an easy way to do it nicely.
Evan Teran
A: 

Sounds like you're looking for a Functor. Basically a class for each type of callback, with the arguments as data members and operator() to invoke the functionality.

marijne
Yes, the only issue left is to come up with a way to store different functors (all take no params in `operator()` but are technically different types in a collection so they can be generically called later. which is why i recommented boost/tr1's `function<>`
Evan Teran
sure, usually with functors you would have a common base class, which would allow them to be stored in a collection.
marijne
+1  A: 

If you don't want to go for any of the C++ options available; std::tr1::function, functors, polymorphism with common base class etc. you can use the C method instead.

The client passes a callback and a pointer to its arguments as a void*, then the callback casts the void* to the correct type when it's called. You'll need to store the void* alongside the callback and you'll need to be very careful with object lifetimes.

int AddCallbackFunc (int type, int(*callback)(void*), void* callbackData)
Joe Gauterin
And the use of `void*` is probably why it was deemed unsafe in the first place.
Matthieu M.
But is there another "C" way to do that? It seems like the only way.
rursw1
+1  A: 

It can be done, using a combination of template and type-erasure.

The idea is to take any type and wrap it into an object with a known interface.

class CallbackBase
{
public:
  virtual ~CallbackBase();

  virtual void execute();
};

template <class T>
class Callback: public CallbackBase
{
public:
  explicit Callback(T functor): mFunctor(functor) {}

  void execute() { mFunctor(); }

private:
  T mFunctor;
};

And now, we can wrap it:

template <class Function>
int AddCallbackFunc (int functionCallbackType, Function f)
{
  std::auto_ptr<CallbackBase> c(new Callback<Function>(f));

  // do something with `c`.
}

I leave it up to you to bind the arguments, the no library way is to create a functor.

Matthieu M.