问题
I want to pass a class method to another function and I wrote these codes:
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);
...
}
};
The above code shows the function that I called from class constructor!
myclass()
{
xObject->executeQuery("test", &(myclass::myfunction));
}
And this part of code shows how I pass myfunction to that method! But, during compile time I got this error:
error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.
I called a thread constructor by same syntax! But it sounds like that the thread constructor makes a new function pointer that I can't understand! Do you have any idea how I can solve this problem with/without thread solution? The below code shows the thread constructor header:
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)...)));
}
回答1:
Update:
In your example you use the function pointer with sqlite3_exec
. sqlite3_exec
requires a C-style function as argument callback
. You can not use a pointer to a class member function here!
Something like this might be a work-around. But beware of thread-safty:
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));
Old answer:
You can use std::function
to wrap class member functions (see 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));
In order to give a pointer to a class member function, you need to also provide an object on which the member function should be called.
std::bind
allows you to do this (and even more). In the case above, the first argument to std::bind
is the pointer to the class member function and the second argument is the object which shall be used to call the function. The following arguments _1
, ... are placeholders for arguments you will provide later when using the function pointer.
回答2:
std::thread
and many other libraries don't receive function pointer. They receive functor.
functor is all things which can be called with ()
operator. So, function pointer is functor:
void (*pf)();
// we can do it
pf();
We also can call the object which overloads operator ()
.
struct Functor
{
void operator ()();
};
Funtor f;
// we also can do it
f();
Member function pointer is not functor. (you can't mpf(this);
. this->*mpf();
is correct.) However, the constructor of std::thread
performs bind.
For example:
#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 */));
}
Look this: call_functor(std::bind(WithParam, 123));
. Although WithParam
cannot be called with f()
because it has a parameter, we can use WithParam
by binding its parameter to 123
.
std::bind
can perform not only binding parameter, but also binding this pointer with member function pointer. So we also can do this: call_functor(std::bind(&Cls::member, &c /* this pointer */));
.
We cannot know the type of all functors. For example: void (*)()
, struct bar
, or std::bind
...
So, to receive functor as parameter, we should use template. For example, look at std::thread
's constructor:
template<typename _Callable, typename... _Args>
explicit
thread(_Callable&& __f, // <-- here
...
In your case, you should do like this:
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));
}
Do you mind use template (Maybe you want to hide the implementation of executeQuery
)? Then, there's another solution, std::function<>
. it's polymorphism-wrapper to functor. It is a little slower than functor, but it can be good solution if you mind use template.
class x
{
public:
void executeQuery(std::string query,
const std::function<int ()(void*, int, char**, char**)> &f);
};
Usage is almost equal to functor. (maybe you need to explicit-cast into std::function<>
) In fact, std::function
is functor, too! (it can be called with ()
operator..)
edit: Ummm.. I've just noticed you're using f
like this:
int rc = sqlite3_exec(db, query.c_str(), &f, 0, &errMessage);
Maybe the third parameter of sqlite3_exec
is function pointer, right? It'll become complicated..
According to here, the 4th argument of sqlite3_exec
will be passed into f
as the 1st argument. Then, we can write code like this:
// 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);
}
edit2: Um, I see the problem.. For one thing, it works well if you do this:
xObject->executeQuery("test", std::bind(&myclass::myfunction, this));
Change into:
using namespace std::placeholders;
...
xObject->executeQuery("test", std::bind(&myclass::myfunction, this, _1, _2, _3, _4));
_1
, _2
, _3
, _4
are placeholders. (If you want to know about placeholders, search google..)
In this case, I don't think placeholders are required, But if there's no placeholders, error occurs...
edit3: the reason of why we should use placeholders: Why are placeholders required in std::bind in this case?
回答3:
After much hanging around and trying out possible solutions, I found that there is no solution for my problem. I had a conversation with a professional c++ programmer(Hedayat Vatankhah) and he explained since a method of a class has a special address in c++, you can't pass its pointer to a c function (like sqlite3_exec
) that expect an absolute address.
BTW, it sounds like that there is an extension for gcc that can convert method address to absolute address (but I leave this solution and I try some simple sqlite3 function to create new version of sqlite3_exec)
来源:https://stackoverflow.com/questions/22909459/how-can-i-pass-a-class-method-to-another-function-like-what-happen-in-thread-con