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?