C ++类

时间:2016-06-03 14:18:13

标签: c++ oop c++11 interface frameworks

我正在开发一个C ++框架,其中一部分是包装一个低级C API。此API允许为不同的“服务”注册回调,每个回调都有自己的签名。每个回调也都有优先权。

我的任务是将这个低级API包装到初级开发人员方便和可用的东西中,因此它必须非常容易使用并且很难犯错误。

API是这样的:

int on_msg(int size, void* data) {
    MsgData* msg = (MsgData*)data;
    // processing of msg->header, msg->body, etc.
}

register_service(ON_MSG, HIGH_PRIORITY, on_msg);

服务也可以取消注册,但在实际问题的应用程序中,这绝不是必需的。

在框架中有很多可能的方法来包装这个界面,但我的想法都不是很完美。我在下面提供了我的想法,我只想看看是否有人可以提出更优雅和方便的东西。

在我正在开发的框架中,有一个类应用程序,其中包含一组旨在被用户覆盖的虚拟方法。目前的方法是允许他覆盖上面的on_msg等所有方法。我还提供了set_priority方法,必须在注册服务之前调用它。在伪代码中它看起来像这样:

class Application {
protected:

    Application() {
        initialize();
        register_service(ON_MSG, get_priority(ON_MSG), wrap(&Application::on_msg));
        register_service(ON_KEYBOARD, get_priority(ON_KEYBOARD), wrap(&Application::on_keyboard));
        register_service(ON_IDLE, get_priority(ON_IDLE), wrap(on_idle));
        register_service(ON_CONNECTED, get_priority(ON_CONNECTED), wrap(on_connected);
        // ...
    }

    // Callbacks
    virtual void on_msg(const string& header, const string& body) {}
    virtual void on_keyboard(int key) {}
    virtual void on_idle() {}
    virtual void on_connected() {}
    // ...

    virtual void initialize() = 0;

    void set_priority(Service service, Priority priority) {
        // remembers priority in internal map
        // or throws exception if callback is already registered
    }

    Priority get_priority(Service service) {
        // returns priority which was set before
        // or default priority
    }
};


class UserApplication : public Application {
protected:
    void initialize() override {
        set_priority(ON_IDLE, Low);
    }

private:
    void on_idle() override (
        // something to do
    }
};

这种方法存在两个问题:

  1. 我必须注册所有服务,因为无法知道哪些方法被用户覆盖。虽然从性能的角度来看并不重要,但这很浪费。
  2. 用户只允许在intialize()方法中设置优先级,但这在界面上并不明显。在用户的应用程序被未处理的异常终止之前,她甚至不会怀疑她在initialized()之外调用set_priority()是行不通的。
  3. 如果我提供回调设置器方法而不是覆盖,则可以解决这两个问题:

    // In class Application:
    using OnMsgHandler = std::function<void(const std::string&, const std::string&)>;
    set_on_msg(Priority, const OnMsgHandler&);
    
    // In client's code:
    set_on_msg(HighPriority,
        [this](const auto& header, const auto& body) {
             on_msg(header, body);
        });
    

    这种方法可行,但客户端的代码现在看起来更复杂。覆盖基类方法似乎更容易,签名更明显,不需要lambdas / bindings /类似的东西。当然,这对于经验丰富的C ++开发人员来说不是问题,但是框架必须由非常缺乏经验的人使用,因此我不会强迫客户使用任何“高级”功能。如果我没有看到更好的解决方案,我会使用这种方法,但我觉得这可以做得更好。

    还有另一种选择:使用第一种方法,但允许用户随时更改优先级。不幸的是,低级API没有任何改变优先级的方法,因此我必须取消订阅然后再次订阅。竞争条件在这里不是问题,因为我有办法在这个API中控制它,但订阅理论上可能会失败并返回一些错误代码。因此可能出现以下情况:

    1. 用户处于订阅状态且优先级较低。
    2. 用户调用set_priority(ON_MSG,HIGH,err);
    3. 错误现在包含错误“由于某些系统故障而无法订阅”。用户仍处于取消订阅状态。
    4. 这种情况很少见(事实上我不确定register_service是否真的会失败 - 我在实践中从未见过它,虽然API允许它表现得那样),但我仍然想避免id。

      理想情况下,我想允许用户以某种方式覆盖方法:

      class UserApplication : public Application {
      
          virtual void on_keyboard(int key, Priority::High) {
              // Priority::High is an empty type here
          }
      
          // or:
      
          template<>
          virtual void on_keyboard<Priority::High>(int key)
          { /* ... */ }
      
          // or:
      
          [[priority(High)]]
          virtual void on_keyboard(int key)
          { /* ... */ }
      };
      

      这样我就可以在编译时知道所需的优先级,并禁止任何人更改它。不幸的是,我不知道如何在不依赖丑陋的宏的情况下用C ++实现这一点,我怀疑它根本不可能完成。

      所以,我的问题是如何才能以最佳方式完成?我的一些方法是否可以做得更好,或者是否有一些标准的解决方案如何为这样的问题设计接口?

      谢谢!

0 个答案:

没有答案