c++ - 使用内联命名空间进行 API 版本控制

标签 c++ api-design

我想在库的 API ( seastar ) 中引入重大更改,而不影响用户。因此,我想为客户提供一种按照自己的节奏迁移到新 API 的方法。为此,我想使用内联 namespace 。基本思想很简单,您为旧版本引入一个命名空间 v1,为新版本引入一个内联命名空间 v2(或者反之亦然)。 https://foonathan.net/2018/11/inline-namespaces/ 中对此进行了很好的描述。 。当您想要引入另一个重大更改(命名空间 v3)时,麻烦就开始了。让我们看一些示例代码作为进一步讨论的基础:

namespace v1 {
    int foo(); // old version of foo
}

inline namespace v2 {
    std::string foo(); // new, incompatible version of foo
    int bar(); // old version of bar
}

namespace v3 {
    std::string bar(); // new, incompatible version of bar
}

现在如果我想将默认的 API 版本更新为 v3,即默认使用 v3 版本的 bar(),我可以将 namespace v3 内联。我们已经陷入了困境:如果我 v3 内联,我就会破坏已经迁移到使用最新的 v2 的客户 版本的 foo() (因此在没有命名空间限定符的情况下使用它)。如果我将 v2v3 inline 都设为内联,以便可以在全局(库)命名空间中访问所有函数的最新版本,我会介绍v3::bar()v2:bar() 之间存在歧义。如果我将 v2::foo() 移动到 v3,我会破坏刚刚开始迁移到 v2::foo() 并正在使用的客户端它具有完全限定名称(::v2::foo())。另一种选择是在与最新 API 版本相对应的命名空间中重新声明所有最新版本的函数,并仅将其内联。这是大量的重复和一些额外的生成代码。有更优雅的解决方案吗?

我还建议在内联命名空间v3使用v2::bar(等等)将所有符号的最新版本导出到最新的内联命名空间。然而,据我所知,这违反了 ADL。

最佳答案

我最终找到了一个解决方案,它并不像我想要的那样漂亮和优雅,但它很简单并且有效。 我的解决方案是为每个重大更改引入两个新的 api 版本。就问题中的示例而言,我执行了以下操作:

namespace v1 {
    int foo(); // old version of foo
}

inline namespace v2 {
    std::string foo(); // new, incompatible version of foo

namespace v3 {
    int bar(); // old version of bar
}

inline namespace v4 {
    std::string bar(); // new, incompatible version of bar
}

奇数版本代表已更改符号的弃用版本,而偶数版本代表其新版本。这个系统是可扩展的、简单且健壮的,但是它有点不直观并且绝对不优雅。 我有预处理器宏,允许客户端选择默认的“api 版本”。例如,如果客户端尚未准备好使用最新的 API,则可以选择 API 版本 3,之后命名空间 v2 和 v3 将内联。在迁移到最新的 API 后,他们可以将 API 版本提升到 4,这将导致上面看到的状态。

关于c++ - 使用内联命名空间进行 API 版本控制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61339880/

相关文章:

java - 为什么 Spinner 类不提供泛型?

spring-boot - 用于搜索 API 的 Spring Boot REST 设计

c++ - 在 ROS 上下文中使用基于 CUDA 的函数的正确方法

C++ 编译器在使用 Bool 包装器类时报告不明确的函数调用

c++ - 为什么我们不能简单地复制 std::function

C++ 重载 [] 与转换

c++ - 非阻塞 Websockets c++ 奇怪的延迟

c++ - 使用设置函数输入变量的方法将函数更改为类

rest - 当端点被 feature-flag/feature-toggle 禁用时,您使用什么 HTTP 状态代码?

java - 根据传递的对象类型进行多次操作