我正在开发一个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
}
};
这种方法存在两个问题:
如果我提供回调设置器方法而不是覆盖,则可以解决这两个问题:
// 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中控制它,但订阅理论上可能会失败并返回一些错误代码。因此可能出现以下情况:
这种情况很少见(事实上我不确定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 ++实现这一点,我怀疑它根本不可能完成。
所以,我的问题是如何才能以最佳方式完成?我的一些方法是否可以做得更好,或者是否有一些标准的解决方案如何为这样的问题设计接口?
谢谢!