如何将类方法传递给另一个函数,就像线程构造函数中发生的那样
How can I pass a class method to another function like what happen in thread constructor
我想将一个类方法传递给另一个函数,我写了这些代码:
class x {
executeQuery(std::string query, int (*f)(void* cpuInfo, int argc, char** argv, char** azColName))
{
int rc = sqlite3_exec(db, query.c_str(), &f, 0, &errMessage);
...
}
};
上面的代码显示了我从类构造函数调用的函数!
myclass()
{
xObject->executeQuery("test", &(myclass::myfunction));
}
这部分代码展示了我如何将 myfunction 传递给该方法!但是,在编译期间,我收到此错误:
error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.
我用相同的语法调用了线程构造函数!但听起来线程构造函数制作了一个我无法理解的新函数指针!您知道我如何在有/没有螺纹解决方案的情况下解决此问题吗?下面的代码显示了线程构造函数标头:
template<typename _Callable, typename... _Args>
explicit
thread(_Callable&& __f, _Args&&... __args)
{
_M_start_thread(_M_make_routine(std::__bind_simple(
std::forward<_Callable>(__f),
std::forward<_Args>(__args)...)));
}
更新:
在您的示例中,您将函数指针与 sqlite3_exec
一起使用。 sqlite3_exec
需要一个 C 样式函数作为参数callback
。您不能在此处使用指向类成员函数的指针!
这样的事情可能是一种解决方法。但要小心线程安全:
namespace wrap {
typedef std::function<int(void*,int,char**,char**)> QueryFunction;
inline QueryFunction& function() {
static QueryFunction f;
return f;
}
void callback(void* cpuInfo, int argc, char** argv, char** azColName);
}
void wrap::callback(void* cpuInfo, int argc, char** argv, char** azColName) {
function()(cpuInfo, argc, argv, azColName);
}
class x {
executeQuery(std::string query, QueryFunction f)
{
wrap::function() = f;
int rc = sqlite3_exec(db, query.c_str(), &wrap::callback, 0, &errMessage);
...
}
};
MyClass* obj = ...;
xObject->executeQuery("test", std::bind(myclass::myfunction, obj));
旧答案:
您可以使用 std::function
包装类成员函数(请参阅此处):
#include <functional>
typedef std::function<int(void*,int,char**,char**)> QueryFunction;
class x {
public:
void executeQuery(std::string query, QueryFunction f) {
f(ptr,0,"hello","test"); // call function
}
};
MyClass* obj = ...;
using std::placeholders;
xObject->executeQuery("test", std::bind(myclass::myfunction, obj, _1, _2, _3, _4));
为了提供指向类成员函数的指针,还需要提供一个对象,应在其上调用成员函数。
std::bind
允许您执行此操作(甚至更多)。在上面的情况下,要std::bind
的第一个参数是指向类成员函数的指针,第二个参数是用于调用函数的对象。以下论点_1
, ...是稍后使用函数指针时将提供的参数的占位符。
std::thread
和许多其他库不接收函数指针。他们接收函子。
函子是可以用运算符调用的所有东西()
。因此,函数指针是函子:
void (*pf)();
// we can do it
pf();
我们也可以调用重载的对象 operator ()
.
struct Functor
{
void operator ()();
};
Funtor f;
// we also can do it
f();
成员函数指针不是函子。(你不能mpf(this);
. this->*mpf();
是正确的。但是,std::thread
的构造函数执行绑定。
例如:
#include <iostream>
#include <functional> // for std::bind
template <typename F>
void call_functor(F f)
{
f();
}
void foo() { std::cout << "foon"; }
struct bar { void operator ()() { std::cout << "barn"; } };
void WithParam(int i) { std::cout << "param : " << i << "n"; }
class Cls
{
public:
void member() { std::cout << "Cls::membern"; }
};
int main()
{
// we can do these, because both foo and b are functor.
bar b;
call_functor(foo);
call_functor(b);
// bind `123`
call_functor(std::bind(WithParam, 123));
// bind `c`
Cls c;
call_functor(std::bind(&Cls::member, &c /* this pointer */));
}
看看这个:call_functor(std::bind(WithParam, 123));
.尽管WithParam
不能用f()
调用,因为它有一个参数,但我们可以通过将其参数绑定到123
来使用WithParam
。
std::bind
不仅可以执行绑定参数,还可以将此指针与成员函数指针绑定。所以我们也可以这样做:call_functor(std::bind(&Cls::member, &c /* this pointer */));
.
我们无法知道所有函子的类型。例如:void (*)()
、struct bar
或std::bind
...
因此,要接收函子作为参数,我们应该使用模板。例如,查看std::thread
的构造函数:
template<typename _Callable, typename... _Args>
explicit
thread(_Callable&& __f, // <-- here
...
在您的情况下,您应该这样做:
class x
{
public:
template <typename Functor>
void executeQuery(std::string query, Functor f)
{
...
int result = f(cpuInfo, argc, argv, azColName);
...
}
};
...
myclass()
{
xObject->executeQuery("test", std::bind(&myclass::myfunction, this));
}
你介意使用模板(也许你想隐藏executeQuery
的实现)吗?然后,还有另一种解决方案,std::function<>
.它是函子的多态包装器。它比函子慢一点,但如果您介意使用模板,它可能是一个很好的解决方案。
class x
{
public:
void executeQuery(std::string query,
const std::function<int ()(void*, int, char**, char**)> &f);
};
用法几乎等于函子。(也许你需要显式转换为std::function<>
)事实上,std::function
也是函子!(可以使用()
运算符调用它。
编辑:嗯。我刚刚注意到你正在使用这样的f
:
int rc = sqlite3_exec(db, query.c_str(), &f, 0, &errMessage);
也许sqlite3_exec
的第三个参数是函数指针,对吧?它会变得复杂。
根据这里,sqlite3_exec
的第 4 个参数将作为第 1 个参数传递到f
。然后,我们可以编写这样的代码:
// header file of `class x`
class x
{
private:
static void ExecuteQuery_Callback(void *param, int argc, char** argv, char** azColName);
// use std::function<>
typedef std::function<int (void*, int, char**, char**)> ExecQueryFunctor;
void executeQuery_impl(std::string query, const ExecQueryFunctor &f);
public:
// wrapper
template <typename F> void executeQuery(std::string query, F f)
{
executeQuery_impl(query, ExecQueryFunctor(f));
}
};
// .cpp file of `class x`
void x::executeQuery_impl(std::string query, const x::ExecQueryFunctor &f)
{
int rc = sqlite3_exec(db, query.c_str(), ExecuteQuery_Callback, &f, &errMessage);
...
}
void x::ExecuteQuery_Callback(void *param, int argc, char** argv, char** azColName)
{
const ExecQueryFunctor *pfunctor = (const ExecQueryFunctor *)param;
(*pfunctor)(NULL, argc, argv, azColName);
}
编辑2:嗯,我看到了问题。首先,如果您这样做,它会很好地工作:
xObject->executeQuery("test", std::bind(&myclass::myfunction, this));
更改为:
using namespace std::placeholders;
...
xObject->executeQuery("test", std::bind(&myclass::myfunction, this, _1, _2, _3, _4));
_1
、_2
、_3
、_4
是占位符。(如果您想了解占位符,请搜索谷歌。
但是如果没有占位符,就会发生错误......
edit3:我们应该使用占位符的原因:为什么在这种情况下在 std::bind 中需要占位符?
经过多次闲逛并尝试可能的解决方案,我发现我的问题没有解决方案。我与一位专业的 c++ 程序员(Hedayat Vatankhah)进行了交谈,他解释说,由于类的方法在 c++ 中具有特殊地址,因此您不能将其指针传递给期望绝对地址的 c 函数(如 sqlite3_exec
)。顺便说一句,听起来 gcc 有一个扩展可以将方法地址转换为绝对地址(但我离开了这个解决方案,我尝试一些简单的 sqlite3 函数来创建新版本的 sqlite3_exec)
- 在派生自线程类的构造函数中传递字符串
- 线程构造函数周围的可变参数模板包装器无法编译
- C++使用函数对象的线程,如何调用多个析构函数而不是构造函数?
- 运行 std::线程不在构造函数中
- 具有unique_ptr和线程的默认矢量构造函数
- 为什么当我在构造函数中创建线程时,实例化对象和对象的指针的行为不同
- 类成员在构造函数(线程)之后更改地址
- unordered_map中的C 线程(无复制构造函数)
- C 从类的构造函数运行Boost线程
- 通过类构造函数创建线程
- 可以在构造函数初始值设定项列表中使用标准::线程
- 迷宫构造函数问题 [线程 1:EXC_BAD_ACCESS(代码 = 1,地址 = 0x8)]
- C 好奇的情况将TypeName/DataType名称/类名称传递给线程构造函数
- 为线程构造函数传递引用以将其绑定到函数失败
- 用函数构造std::线程
- C++无限依次运行两个函数(线程)
- c++将各种参数传递给父类构造函数(线程c++11)
- 初始化头文件中声明的模板构造函数/例程
- 使用2参数函数构造std::线程时出错
- 使用类构造函数在线程中启动成员函数