我想在库的 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()
(因此在没有命名空间限定符的情况下使用它)。如果我将 v2
和 v3
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/