动态创建一个函数指针,该指针调用给定实例上的方法

时间:2012-04-02 19:44:06

标签: c++ function-pointers pointer-to-member

我怀疑这是不可能的,但我想我会问。假设我有一个方法类:

class A {
public:
    void b(int c);
};

我可以指向该成员函数:

void (A::*ptr)(int) = &A::b;
(someAInstance.*ptr)(123);

我也可以滥用函数指针并创建一个直接接收A参数的指针(我不知道这是否安全,但它可以在我的机器上运行):

void (*ptr2)(A*, int) = (void (*)(A*, int))&A::b;
(*ptr2)(&someAInstance, 123);

想要的是以某种方式讨论A参数,并创建一个只需int的函数指针,但调用A::b方法我预定义的特定A实例。 A实例将为该特定函数指针保持不变,但可能有几个函数指针都指向相同的A::b方法,但使用不同的A实例。例如,我可以创建一个单独的包装函数:

A* someConstantA = new A;
void wrapper(int c) {
    someConstantA->b(c);
}

void (*ptr3)(int) = &wrapper;

现在我可以使用ptr3而不知道调用它的特定A,但我必须定义一个特殊的函数来处理它。我需要一种方法来为任意数量的A实例制作指针,因此我不能像那样硬编码。这有可能吗?


编辑:应该提到,我被困在C ++ 03的土地上,也无法使用Boost

6 个答案:

答案 0 :(得分:3)

不要创建包装器功能,创建包装器 functor 。这允许您在可调用对象中封装您想要的任何状态(例如A*)。

class A {
public:
    void b(int c) {}
};

struct wrapper {
  A* pA;
  void (A::*pF)(int);
  void operator()(int c) { (pA->*pF)(c); }
  wrapper(A* pA, void(A::*pF)(int)) : pA(pA), pF(pF) {}
};

int main () {
  A a1;
  A a2;

  wrapper w1(&a1, &A::b);
  wrapper w2(&a2, &A::b);

  w1(3);
  w2(7);
}

答案 1 :(得分:1)

如果您使用的是C ++ 11,则可以使用lambda(未经测试的代码):

template<typename T, typename A>
 std::function<void(A)> curry(T& object, void (T::*ptr)(A))
{
  return [](A a) { (object.*ptr)(std::forward<A>(a)); }
}

答案 2 :(得分:1)

如果你有一个足够新的编译器(例如gcc 4.2+),它应该包含TR1,你可以使用std::tr1::bind

#include <cstdio>
#include <tr1/functional>

class A {
public:
    void b(int c) {
        printf("%p, %d\n", (void*)this, c);
    }
};

int main() {
    A* a = new A;

    std::tr1::function<void(int)> f =
        std::tr1::bind(&A::b, a, std::tr1::placeholders::_1);  // <--
    f(4);

    delete a;

    return 0;
}

在没有TR1的纯C ++ 03中也可行,但也更麻烦:

std::binder1st<std::mem_fun1_t<void, A, int> > f =
    std::bind1st(std::mem_fun(&A::b), a);

您也可以编写自己的函数对象。

请注意,在上述所有情况下,您需要非常小心a的生命周期,因为这是一个裸指针。使用std::tr1::bind,您至少可以将指针包装在std::tr1::shared_ptr中,这样它就可以与函数对象一样长。

std::tr1::shared_ptr<A> a (new A);
std::tr1::function<void(int)> f =
    std::tr1::bind(&A::b, a, std::tr1::placeholders::_1);

答案 3 :(得分:0)

我正在使用Boost::bind

基本上:

class A
{
    int myMethod(int x)
    {
        return x*x;
    }
};

int main(int argc, char* argv[])
{
    A test();
    auto callable = boost::bind(&A::myMethod, &A, _1);

    // These two lines are equivalent:
    cout << "object with 5 is: " << test.myMethod(5) << endl;
    cout << "callable with 5 is: " << callable(5) << endl;
    return 0;
}

我认为这应该有效。我也在这里使用auto来推断boost :: bind()在编译时返回的类型,编译器可能支持也可能不支持。有关bind返回类型的说明,请参阅stackoverflow中的other question

Boost支持回到Visual Studio 2003(我认为),这一切都可以在那里工作,不过你认为我会使用BOOST_AUTO。请参阅已经链接的其他问题以获得解释。

答案 4 :(得分:0)

你想做什么是不可能的。 为了理解原因,假设它是可能的 - 函数指针必须指向可执行文件或其中一个库中的某个函数,因此它必须指向一个函数,该函数知道调用哪个A实例,就像你的包装函数。因为A的实例直到运行时才知道,所以你必须在运行时创建这些函数,这是不可能的。

你可以在C ++ 03中尝试做什么,只要你乐意传递函数对象而不是函数指针。

正如其他人已经给出了C ++ 11 lambdas,TR1和boost(所有这些都比下面更漂亮)的解决方案,但你提到你不能使用C ++ 11,我会贡献一个纯粹的C ++ 03:

int main()
{
    void (A::*ptr)(int) = &A::b;
    A someAInstance;

    std::binder1st<std::mem_fun1_t<void,A,int> > fnObj =
         std::bind1st(std::mem_fun(ptr), &someAInstance);
    fnObj(321);
};

答案 5 :(得分:0)

我已经使用模板Delegate类进行了一些工作。

// T is class, R is type of return value, P is type of function parameter
template <class T, class R, class P> class Delegate
{
    typedef R (T::*DelegateFn)(P);
private:
    DelegateFn func;
public:
    Delegate(DelegateFn func)
    {
        this->func = func;
    }
    R Invoke(T * object, P v)
    {
        return ((object)->*(func))(v);
    }
};

class A {
private:
    int factor;
public:
    A(int f) { factor = f; }
    int B(int v) { return v * factor; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    A * a1 = new A(2);
    A * a2 = new A(3);

    Delegate<A, int, int> mydelegate(&A::B);

    // Invoke a1->B
    printf("Result: %d\n", mydelegate.Invoke(a1, 555));

    // Invoke a2->B
    printf("Result: %d\n", mydelegate.Invoke(a2, 555));

    _getch();
    delete a1;
    delete a2;
    return 0;
}