【问题标题】:raw function pointer from a bound method来自绑定方法的原始函数指针
【发布时间】:2009-02-05 15:37:15
【问题描述】:

我需要将一个方法绑定到一个函数回调中,除非这个 sn-p 是不合法的,正如demote-boostfunction-to-a-plain-function-pointer 中所讨论的那样。

获得这种行为的最简单方法是什么?

struct C {
void m(int x) {
    (void) x;
    _asm int 3;
}};

typedef void (*cb_t)(int);
int main() {
    C c;
    boost::function<void (int x)> cb = boost::bind(&C::m, &c, _1);
    cb_t raw_cb = *cb.target<cb_t>(); //null dereference
    raw_cb(1);
    return 0;
}

【问题讨论】:

    标签: c++ boost delegates callback


    【解决方案1】:

    您可以创建自己的类来执行与 boost 绑定功能相同的操作。该类所要做的就是接受函数类型和指向包含该函数的对象的指针。例如,这是一个 void return 和 void param delegate:

    template<typename owner>
    class VoidDelegate : public IDelegate
    {
    public:
       VoidDelegate(void (owner::*aFunc)(void), owner* aOwner)
       {
          mFunction = aFunc;
          mOwner = aOwner;
       }
       ~VoidDelegate(void)
       {}
       void Invoke(void)
       {
          if(mFunction != 0)
          {
             (mOwner->*mFunction)();
          }
       }
    
    private:
       void (owner::*mFunction)(void);
       owner* mOwner;
    };
    

    用法:

    class C
    {
       void CallMe(void)
       {
          std::cout << "called";
       }
    };
    int main(int aArgc, char** aArgv)
    {
       C c;
       VoidDelegate<C> delegate(&C::CallMe, &c);
       delegate.Invoke();
    }
    

    现在,由于VoidDelegate&lt;C&gt; 是一种类型,拥有这些类型的集合可能不切实际,因为如果列表也包含 B 类的函数怎么办?没办法。

    这就是多态性发挥作用的地方。你可以创建一个接口IDelegate,它有一个函数Invoke:

    class IDelegate
    {
       virtual ~IDelegate(void) { }
       virtual void Invoke(void) = 0;
    }
    

    如果VoidDelegate&lt;T&gt; 实现了 IDelegate,您可以拥有一个 IDelegate 集合,因此可以对不同类类型的方法进行回调。

    【讨论】:

    • 看起来很有希望..你能发布使用情况吗?
    • 不幸的是,您只是将一个成员函数调用替换为另一个。
    • 正确。这称为外部多态性。您基本上可以将成员函数传递给对象,传递该对象并稍后在为其设置的类上调用它。非常适合活动!这就像 C# 中的委托。
    • 是的,但它并没有解决 OP 试图获取适合回调的原始指针的问题。
    【解决方案2】:

    您可以将该绑定参数推入全局变量并创建一个静态函数,该函数可以获取该值并在其上调用该函数,或者您必须动态生成每个实例的函数 - 这个将涉及某种动态代码生成,以在堆上生成一个存根函数,该函数将静态局部变量设置为您想要的值,然后在其上调用该函数。

    第一种方法简单易懂,但根本不是线程安全的或可重入的。第二个版本混乱且困难,但如果操作正确,它是线程安全和可重入的。

    编辑:我刚刚发现 ATL 使用代码生成技术来做到这一点 - 它们即时生成设置 this 指针和其他数据的 thunk,然后跳转到回调函数。 Here's a CodeProject article 解释了它是如何工作的,并且可能会让你知道如何自己做。尤其是最后一个示例(程序 77)。

    请注意,自从撰写本文以来,DEP 已经存在,您需要使用 VirtualAllocPAGE_EXECUTE_READWRITE 来获取一块内存,您可以在其中分配您的 thunk 并执行它们。

    【讨论】:

    • on-the-fly 代码生成绝对是太复杂了。寻找尽可能简单。
    • 第一种方法有点像我做的,看我的回复..这是你在说什么吗?
    • 我认为它会是,但我想我还是把它扔在那里。
    【解决方案3】:
    #include <iostream>
    typedef void(*callback_t)(int);
    
    template< typename Class, void (Class::*Method_Pointer)(void) >
    void wrapper( int class_pointer )
    {
       Class * const self = (Class*)(void*)class_pointer;
       (self->*Method_Pointer)();
    }
    
    class A
    {
    public:
       int m_i;
       void callback( )
       { std::cout << "callback: " << m_i << std::endl; }
    };
    
    int main()
    {
       A a = { 10 };
       callback_t cb = &wrapper<A,&A::callback>;
       cb( (int)(void*)&a);
    }
    

    【讨论】:

      【解决方案4】:

      我现在可以通过将 C 转换为单例,将 C::m 分解为 C::m_Impl 并声明静态 C::m(int) 来将其转发到单例实例。谈论一个黑客。

      【讨论】:

      • 如果您一次只使用一个回调,那么这将工作得很好。
      猜你喜欢
      • 1970-01-01
      • 2014-07-11
      • 2018-08-26
      • 2013-07-30
      • 2021-07-15
      • 2016-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多