1

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'
waas1919
  • 2,365
  • 7
  • 44
  • 76
  • 3
    Without an extra parameter to `esp_wifi_set_promiscuous_rx_cb` which can take a pointer to your class (or some other token), this is not possible. – Richard Critten May 31 '20 at 11:14
  • You'll have to change `wifi_promiscuous_cb_t` to be a member function pointer rather than a non-member function pointer (such as a static function, like it currently is). – Eljay May 31 '20 at 11:18
  • 1
    Does this answer your question? [Callback functions in C++](https://stackoverflow.com/questions/2298242/callback-functions-in-c) – gre_gor May 31 '20 at 11:23
  • you want have additional parameter in callback - pointer (to your class object). but caller not pass it. only solution here - create dynamic callback function which will be 2 in 1 - hold your custom pointer and call your class member function with it. but such solution will be os and platform specific and require bit of asm code – RbMm May 31 '20 at 11:53
  • Are any of the parameters passed via the callback an `opaque` pointer which can be used for user data? – WBuck May 31 '20 at 12:36
  • @stefan.gal and how this can help ? – RbMm May 31 '20 at 13:14

2 Answers2

1

After some research, I hope I found the solution. The trick is to bind member function first and then obtain the function pointer from the std::function. Notice the usage of my_wifi_promiscuous_cb_t and std::function::target<>().

#include <iostream>
#include <functional>
using namespace std::placeholders;

// using fake definitions
extern "C"
{
  enum wifi_promiscuous_pkt_type_t {};
  typedef int32_t esp_err_t;
  typedef void (*wifi_promiscuous_cb_t)(void* buf, wifi_promiscuous_pkt_type_t type);
  typedef void my_wifi_promiscuous_cb_t(void* buf, wifi_promiscuous_pkt_type_t type);
  esp_err_t esp_wifi_set_promiscuous_rx_cb(wifi_promiscuous_cb_t cb)
  {
    return 0;
  }  
}

class Class
{
public:
  void mycallback(void* buff, wifi_promiscuous_pkt_type_t type) {}

  void registerMyCallback() {
    std::function<void(void*, wifi_promiscuous_pkt_type_t)> fun2 = std::bind(&Class::mycallback, this, _1, _2);
    esp_wifi_set_promiscuous_rx_cb(fun2.target<my_wifi_promiscuous_cb_t>());
  }
};

int main()
{
  Class c;
  c.registerMyCallback();
}
stefan.gal
  • 302
  • 2
  • 6
  • This is only working by chance. The interface type used by the lib is: `extern "C" void (*)(void*, wifi_promiscuous_pkt_type);` (notice that this interface has C linkage). While you using `void(void*, wifi_promiscuous_pkt_type_t)` Which has C++ linkage. These are not exactly the same. Though on your compiler they seem to have the same calling convention underneath the hood, but there is no guarantee that this is the case for other compilers. – Martin York May 31 '20 at 17:17
1

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

Martin York
  • 257,169
  • 86
  • 333
  • 562