【问题标题】:Callback in C++, template member?C++中的回调,模板成员?
【发布时间】:2009-08-16 10:53:19
【问题描述】:

以下代码不起作用,但它很好地表达了我想要做的事情。模板结构容器存在问题,我认为应该可以工作,因为它的大小对于任何模板参数都是已知的。

class callback {

  public:

  // constructs a callback to a method in the context of a given object
  template<class C>
  callback(C& object, void (C::*method)())
    : ptr.o(object), ptr.m(method) {}

  // calls the method
  void operator()() {
    (&ptr.o ->* ptr.m) ();
  }

  private:

  // container for the pointer to method
  template<class C>
  struct {
    C& o;
    void (C::*m)();
  } ptr;

};

有没有办法做这样的事情?我的意思是有一个非模板类回调,它包含任何指向方法的指针?

感谢 C++ 大师!

编辑:

请看这个:

Callback in C++, template member? (2)

【问题讨论】:

标签: c++ templates callback pointer-to-member


【解决方案1】:

这是一个完整的工作示例,可以完成我认为您正在尝试做的事情:

#include <iostream>
#include <memory>

// INTERNAL CLASSES

class CallbackSpecBase
{
  public:
    virtual ~CallbackSpecBase() {}
    virtual void operator()() const = 0;
};

template<class C>
class CallbackSpec : public CallbackSpecBase
{
  public:
    CallbackSpec(C& o, void (C::*m)()) : obj(o), method(m) {}
    void operator()() const { (&obj->*method)(); }

  private:
    C& obj;
    void (C::*method)();
};

// PUBLIC API

class Callback
{
  public:
    Callback() {}

    void operator()() { (*spec)(); }

    template<class C>
      void set(C& o, void (C::*m)()) { spec.reset(new CallbackSpec<C>(o, m)); }

  private:
    std::auto_ptr<CallbackSpecBase> spec;
};

// TEST CODE

class Test
{
  public:
    void foo() { std::cout << "Working" << std::endl; }
    void bar() { std::cout << "Like a charm" << std::endl; }
};

int main()
{
  Test t;
  Callback c;
  c.set(t, &Test::foo);
  c();
  c.set(t, &Test::bar);
  c();
}

【讨论】:

【解决方案2】:

我最近实现了这个:

#define UNKOWN_ITEM 0xFFFFFFFF

template <typename TArg>
class DelegateI
{
public:
    virtual void operator()(TArg& a)=0;
    virtual bool equals(DelegateI<TArg>* d)=0;
};


template <class TArg>
class Event
{
public:    
    Event()
    {
    }

    ~Event()
    {
    for (size_t x=0; x<m_vDelegates.size(); x++)
        delete m_vDelegates[x]; 
    }

    void operator()(TArg& a)
    {
        for (size_t x=0; x<m_vDelegates.size(); x++)
        {
            m_vDelegates[x]->operator()(a);
        }
    }

    void operator+=(DelegateI<TArg>* d)
    {
        if (findInfo(d) != UNKOWN_ITEM)
        {
            delete d;
            return;
        }

        m_vDelegates.push_back(d);
    }

    void operator-=(DelegateI<TArg>* d)
    {
        uint32 index = findInfo(d);

        delete d;

        if (index == UNKOWN_ITEM)
            return;

        m_vDelegates.erase(m_vDelegates.begin()+index);
    }

protected:
    int findInfo(DelegateI<TArg>* d)
    {
        for (size_t x=0; x<m_vDelegates.size(); x++)
        {
            if (m_vDelegates[x]->equals(d))
                return (int)x;
        }

        return UNKOWN_ITEM;
    }

private:
    std::vector<DelegateI<TArg>*> m_vDelegates;
};

template <class TObj, typename TArg>
class ObjDelegate : public DelegateI<TArg>
{
public:
    typedef void (TObj::*TFunct)(TArg&); 

    ObjDelegate(TObj* t, TFunct f)
    {
        m_pObj = t;
        m_pFunct = f;
    }

    virtual bool equals(DelegateI<TArg>* di)
    {
        ObjDelegate<TObj,TArg> *d = dynamic_cast<ObjDelegate<TObj,TArg>*>(di);

        if (!d)
            return false;

        return ((m_pObj == d->m_pObj) && (m_pFunct == d->m_pFunct));
    }

    virtual void operator()(TArg& a)
    {
        if (m_pObj && m_pFunct)
        {
            (*m_pObj.*m_pFunct)(a);
        }
    }

    TFunct m_pFunct;   // pointer to member function
    TObj* m_pObj;     // pointer to object
};

template <typename TArg>
class FunctDelegate : public DelegateI<TArg>
{
public:
    typedef void (*TFunct)(TArg&); 

    FunctDelegate(TFunct f)
    {
        m_pFunct = f;
    }

    virtual bool equals(DelegateI<TArg>* di)
    {
        FunctDelegate<TArg> *d = dynamic_cast<FunctDelegate<TArg>*>(di);

        if (!d)
            return false;

        return (m_pFunct == d->m_pFunct);
    }

    virtual void operator()(TArg& a)
    {
        if (m_pFunct)
        {
            (*m_pFunct)(a);
        }
    }

    TFunct m_pFunct;   // pointer to member function
};


template <typename TArg>
class ProxieDelegate : public DelegateI<TArg>
{
public:
    ProxieDelegate(Event<TArg>* e)
    {
        m_pEvent = e;
    }

    virtual bool equals(DelegateI<TArg>* di)
    {
        ProxieDelegate<TArg> *d = dynamic_cast<ProxieDelegate<TArg>*>(di);

        if (!d)
            return false;

        return (m_pEvent == d->m_pEvent);
    }

