我可以使用 std::bind 将指向成员函数的指针转换为指向函数的指针吗?
Can I use std::bind to convert a pointer to member function into a pointer to function?
我想传递一个成员函数作为回调。回调是一个基本的函数指针。
所以我有类似的东西:
h 文件:
void (*pRequestFunc) (int someint) = 0;
void RegisterRequestCallBack(void (*requestFunc) (int someint))
{
pRequestFunc = requestFunc;
}
class A
{
void callBack(int someint);
}
Cpp 文件:
RegisterRequestCallBack(&A::callBack); // This does not work.
注意我试图从我的更大的例子中提取这个例子并删掉所有其他的东西——所以它可能并不完美。
据我所知,问题是成员函数指针确实(在幕后)有一个额外的参数(和实例 - 即 this
)并且与普通函数指针不兼容。
RegisterRequestCallBack()
实际上不是我的代码 - 所以我无法更改它。
所以我读到 boost::bind 可以做我需要的 - 我希望 c++11 std::bind 可以做同样的事情 - 但我不知道如何有效地使用它从成员函数指针获取标准函数指针...
我想要这样的东西:
std::bind(&A::callBack)
...就我所知,我对网上例子的理解很差:(
要能够绑定到您需要做的成员函数:
std::function<void(int)> function = std::bind(&A::foo, this, std::placeholders::_1);
或者您的情况:
RegisterRequestCallBack(std::bind(&A::callback, this, std::placeholders::_1));
但在我看来,最明确的实现方式是使用 lambda 函数。在这里你有一个例子可以做类似的事情可以启发你:
#include <array>
#include <map>
#include <vector>
#include <functional>
#include <iostream>
class TaskManager {
public:
using task_t = std::function<void()>;
void run();
void addTask(task_t task);
private:
std::vector<task_t> _tasks;
};
void TaskManager::run() {
for (auto& task : _tasks) {
task();
}
}
void TaskManager::addTask(task_t task) {
_tasks.push_back(task);
}
class Example {
public:
Example(){
taskManager.addTask([this]() {
task1();
});
taskManager.addTask([this,a=int(4)](){
task2(a);
});
}
TaskManager taskManager;
private:
void task1(){ std::cout << "task1!\n"; }
void task2(int a){ std::cout << "task2 says: " << a << "\n"; }
};
int main() {
Example example;
example.taskManager.run();
}
输出:
task1!
task2 says: 4
NathanOliver 的评论是正确的,您的怀疑大部分是正确的。指向成员函数的指针的确切工作方式没有具体说明,但包括 this
作为隐藏参数 mostly 是有效的。您只需要为继承和指向 virtual
函数的指针做一些额外的工作(是的,您也可以获取它们的地址)。
现在,回调通常包含一个受您控制的 void*
参数,您可以使用它来传递 A*
。在这些情况下,您可以编写一个包装器 (static
) 函数,将 void*
转换回 A*
并执行对 &A::callback
的实际调用。
这里不是这种情况。注册采用单一功能,没有数据。为了让它在 real-life 情况下工作,你必须求助于极端的解决方案——而不是可移植的 C++。一种这样的方法是动态生成程序集 (!)。您在运行时创建
的编译等效项
void __trampoline_0x018810000 (int i)
{
A* __this = reinterpret_cast<A*>(0x018810000);
__this->callback(i);
}
如您所见,您必须为每个 A*
值生成一个蹦床,而管理它们的生命周期是一个很大的痛苦。
我想传递一个成员函数作为回调。回调是一个基本的函数指针。
所以我有类似的东西:
h 文件:
void (*pRequestFunc) (int someint) = 0;
void RegisterRequestCallBack(void (*requestFunc) (int someint))
{
pRequestFunc = requestFunc;
}
class A
{
void callBack(int someint);
}
Cpp 文件:
RegisterRequestCallBack(&A::callBack); // This does not work.
注意我试图从我的更大的例子中提取这个例子并删掉所有其他的东西——所以它可能并不完美。
据我所知,问题是成员函数指针确实(在幕后)有一个额外的参数(和实例 - 即 this
)并且与普通函数指针不兼容。
RegisterRequestCallBack()
实际上不是我的代码 - 所以我无法更改它。
所以我读到 boost::bind 可以做我需要的 - 我希望 c++11 std::bind 可以做同样的事情 - 但我不知道如何有效地使用它从成员函数指针获取标准函数指针...
我想要这样的东西:
std::bind(&A::callBack)
...就我所知,我对网上例子的理解很差:(
要能够绑定到您需要做的成员函数:
std::function<void(int)> function = std::bind(&A::foo, this, std::placeholders::_1);
或者您的情况:
RegisterRequestCallBack(std::bind(&A::callback, this, std::placeholders::_1));
但在我看来,最明确的实现方式是使用 lambda 函数。在这里你有一个例子可以做类似的事情可以启发你:
#include <array>
#include <map>
#include <vector>
#include <functional>
#include <iostream>
class TaskManager {
public:
using task_t = std::function<void()>;
void run();
void addTask(task_t task);
private:
std::vector<task_t> _tasks;
};
void TaskManager::run() {
for (auto& task : _tasks) {
task();
}
}
void TaskManager::addTask(task_t task) {
_tasks.push_back(task);
}
class Example {
public:
Example(){
taskManager.addTask([this]() {
task1();
});
taskManager.addTask([this,a=int(4)](){
task2(a);
});
}
TaskManager taskManager;
private:
void task1(){ std::cout << "task1!\n"; }
void task2(int a){ std::cout << "task2 says: " << a << "\n"; }
};
int main() {
Example example;
example.taskManager.run();
}
输出:
task1!
task2 says: 4
NathanOliver 的评论是正确的,您的怀疑大部分是正确的。指向成员函数的指针的确切工作方式没有具体说明,但包括 this
作为隐藏参数 mostly 是有效的。您只需要为继承和指向 virtual
函数的指针做一些额外的工作(是的,您也可以获取它们的地址)。
现在,回调通常包含一个受您控制的 void*
参数,您可以使用它来传递 A*
。在这些情况下,您可以编写一个包装器 (static
) 函数,将 void*
转换回 A*
并执行对 &A::callback
的实际调用。
这里不是这种情况。注册采用单一功能,没有数据。为了让它在 real-life 情况下工作,你必须求助于极端的解决方案——而不是可移植的 C++。一种这样的方法是动态生成程序集 (!)。您在运行时创建
的编译等效项void __trampoline_0x018810000 (int i)
{
A* __this = reinterpret_cast<A*>(0x018810000);
__this->callback(i);
}
如您所见,您必须为每个 A*
值生成一个蹦床,而管理它们的生命周期是一个很大的痛苦。