我想在不破坏用户的情况下,在库的API (西斯塔)中引入一个突破性的更改。因此,我希望为客户端提供一种以自己的速度迁移到新API的方法。为此,我想使用内联名称空间。基本思想非常简单,您可以为旧版本引入一个namespace v1,为新版本引入一个inline namespace v2 (或者相反)。在https://foonathan.net/2018/11/inline-namespaces/中很好地描述了这一点。当您想要引入另一个突破性的更改,即namespace 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 inline。我们陷入了两难境地:如果我只使用v3 inline,我就会中断已经迁移到使用最新的v2版本的foo()的客户机(因此使用它时没有名称空间限定符)。如果我同时创建了v2和v3 inline,以便所有函数的最新版本都可以在全局(库)命名空间中访问,那么在v3::bar()和v2:bar()之间就会出现歧义。如果我将v2::foo()移动到v3,就会破坏刚刚开始迁移到v2::foo()并使用完全限定名(::v2::foo())的客户端。另一个选项是重新声明名称空间中与最新API版本相对应的所有最新版本的函数,并且只声明该inline。这是大量的重复和一些额外的生成代码。有没有更优雅的解决方案?
有人还建议我只将所有符号的最新版本导出到using v2::bar (等等)到最新的内联命名空间中。然而,据我所知,这破坏了ADL。
发布于 2020-04-23 16:29:48
最后,我解决了一个不像我希望的那样好和优雅的解决方案,但它很简单,而且很有效。我的解决方案是为每个重大更改引入两个新的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,则可以选择after 3,在此之后,名称空间v2和v3将是内联的。在迁移到最新的API之后,它们可以将API版本提高到4,这将导致上述状态。
https://stackoverflow.com/questions/61339880
复制相似问题