如何生成自由函数

时间:2014-02-28 18:57:13

标签: c++ c templates callback wrapper

问题

我有一个类似C的API,我无法控制,具有注册/取消注册事件回调的功能:

enum Event { Evt1, Evt2, Evt3 }; // events generated by API library
typedef void(__cdecl *Callback)(Event e, void* context);
void API_add_callback(Event e, Callback cb, void* context);
void API_remove_callback(Event e, Callback cb, void* context);

我去创建一个包装器基类APIClient来封装这个API,如下所示:

class APIClient
{
  public:
    APIClient(){}

  protected:

    // this is to be used by subclasses
    void subscribe(const set<Event>& events)
    {
      _events = events;
      set<Event>::const_iterator it;
      for (it = _events.begin(); it != _events.end(); ++it)
      {
        API_add_callback(e, &callback, this);
      }
    }

    // this is to be used by subclasses
    void unsubscribe()
    {
      set<Event>::const_iterator it;
      for (it = _events.begin(); it != _events.end(); ++it)
      {
        API_remove_callback(e, &callback, this);
      }
    }

    // this is to be implemented by subclasses
    virtual void on_event(Event e) = 0;

  private:

    // this is the proxy callback that we register for all events
    static void __cdecl callback(Event e, void* context)
    {
      APIClient* instance = (APIClient*)context;

      // forward the event to the subclass
      instance->on_event(e);
    }

    set<Event> _events;
};

到目前为止,这么好,我想。然后我创建了两个子类FooBar APIClient s:

// This one is interested in Evt1 and Evt2 of the API...
class Foo : public APIClient
{
  public:

    Foo() : APIClient()
    {
      set<Event>s;
      s.insert(Evt1);
      s.insert(Evt2);
      subscribe(s);
    }

    ~Foo()
    {
      unsubscribe();
    }

  protected:

    virtual void on_event(Event e)
    {
      // here e will be Evt1 or Evt2, whenever they are fired
      // by the API
    }
};

// And this one is interested in Evt2 and Evt3 of the API...
class Bar : public APIClient
{
  public:

    Bar() : APIClient()
    {
      set<Event>s;
      s.insert(Evt2);
      s.insert(Evt3);
      subscribe(s);
    }

    ~Bar()
    {
      unsubscribe();
    }

  protected:

    virtual void on_event(Event e)
    {
      // here e will be Evt2 or Evt3, whenever they are fired
      // by the API
    }
};

麻烦的是,它不起作用...... 因为API背后的库确定了基于事件和回调的唯一订阅,而不是上下文(上下文只是附加的,可选的,用户数据)。所以,总的来说,事实证明,

之后
API_add_callback(Evt2, &callback, instance_of_Foo);
API_add_callback(Evt2, &callback, instance_of_Bar);

只有第二个订阅获胜,因此Foo永远不会听到Evt2

我失败的解决方案尝试

因为看起来API需要针对我认为的同一事件的每个新订阅的特定回调(即不同的地址):代码生成...模板! 在对APIClient进行模板化之后,&APIClient<Foo>::callback&APIClient<Bar>::callback之类的内容会给我不同的地址,对吧?错误!如果它们完全不同,它只会生成不同的地址(即不同的功能)。

所以

template<typename T>
class APIClient
{
    // ... other code ...

    static void __cdecl callback(Event e, void* context)
    {
      APIClient* instance = (APIClient*)context;

      // forward the event to the subclass
      instance->on_event(e);
    }
}

不好。但是以下内容会强制T=FooT=Bar的模板实例化,从而给我&APIClient<Foo>::callback != &APIClient<Bar>::callback

template<typename T>
class APIClient
{
    // ... other code ...

    static void __cdecl callback(Event e, void* context)
    {
      APIClient* instance = (APIClient*)context;

      // Use T here explicitely to force different template
      // instantiations of APIClient<T>::callback
      T::call_something();

      // forward the event to the subclass
      instance->on_event(e);
    }
}

这不好。看起来我试图超越编译器使其生成看似冗余的代码,我认为我注定要失败:)

问题(最后)

  • 是否有一种干净的方式(没有单身人士和所有人)来解决我原来的问题?
  • 有没有办法以模板的方式做到这一点,确保我为APIClient<T>::callback的每个实例化获得不同的函数而不做任何丑陋的事情?

注意:不幸的是,排除了C ++ 11。

1 个答案:

答案 0 :(得分:1)

这个东西叫做奇怪的重复模板模式(http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

template<typename T>
class APIClient
{
  public:
    APIClient(){}

  protected:

    // this is to be used by subclasses
    void subscribe(const set<Event>& events)
    {
      _events = events;
      set<Event>::const_iterator it;
      for (it = _events.begin(); it != _events.end(); ++it)
      {
         API_add_callback(it, &(T::callback), this);
      }
    }

    // this is to be used by subclasses
    void unsubscribe()
    {
      set<Event>::const_iterator it;
      for (it = _events.begin(); it != _events.end(); ++it)
      {
        API_remove_callback(it, &(T::callback), this);
      }
    }

  private:

    // this is the proxy callback that we register for all events
    static void __cdecl callback(Event e, void* context)
    {
       T * instance = (T*)context;

      // forward the event to the subclass
      instance->on_event(e);
    }

    set<Event> _events;
};

重要的变化是回调现在使用T * instance。这使访问成为一个问题。你有几个方法可以解决这个问题。您可以将on_event保留为虚拟,或者像我一样将其从APIClient中完全删除。子类仍然需要实现它,但它可以是公共的,或APIClient可以是子类的朋友。