6

I'm using a 3rd party library that allows me to register callbacks for certain events. The register function looks something like this. It uses the Callback signature.

typedef int (*Callback)(std::string);

void registerCallback(Callback pCallback) {
//it gets registered
}

My problem is that I want to register a member function as a callback something like this

struct MyStruct {
    MyStruct();
    int myCallback(std::string str);
};

MyStruct::MyStruct() {
    registerCallback(&MyStruct::myCallback);
}

int MyStruct::myCallback(std::string str) {
    return 0;
}

Of course, the compiler complains, saying

error C2664: 'registerCallback' : cannot convert parameter 1 from 'int (__thiscall MyStruct::* )(std::string)' to 'Callback'

I've been looking at boost libraries like function and bind, but none of those seem to be able to do the trick. I've been searching all over Google for the answer, but I don't even know what to call this, so it hasn't been much help.

Thanks in advance.

Jonathan Ross
  • 61
  • 1
  • 4
  • Thanks everyone! I found one other post that asks the same question, and it looks like the consensus is just to make the member function a normal function or make it static. [link](http://stackoverflow.com/questions/3171418/v8-functiontemplate-class-instance). Unless someone has some really crazy TMP tricks? – Jonathan Ross Nov 08 '11 at 06:20

6 Answers6

8

You're trying to pass a member function pointer as a normal function pointer which won't work. Member functions have to have the this pointer as one of the hidden parameters, which isn't the case for normal functions, so their types are incompatible.

You can:

  1. Change the type of your argument to accept member functions and also accept an instance to be the invoking object
  2. Quit trying to pass a member function and pass a normal function (perhaps by making the function static)
  3. Have a normal function that takes an instance of your class, a member function pointer, and a std::string and use something like boost's bind to bind the first two arguments
  4. Make the callback registration function accept a functor object, or an std::function (I think that's the name)
  5. Numerous other ways which I won't detail here, but you get the drift.
Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • The problem is I cannot change the function called `registerCallback`. It is part of a third party library. I can't use `boost::bind` because I get a similar error. `boost::bind` returns a `bind_t` object, which I believe is a functor of some sort. In any case it is not a function pointer and the compiler will complain that it cannot convert a bind_t to a `Callback` – Jonathan Ross Nov 08 '11 at 05:16
  • If it helps the 3rd party library I'm talking about is V8 engine. The specific task I'm trying to do is register a JavaScript callback function like this `ObjectTemplate::New()->Set(String::New("getRemote"), FunctionTemplate::New(&RemoteObject::getRemoteCallback));` And `FunctionTemplate::New` takes an `InvokationCallback` that looks like this `typedef Handle (*InvocationCallback)(const Arguments& args);` – Jonathan Ross Nov 08 '11 at 05:41
2

Member functions are not convertible to the normal functions for its own good reasons. If your design allows, then make MyStruct::myCallback() a static member method and the code should work fine.

struct MyStruct {
  ...
  static int myCallback(std::string str);
  ^^^^^^
};
Community
  • 1
  • 1
iammilind
  • 68,093
  • 33
  • 169
  • 336
1

Due to the inflexible hardcoding of an actual function type for the callback you can't use any of the normal adapting or binding tricks to help you here. The third party library really wants you to pass in a non-member function and there isn't much you can do. You may want to consider why you're trying to pass it the address of a member function, and if your design or use of the library could change.

One option, if you only need to set a single callback, is to have a static or namespace private function that refers to a singleton instance pointer, and uses that to dispatch upon callback.

If you need multiple items, then possibly a template would wrap the hackiness (untested code here, just the idea).

template <int which_callback>
struct CallbackHolderHack
{
    static int callback_func(std::string str) { dispatchee_->myCallback(str); }
    static MyStruct* dispatchee_;
};

template <int which_callback>
MyStruct* CallbackHolderHack::dispatchee_(0);

And use it:

CallbackHolderHack<0>::dispatchee_ = new MyStruct;
registerCallback(&CallbackHolderHack<0>::callback_func);
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • Very imaginative! Unfortunately, I am required to register these call backs dynamically during run-time. This would work if I knew ahead of time all the callbacks I would need to register. Maybe I'll take a look at the Chrome code base and see what they do. The V8 documentation is quite lacking :( – Jonathan Ross Nov 08 '11 at 06:06
0

Since you are using C++, why not make the callback a functor object? Then you can use std::mem_fun.

Edit: Seems std::mem_fun has been deprecated in the latest C++11 standard. So std::function might be a better solution if you have a new compiler.

See this SO question for hints about using it.

Community
  • 1
  • 1
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

Depends on how you are using it.... eg a Singlton would be much simplier

struct MyStruct {
    static MyStruct& Create() { 
        static MyStruct m; return m;
    }
    static int StaticCallBack(std::string str) { 
        return Create().Callback(str)
    }
    private:
    int CallBack(std::string str);
    MyStruct();
};

Or if you want to have many of these objects you have several choices. You would need a way to route it before the callback is called.

Joe McGrath
  • 1,481
  • 10
  • 26
  • The reason I want to register a member function is so that I can store additional data that the callback can use when it is called, and it would be nice to encapsulate that in a class. If I use a singleton or make my class all static, the I'd have to use a map of some sort to store and retrieve the data I wanted based on the meager amount of data I get from the arguments, which in many cases are not guaranteed to be unique. – Jonathan Ross Nov 08 '11 at 05:39
  • If there is nothing in the string that could say which object it should go to, I believe using a better suited library or modifying it would be better than hacking around it. yYou could check out ATL thunking. Basicly you would need to generate a function for each object during runtime that calls your member function. May be easier with newer standards and libraries. – Joe McGrath Nov 08 '11 at 06:06
-1
class ICallBackInterface
{
public:
    virtual void FireCallBack( std::string& str ) = 0;
};

std::set<ICallBackInterface*> CallBackMgr;

class MyStruct : public ICallBackInterface
{
public:
    MyStruct()
    {
        CallBackMgr.insert( this );
    }

    ~MyStruct()
    {
        CallBackMgr.erase( this );
    }

    virtual void FireCallBack( std::string& str )
    {
        std::cout << "MyStruct  called\n";
    }
};

void FireAllCallBack(std::string& str )
{
    for ( std::set<ICallBackInterface*>::iterator iter = CallBackMgr.begin();
        iter != CallBackMgr.end();
        ++iter)
    {
        (*iter)->FireCallBack( str );
    }
}

This is another way to use polymorphism to achieve the same effect

parapura rajkumar
  • 24,045
  • 1
  • 55
  • 85