Версии API со встроенными пространствами имен - PullRequest
1 голос
/ 21 апреля 2020

Я хочу внести критическое изменение в API библиотеки ( seastar ), не нарушая пользователей. Поэтому я хочу предоставить клиентам возможность перехода на новый API в своем собственном темпе. Для этого я хотел бы использовать встроенные пространства имен. Идея базового c достаточно проста: вы вводите 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, то есть сделать версию bar() v3 доступной по умолчанию, я можно сделать 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 (и так далее) внутри inline namespace v3, чтобы экспортировать последнюю версию всех символов в последнее встроенное пространство имен. Это, однако, нарушает ADL, насколько я знаю.

1 Ответ

1 голос
/ 23 апреля 2020

В итоге я остановился на решении, которое не так красиво и элегантно, как хотелось бы, но просто и работает. Мое решение состоит в том, чтобы представить две новые версии 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, что приведет к состоянию, указанному выше.

...