首页 > 解决方案 > C++ API 设计:使用 void* 是个坏主意吗?

问题描述

我正在开发一个我希望在调用方尽可能通用的 API。主要设计思想是提供一种信号/插槽类型的实现,允许 API 的用户订阅给定的一组事件,并将用户定义的回调附加到它们。

公共接口看起来像这样:
RetCallback subscribe(EventEnum& ev, std::function<void(void*)> fn) const; : 注意void(void*)这里的签名。EventEnum在公共头文件中给出,以及类型定义。

然后,API 的内部工作将通过 notify 方法通知其订阅的观察者事件,并提供数据以转发给客户端:

void dummyHeavyOperation() const {
    std::this_thread::sleep_for(2s);
    std::string data = "I am working very hard";
    notify(EventEnum::FooEvent, &data);
}

客户端订阅并将数据转换为(记录的)类型,如下所示:

auto subscriber = Controller->subscribe(EventEnum::FooEvent, callback);

在哪里

void callback(void* data) {
    auto* myData = (std::string*) data;
    std::cout << "callback() with data=" << *myData << std::endl;

    /// Do things
}

这是一个合理的设计还是不受欢迎?您经验丰富的现代 C++ 开发人员的头脑告诉您什么?

[编辑]
我还应该补充一点,API 是作为在运行时加载的共享库提供的。因此,任何编译时耦合(以及代码生成,除非我弄错了)都不在讨论范围内

谢谢!

标签: c++c++11api-design

解决方案


C++ API 设计:使用 void* 是个坏主意吗?

是的。

要实现等效的 API,您应该使用std::any,这是类型擦除的检查版本。std::any自当前的 C++17 标准版本以来,仅在标准库中可用。如果您没有 C++17(或不希望 API 的用户依赖 C++17),那么您可以使用非标准实现。

另一种方法是根本不删除参数类型,而是一直使用模板。有关此类回调 API 的示例,请参见Boost Signals


推荐阅读