Search code examples
c++c++11callbackesp32

C++ - How to bind a callback to a class method without being static?


I have my class:

class Foo
{
public:
  (...)    
private:        
    void mycallback(void* buff, wifi_promiscuous_pkt_type_t type);
    void registerMyCallback();
};

The mycallback is the callback.

I want to use a method esp_wifi_set_promiscuous_rx_cb to register the mycallback so that when a WiFi packet is detected, this callback method will be executed.

The esp_wifi_set_promiscuous_rx_cb signature is:

esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb);

Where the wifi_promiscuous_cb_t definition is:

typedef void (* wifi_promiscuous_cb_t)(void *buf, wifi_promiscuous_pkt_type_t type);

I want to use the mycallback method inside my class, therefore I simply can't use like this:

  void Foo::registerMyCallback()
  {
    esp_wifi_set_promiscuous_rx_cb(&mycallback);
  }

I know that I could use something similar if I would just make my method as static. Is there anyway that I bind mycallback to esp_wifi_set_promiscuous_rx_cb without making the callback static?

I have tried the following:

esp_wifi_set_promiscuous_rx_cb(std::bind(&Foo::mycallback, this, std::placeholders::_1, std::placeholders::_2));

But I am still having the following error:

cannot convert 'std::_Bind_helper<false, void (Foo::Foo::*)(void*, wifi_promiscuous_pkt_type_t), 
Foo::Foo*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type 
to 
'wifi_promiscuous_cb_t {aka void (*)(void*, wifi_promiscuous_pkt_type_t)}' for argument '1'

Solution

  • Th library you are using is C package.
    Thus the only guaranteed way pass a valid function is to pass a C function with C linkage. This function can then call the method on your object.

    If you want the callback method to be non static you need to store a pointer (ore reference) to the callback object somewhere that your callback function can find it. (in most C callback functions you can provide a void* object that is passed to your callback, but this interface does not seem to allow this so you will have to save the value yourself).

    Foo*  myCBObject = nullptr;
    
    extern "C" void myCB(void *buf, wifi_promiscuous_pkt_type_t type)
    {
        try
        {
            myCBObject->mycallback(buff, type);
        }
        catch(...) {} // Don't allow exceptions to cross C linkage
    }
    
    ...
    // Your code.
    void Foo::registerMyCallback()
    {
        myCBObject = this;
        esp_wifi_set_promiscuous_rx_cb(myCB);
    }
    

    Note: You should NOT be registering static member functions with a C library. If this works it is only by chance. There is no guarantee that a static function has the same calling convention of a C function (they usually do but that is not guaranteed).