Question

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)...)));
  }
Was it helpful?

Solution

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.

OTHER TIPS

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?

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)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top