问题

我有一个无法控制的类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:
// 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。

    最佳答案

    这称为奇怪的重复模板模式(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中完全删除。子类仍然需要实现它,但是它可以是公共(public)的,也可以是APIClient可以是子类的 friend 。

    08-17 00:27