I want to pass class member function as callback

Asked 2 years ago, Updated 2 years ago, 67 views

I want to pass a member function of my own class to a function in the library that takes the callback function as an argument, but it doesn't work.
Please point out what's wrong with you.

[What I did]

Because the member variable is not accessible when passed by the lambda function. ② std::bind attempts to wrap but fails to compile.

 error:cannot convert'std::_Bind<std::_Mem_fn<void(test_class::*)(unsigned char*, unsigned char)>(test_class*, std:::_Placeholder<1>, std:_Placeholder_lt;2>>>>

】What I want to realize したい
I want to pass my own class member function as a callback.

コードTest Code br
Excerpts from espnow.h

typedef unsigned charu8;
typeedef void (*esp_now_send_cb_t) (u8*mac_addr, u8status);
intesp_now_register_send_cb(esp_now_send_cb_tcb);

test_class.h

class test_class
{
public:
    uint8_tval=88;
    test_class(){};
    void initialize();
    void send_cb(uint8_t*macaddr,uint8_t status);
};

test_class.cpp

void test_class::initialize()
{
    Serial.println("initialize()");

    // I want to pass send_cb() to esp_now_register_send_cb().

    // このThere is no compilation error with this code, but I cannot access the val.
    esp_now_register_send_cb([](uint8_t*macaddr,uint8_t status){
        Serial.print("esp_now_register_send_cb([]):val=");
        // Serial.println(val);// Uncomment Error
    });

    // st I tried using std::bind, but I couldn't give it to esp_now_register_send_cb().
    auto func=std::bind(&test_class::send_cb, this, std::placeholders::_1, std::placeholders::_2);
    uint8_t macaddr[] = {0x09, 0x00, 0xA6, 0x18, 0x11, 0xBC};
    func(macaddr,val);// functioning
    esp_now_register_send_cb(func); // Uncomment Compilation Error
    // error:cannot convert'std::_Bind<std::_Mem_fn<void(test_class::*)(unsigned char*, unsigned char)>(test_class*, std::_Placeholder<1>, std::_Placeholder<2>tend_to_spond;vd:'to_s
}

void test_class::send_cb(uint8_t*macaddr,uint8_t status) 
{
    Serial.printf("send_cb():%d:%d\n", macaddr[0], status);
}

test.ino

#include<arduino.h>
# include <ESP8266 WiFi.h>
US>extern "C" {
  # include <espnow.h>
}

test_class test;
void setup()
{
    Serial.begin (115200);
    test.initialize();
}

void loop(){}

[Environment]
Windows 10
Arduino IDE 1.8.3
WSP8266SDK 2.3.0

Please give us your advice.

ごPlease reply br
As a C++ beginner, I would like to keep it for the same beginner programmers.
To solve this problem, we needed to understand the difference in function structure between the "function pointer" in C language and the "member function pointer" in C++ language.

As a beginner, I can't explain this difference (or, I can't say...) If you search for the difference between function pointers and member function pointers in c language, you will find many sites for detailed explanations or application solutions.

Beginners don't understand (or can't associate) keywords to solve problems, so they run into a wall and stomp.
At that time, the advice of stackoverflow staff will help us find a way to solve the problem.
I always look at all of you who have received responses through stackoverflow with respect.
I'll try my best to get out of the way and help others. (It's still a long way off...)
774RR, Hideki, thank you.

c++ arduino

2022-09-30 15:51

2 Answers

In the old days, MFC used the following procedure, but if esp_now_register_send_cb cannot be changed, this method cannot be taken.Just for your information.

The non-static member function takes the implicit argument this, so the calling procedure is incompatible at the assembly level with questions tagged with

, so you cannot point to any non-static member function in the class.

If you want to call back the class's non-static member functions,

  • The static member function is compatible with the normal function of and can be pointed to with a pointer to the normal function (but not this).
  • Need this to call non-static member functions

Therefore, this needs to be delivered separately, and the implementation looks like this

class test_class{
    static int CallbackSugarFunc(void*arg){
        return reinterpret_cast<test_class*>(arg)->callbackfunc();
    }
    int callbackfunc();
};

Library side to register callback is also

typedefint(*callback_entry_func)(void*arg);
int register_callback(callback_entry_funcf, void*arg_to_callbackfunc);

As shown in , you can pass additional void*, so you can pass this when registering.

void test_class::initialize(){
    register_callback(&test_class::CallbackSugarFunc, this);
}

If the library does not have a way to pass this as an additional argument, you should create another class to manage the callback list and keep the this list for callback.

delegate manages this and pointers to non-static member functions as a set.As expected, it's a late-stage language. complements the deficiencies.


2022-09-30 15:51

Unfortunately, I don't think so.

A function created with std::bind has the same call as a normal function, but it cannot be a function pointer because the internal structure is completely different.

The lambda function can be used as a normal function without capturing variables.However, if you capture this so that you can access the member variable, it is no longer a normal function, so you cannot pass it to esp_now_register_send_cb.

Best of all, modify esp_now_register_send_cb to

 intesp_now_register_send_cb(std::function<void(u8*,u8)>cb);

If it is an external function, you cannot change it in the first place.

Why don't you create an independent callback function, where you receive a callback and call the member function of the registered object?

For your information, the capture of this is

[this](uint8_t*macaddr,uint8_t status){
        Serial.print("esp_now_register_send_cb([]):val=");
        Serial.println(this->val);
    }

You can do the following:


2022-09-30 15:51

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.