【问题标题】:Dynamically create a function pointer that calls a method on a given instance动态创建调用给定实例上的方法的函数指针
【发布时间】:2012-04-16 11:43:42
【问题描述】:

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

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 实例上调用A::b 方法我已经预定义了。 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

【问题讨论】:

  • 必须是函数指针吗?你想要做的通常是使用函数对象来完成(例如使用std::bind)。
  • @KennyTM 任何可调用的作品;我不知道std::bind,正在阅读中
  • ^^^ 或 boost::bind 如果你没有 C++11。
  • 函数对象是定义operator()的对象,因此您可以像函数一样使用它。在内部,它可以保存一个this 指针和一个函数指针。 std::bind 是一种方法,或者您可以手动滚动。
  • @Ben 啊,我刚刚意识到std::bind 也在 c++11 中,所以我不能使用它(编辑了我遇到问题的环境)。我没想过要创建一个实现operator() 的类;这可能是这样做的方法

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


【解决方案1】:

不要创建包装器函数,创建包装器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);
}

【讨论】:

    【解决方案2】:

    如果您有足够新的编译器(例如 gcc 4.2+),它应该包括 TR1,您可以在其中使用 std::<i>tr1</i>::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】:

      如果您使用的是 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)); }
      }
      

      【讨论】:

      • 我会为 lambda 杀戮,但遗憾的是没有;卡在 03
      【解决方案4】:

      我会为此使用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() 在编译时返回的类型,您的编译器可能支持也可能不支持。请参阅 stackoverflow 上的 other question 以了解 bind 的返回类型。

      Boost 支持回到 Visual Studio 2003(我认为),这一切都将在那里工作,尽管我认为您将使用 BOOST_AUTO。请参阅已链接的其他问题以获取解释。

      【讨论】:

        【解决方案5】:

        你想做的事是不可能的。 要了解原因,假设它是可能的 - 函数指针必须指向可执行文件或其库之一中的某个函数,因此它必须指向一个知道要调用 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);
        };
        

        【讨论】:

        • 我认为KennyTM's answer 已经涵盖了相同的 C++03 实现,但感谢开头的解释
        • @MichaelMrozek 确实如此......自从我第一次阅读现有答案以来,他已经更新了它
        【解决方案6】:

        我已经使用模板 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;
        }
        

        【讨论】:

          猜你喜欢
          • 2013-10-04
          • 1970-01-01
          • 2012-12-19
          • 2010-09-14
          • 2011-02-11
          • 1970-01-01
          • 1970-01-01
          • 2013-02-15
          • 1970-01-01
          相关资源
          最近更新 更多