如何将类方法传递给另一个函数,就像线程构造函数中发生的那样

时间:2014-04-07 10:12:17

标签: c++ function-pointers argument-passing

我想将一个类方法传递给另一个函数,我写了这些代码:

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)...)));
  }

3 个答案:

答案 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 barstd::bindstd::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的第三个参数是函数指针,对吗?它会变得复杂..

根据heref的第四个参数将作为第一个参数传递给// 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)