最近遇到一个需求,需要将C++类成员函数强制转化为C-Style的回调函数,以供C原生API调用。
C++实现注册回调函数机制可分为2类:
理想状态下,我们更倾向于采用1的方式进行开发。推荐使用C++11新特性lambda定义回调函数,使用std::function接收和保存不同class的成员函数指针。
如果想借用优质的C原生API注册回调,则需要考虑将成员函数指针强制转化为严格的C-Style普通函数指针。推荐使用C++17新特性inline static构造如下Wrapper类——CallbackWrapper。
当然,如果我们对回调函数访问类成员变量和成员函数没有强需要,也可以在类内声明友元函数或者static静态成员函数来注册回调函数。此方法不做赘述。
#include
#include
#include
#include
// ============================================================================
typedef void(*func_callback)(const char*xx, void* yy);
static std::unordered_map g_funcs;
static std::unordered_map g_yys;
static void g_regist_handler(const char*xx, void(*func)(const char*xx, void *yy), void *yy) {
g_funcs[xx] = func;
g_yys[xx] = yy;
std::cout << "zhong yu deng dao ni!" << std::endl;
}
static void g_send(const char* x) {
g_funcs[x](x, g_yys[x]);
}
// ============================================================================
class A1 {
public:
using lambda_callback = std::function;
typedef void(*func_callback)(const char* xx, void* yy);
A1() = default;
~A1() = default;
void regist_handler(const char* x, lambda_callback func, void* y) {
_handlers[x] = func;
_params[x] = y;
}
void send(const char* x) {
_handlers[x](x, _params[x]);
}
private:
std::unordered_map _handlers;
std::unordered_map _params;
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class A2 {
public:
typedef void(*func_callback)(const char*xx, void *yy);
A2() = default;
~A2() = default;
void regist_handler(const char* x, func_callback func, void* y) {
g_regist_handler(x, func, y);
}
private:
};
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class CallbackWrapper {
public:
static void wrapper_func(const char*xx, void *yy) {
broker_func(xx, yy);
}
// g++ -std=c++17
inline static std::function broker_func;
};
class B {
public:
B() = default;
~B() = default;
void print() {
std::cout << "B obj." << std::endl;
}
void on_test_handler(const char*xx, void *yy) {
this->print();
std::cout << xx << "," << (const char*)yy << std::endl;
}
void init() {
// ABC
_a1.regist_handler(
"ABC",
[this](const char*xx, void *yy){
this->on_test_handler(xx, yy);
},
(void*)"111123"
);
// DEF
_a1.regist_handler(
"DEF",
[this](const char*xx, void *yy){
this->on_test_handler(xx, yy);
},
(void*)"444456"
);
// GHI
_a1.regist_handler(
"GHI",
[this](const char*xx, void *yy){
this->on_test_handler(xx, yy);
},
(void*)"777789"
);
// abc
CallbackWrapper::broker_func = std::bind(&B::on_test_handler, this, std::placeholders::_1, std::placeholders::_2);
_a2.regist_handler(
"abc",
CallbackWrapper::wrapper_func,
(void*)"123"
);
// def
CallbackWrapper::broker_func = std::bind(&B::on_test_handler, this, std::placeholders::_1, std::placeholders::_2);
_a2.regist_handler(
"def",
CallbackWrapper::wrapper_func,
(void*)"456"
);
// ghi
CallbackWrapper::broker_func = std::bind(&B::on_test_handler, this, std::placeholders::_1, std::placeholders::_2);
_a2.regist_handler(
"ghi",
CallbackWrapper::wrapper_func,
(void*)"789"
);
}
void send(const char* x) {
_a1.send(x);
}
private:
A1 _a1; // callback for own server
A2 _a2; // callback for c server
};
// ============================================================================
// g++ *.cpp -std=c++17 -o main
int main(int argc, const char* argv[]) {
std::cout << "Hello World!" << std::endl;
B b;
b.init();
// lambda function with this to stl-function-style callback function
b.send("ABC");
b.send("DEF");
b.send("GHI");
// class member function to c-style callback function
g_send("abc");
g_send("def");
g_send("ghi");
return 0;
}
// ============================================================================
上述代码在注册不同成员函数时会出现bug(仅注册成功最后一个成员函数指针),与执行回调函数的时机有关,遂作如下改动即可避免该问题:
#include
#include
#include
using namespace std;
class CallbackWrapper {
public:
static auto get_current_function(size_t i) {
cout << "get_current_function_index=" << i << endl;
return broker_func[i];
}
// g++ -std=c++17
static void add_function_obj(std::function func) {
broker_func.push_back(func);
cout << "broker_func.size=" << broker_func.size() << endl;
}
private:
inline static vector> broker_func;
};
class regist {
public:
typedef void(*func_callback)(int*, int*);
regist() = default;
~regist() = default;
void do_regist(func_callback func) {
cout << "do_regist &func= " << &func << endl;
fs.push_back(func);
}
vector fs;
};
class test {
public:
test(/* args */) = default;
~test() = default;
void demo1(int* a, int* b) {
cout << "demo1: " << *a << " , " << *b << endl;
}
void demo2(int* a, int* b) {
cout << "demo2: " << *a << " , " << *b << endl;
}
void init() {
CallbackWrapper::add_function_obj(std::bind(
&test::demo1, this,
std::placeholders::_1, std::placeholders::_2
));
r.do_regist([](int* x, int* y){
CallbackWrapper::get_current_function(0)(x, y);
});
CallbackWrapper::add_function_obj(std::bind(
&test::demo2, this,
std::placeholders::_1, std::placeholders::_2
));
r.do_regist([](int* x, int* y){
CallbackWrapper::get_current_function(1)(x, y);
});
}
regist r;
};
int main() {
test t;
t.init();
int a = 12, b = 13, c = 14;
t.r.fs[0](&a, &b);
t.r.fs[1](&b, &c);
return 0;
}