    virtual void operator()(TArg& a)
    {
        if (m_pEvent)
        {
            (*m_pEvent)(a);
        }
    }

    Event<TArg>* m_pEvent;   // pointer to member function
};


template <class TObj, class TArg>
DelegateI<TArg>* delegate(TObj* pObj, void (TObj::*NotifyMethod)(TArg&))
{
    return new ObjDelegate<TObj, TArg>(pObj, NotifyMethod);
}

template <class TArg>
DelegateI<TArg>* delegate(void (*NotifyMethod)(TArg&))
{
    return new FunctDelegate<TArg>(NotifyMethod);
}

template <class TArg>
DelegateI<TArg>* delegate(Event<TArg>* e)
{
    return new ProxieDelegate<TArg>(e);
}

像这样使用它:

定义:

Event<SomeClass> someEvent;

登记回调:

someEvent += delegate(&someFunction);
someEvent += delegate(classPtr, &class::classFunction);
someEvent += delegate(&someOtherEvent);

触发器:

someEvent(someClassObj);

您还可以创建自己的代表并覆盖他们所做的事情。我做了几个其他的,其中一个能够确保事件触发 gui 线程中的函数,而不是它被调用的线程。

【讨论】:

  • 它需要是一个模板,只允许可以处理该类型对象的委托注册。
  • 啊,我明白了,非常感谢。但是在后台有一些动态分配,new 在委托函数中。请在此处查看我的其他问题:stackoverflow.com/questions/1284239/…
  • 是的,我知道,这是我唯一不满意的地方,但它避免了复制对象,最终用户不需要知道它。如果你有更好的方法来做这件事没有副本,请告诉我。
  • 查看我的其他问题...也许有人知道更好的方法 :-) 再次感谢 Lodle 为您提供非常好的答案。
【解决方案3】:

你需要使用多态性。使用带有虚拟调用方法的抽象基类(operator(),如果你愿意的话),以及使用正确类型签名实现虚拟方法的模板化后代。

按照您现在的方式,保存该类型的数据是模板化的,但用于调用方法和传递对象的代码却不是。那是行不通的;模板类型参数需要流经构造和调用。

【讨论】:

    【解决方案4】:

    @巴里·凯利

    #include <iostream>
    
    class callback {
      public:
      virtual void operator()() {};
    };
    
    template<class C>
    class callback_specialization : public callback {
      public:
      callback_specialization(C& object, void (C::*method)())
        : o(object), m(method) {}
    
      void operator()() {
        (&o ->* m) ();
      }
    
      private:
      C& o;
      void (C::*m)();
    
    };
    
    class X {
      public:
      void y() { std::cout << "ok\n"; }
    };
    
    int main() {
      X x;
      callback c(callback_specialization<X>(x, &X::y));
      c();
      return 0;
    }
    

    我试过了,但它不起作用(打印“ok”)...为什么?

    编辑: 正如 Neil Butterworth 提到的,多态性通过指针和引用起作用,

      X x;
      callback& c = callback_specialization<X>(x, &X::y);
      c();
    

    编辑: 使用此代码,我得到一个错误:

    invalid initialization of non-const reference of type ‘callback&’
    from a temporary of type ‘callback_specialization<X>’
    

    现在,我不明白那个错误,但是如果我将 callback& c 替换为 const callback& cvirtual void operator()()virtual void operator()() const,它可以工作。

    【讨论】:

    • 您实例化一个回调对象,然后调用它的 operator() 方法,该方法被定义为什么都不做。为什么它应该做任何事情?
    • 是虚拟的,不应该调用callback_specialization版本吗?至少那是我想做的……
    • 它被称为切片——你用一个更大的派生对象初始化一个“回调”,所以你会丢失额外的位。您可以尝试从您的专业中初始化一个回调&。
    • 不,不应该——对象 c 是回调类型,所以你会得到回调 operator()。虚方法只能通过指针或引用起作用。
    【解决方案5】:

    你没有说你发现了什么错误,但我发现这行得通:

    template<typename C>
    class callback {
    
      public:
    
      // constructs a callback to a method in the context of a given object
      callback(C& object, void (C::*method)())
        : ptr(object,method) {}
    
      // calls the method
      void operator()() {
        (&ptr.o ->* ptr.m) ();
      }
    
      private:
    
      // container for the pointer to method
      // template<class C>
      struct Ptr{
      Ptr(C& object, void (C::*method)()): o(object), m(method) {}
        C& o;
        void (C::*m)();
      } ptr;
    
    };
    

    请注意,Ptr 需要一个构造函数,因为它有一个引用成员。

    您可以不使用 struct Ptr 并拥有原始成员。

    用 VS2008 express 测试。

    【讨论】:

    • 这种类型不会是多态的,我认为这在很大程度上是练习的重点。现在,如果你添加了一个带有虚拟operator() 的基类,那么你会做得很好。
    • 是的,但现在回调是一个模板......有没有办法做到这一点,而不回调是一个模板?
    • 还必须考虑对象切片等。最好通过指针存储数据。
    【解决方案6】:

    改进 OP 的答案:

    int main() {
      X x;
      callback_specialization<X> c(x, &X::y);
      callback& ref(c);
      c();
      return 0;
    }
    

    这会打印“ok”。

    在 VS2008 express 上测试。

    【讨论】:

      【解决方案7】:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-23
        • 1970-01-01
        • 1970-01-01
        • 2021-04-21
        • 1970-01-01
        相关资源
        最近更新 更多