我想将一个类方法传递给另一个函数,我写了这些代码:
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)...)));
}
答案 0 :(得分:1)
<强>更新强>:
在您的示例中,您将函数指针与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
来包装类成员函数(请参阅here):
#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
,...是稍后在使用函数指针时将提供的参数的占位符。
答案 1 :(得分:0)
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
的构造函数执行 bind 。
例如:
#include <iostream>
#include <functional> // for std::bind
template <typename F>
void call_functor(F f)
{
f();
}
void foo() { std::cout << "foo\n"; }
struct bar { void operator ()() { std::cout << "bar\n"; } };
void WithParam(int i) { std::cout << "param : " << i << "\n"; }
class Cls
{
public:
void member() { std::cout << "Cls::member\n"; }
};
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()
调用WithParam
因为它有参数,但我们可以通过绑定将123
的参数用于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);
};
。它是funorp的多态 - 包装器。它比仿函数慢一点,但如果您介意使用模板,它可能是一个很好的解决方案。
std::function<>
用法几乎等于仿函数。 (也许你需要明确地转换成std::function
)事实上,()
也是函子! (可以使用f
运算符调用它。)
编辑:嗯...我刚刚注意到您正在使用int rc = sqlite3_exec(db, query.c_str(), &f, 0, &errMessage);
这样:
sqlite3_exec
也许sqlite3_exec
的第三个参数是函数指针,对吗?它会变得复杂..
根据here,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);
}
。然后,我们可以编写如下代码:
xObject->executeQuery("test", std::bind(&myclass::myfunction, this));
edit2:嗯,我看到了问题..首先,如果你这样做会很好:
using namespace std::placeholders;
...
xObject->executeQuery("test", std::bind(&myclass::myfunction, this, _1, _2, _3, _4));
转变为:
_1
_2
,_3
,_4
,{{1}}是占位符。 (如果你想了解占位符,请搜索谷歌..)
在这种情况下,我不认为占位符是必需的,但如果没有占位符,则会发生错误...
edit3:我们应该使用占位符的原因:Why are placeholders required in std::bind in this case?
答案 2 :(得分:0)
经过多次闲逛并尝试可能的解决方案后,我发现我的问题没有解决办法。我与一位专业的c ++程序员(Hedayat Vatankhah)进行过对话,他解释说,因为类的方法在c ++中有一个特殊的地址,你不能将它的指针传递给ac函数(比如sqlite3_exec
)绝对地址。
顺便说一句,听起来有一个gcc的扩展,可以将方法地址转换为绝对地址(但我留下这个解决方案,我尝试了一些简单的sqlite3函数来创建新版本的sqlite3_exec)