user966379
user966379

Reputation: 2983

Setting a callback function which is non static member function of a class

typedef void (*CALLBACK)();
class Filter
{
public:
     void callback()
    {
        cout << "callback" << endl;
    }
};

void SetCallback(CALLBACK pCallBack )
{
    pCallBack();
}



int main()
{
    Filter f;
    SetCallback(f.callback);
}

In main, SetCallback(f.callback); statement is giving error. Can anyone help me to fix the issue

Upvotes: 10

Views: 12462

Answers (5)

rkc137
rkc137

Reputation: 33

i just making a static variable _this, that a pointer on my class, and make method static, where this replaced with _this

class my_class
{
private:
    inline static my_class *_this = nullptr;
    int data{};
    static void callback_method()
    {
        _this->data++;
    }
public:
    my_class()
    {
        _this = this;
        legacy_c_func(callback_method);
        _this = nullptr;
    }
};

but tbh its not thread safe

Upvotes: 0

user2020
user2020

Reputation: 41

A simplier example about callback for 'non-static method' :

#include <iostream>
#include <string>
#include <functional>

using namespace std::placeholders;

class Test
{
public:
    void SetValue(int i) { v = i;}

    int v;
};

int main()
{
    Test a { 123 };

    std::cout << a.v << std::endl;  // print 123

    auto _callback = std::bind(&Test::SetValue, &a, _1); // create the callback

    _callback(55); // call the callback

    std::cout << a.v << std::endl; // print 55

    return 0;
}

output :

123
55

Upvotes: 3

Sebastian Redl
Sebastian Redl

Reputation: 71899

The problem is that a member function isn't a plain function without parameters, because it always has the implicit this parameter.

If you encounter a legacy C interface that requires a plain callback function without a user context parameter (a void* that the function just passes on to the callback) you have a problem.

If you do have the user context, it's easy. Pass the object pointer as the context, and use a wrapper function as the actual callback:

typedef void (*CALLBACK)(void*);
class Filter
{
public:
    static void CallbackWrapper(void* context) {
        static_cast<Filter*>(context)->callback();
    }

private:
    void callback();
};

int main() {
    Filter f;
    SetCallback(&Filter::CallbackWrapper, &f);
}

If you don't have the context, here are some options:

  • Store the object in a global variable and access it from a wrapper. This has the obvious downsides of using a global variable, and not allowing more than one callback this way. For long-running callbacks this is really bad.
  • A small improvement to the above is to use a thread-local global variable. This is interesting for tightly scoped callbacks, e.g. you call a function that will immediately use your callback multiple times and then return. Think qsort(). At least this way, you don't get the thread safety issues. Still not an option for long-running callbacks.
  • Finally, the option that works on most platforms but is a lot of work, you can generate a stub function at runtime which embeds the object pointer. This basically means allocating a piece of memory, disabling execution protection on that memory if the platform uses this, and put machine code there that loads the object pointer and calls the function on it.

The final option still has lots of downsides: it's extremely platform-specific and may not even work at all on some (you can't disable execution protection in iOS, AFAIK), it's CPU-specific (since you need to generate the right code for each), and there's the issue of managing the memory for the stub. On the other hand, sometimes it's the only thing that works. Delphi does this kind of stuff for its window and hook procedures sometimes, and the ATL does so too.

Upvotes: 11

Steve
Steve

Reputation: 6424

Here is a method I've used to implement a callback to a pointer to member function.

It might require C++11.

#include <iostream>
#include <string>
#include <functional>

using namespace std;

struct MessageSource
{
       function<void(const string& msg)> _callback;

       template<typename A, typename B>
       void connect(A func_ptr, B obj_ptr)
       {
              _callback = bind(func_ptr, obj_ptr, placeholders::_1);
       }

       void send_msg(const string& msg)
       {
              if (_callback)
                     _callback(msg);
       }

       void disconnect()
       {
              _callback = nullptr;
       }
};

struct Printer
{
       void print(const string& msg) { std::cout << msg << std::endl; };
};

int main()
{
       {
              Printer p;
              MessageSource s;
              s.connect(&Printer::print, &p);
              s.send_msg("test");
              s.disconnect();
              s.send_msg("test again");
       }

       system("pause");
       return 0;
}

Upvotes: 5

Serge Ballesta
Serge Ballesta

Reputation: 148880

You should think about what a callback really is and how a member function is called.

When you give a callback function, you just give the address of a function that will later be called with parameters on which you normally have little control.

When a member function is called, its first parameter is the this pointer, that is a reference to the object on which the method is called.

That's the reason why it is not possible to use a member method as a callback. You can only use true functions or static member functions that do not need the special (implicit for programmer but real in a compiler point of view) parameter this.

Upvotes: 0

Related Questions