代码之家  ›  专栏  ›  技术社区  ›  Dustin Getz sunsations

来自绑定方法的原始函数指针

  •  3
  • Dustin Getz sunsations  · 技术社区  · 16 年前

    我需要将一个方法绑定到一个函数回调中,但如中所述,此代码段是不合法的。 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;
    }
    
    4 回复  |  直到 15 年前
        1
  •  2
  •   Chap    16 年前

    您可以让自己的类执行与boost bind函数相同的操作。类所要做的就是接受函数类型和指向包含函数的对象的指针。例如,这是一个void返回和void参数委托:

    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<C> 是否是一个类型,拥有这些类型的集合可能不实用,因为如果列表也包含类B的函数呢?它不能。

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

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

    如果 VoidDelegate<T> 实现IDelegate可以有一个IDelegate集合,因此可以回调不同类类型的方法。

        2
  •  2
  •   Salman A    16 年前

    您可以将绑定的参数推送到全局变量中,并创建一个静态函数来获取该值并对其调用该函数,或者您必须动态生成每个实例的函数-这将涉及某种动态代码生成,以在具有静态局部变量集的堆上生成一个存根函数到所需的值,然后对其调用函数。

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

    编辑:我刚发现ATL使用代码生成技术来实现这一点——它们会动态生成thunk,从而设置 this 指针和其他数据,然后跳转到回调函数。 Here's a CodeProject article 这就解释了它是如何工作的,并可能给你一个如何自己去做的想法。特别是最后一个样本(程序77)。

    请注意,由于文章是写的,DEP已经存在,您需要使用 VirtualAlloc 具有 PAGE_EXECUTE_READWRITE 为了得到一块内存,您可以在其中分配您的thunk并执行它们。

        3
  •  1
  •   deft_code    15 年前
    #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
  •  0
  •   Dustin Getz sunsations    16 年前

    我现在让它工作起来,将C转换成一个单例,将C::M分解成C::M impl,然后声明静态C::M(int),它将转发到单例实例。说说黑客。