Знаешь что? Я думаю, что у меня может быть просто жизнеспособное решение для этого. Это на самом деле очень просто, и это очень близко к первоначальному предложению OP (у которого действительно была проблема потенциального дублирующегося определения, если вы хотели открыть пространство имен дважды в одной и той же единице перевода). Вам просто нужно немного подумать в сторону, и не стоит слишком ценить, чтобы увидеть, что ваши пространства имен заключены в скобки вместо фигурных скобок.
Итак, позвольте мне изложить это здесь, потому что в этом нет ничего особенного, а затем я объясню, почему мне лично это нравится.
Код:
Макросы:
#define DECLARE_NAMESPACE(ns) \
namespace ns {\
static constexpr const char *_curNamespace = #ns; \
}
#define BEGIN_NAMESPACE(ns) \
namespace ns { \
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
#define END_NAMESPACE }
Пример кода:
#include <iostream>
DECLARE_NAMESPACE (Foo)
BEGIN_NAMESPACE (Foo)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
BEGIN_NAMESPACE (Foo)
void another_print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
DECLARE_NAMESPACE (BarBar)
BEGIN_NAMESPACE (BarBar)
void print_namespace_name () { std::cout << _curNamespace << "\n"; }
END_NAMESPACE
END_NAMESPACE
int main ()
{
Foo::print_namespace_name ();
Foo::another_print_namespace_name ();
Bar::print_namespace_name ();
Bar::BarBar::print_namespace_name ();
}
Выход:
Foo
Foo
Bar
BarBar
Теперь это, очевидно, очень просто реализовать, а также легко использовать и не имеет очевидных ограничений. В частности, он может обрабатывать вложенные пространства имен (как показано в коде выше), и открытие пространства имен дважды в одном и том же модуле компиляции также работает (опять же, это показано во фрагменте кода).
Но, но, но, разве мы не должны дважды вводить имя пространства имен, и разве это не то самое, чего мы пытались избежать, чтобы устранить опечатки?
Ну, конечно, мы должны дважды ввести имя, но что с того, жить с ним. Дело в том, что с этим конкретным набором макросов компилятор теперь будет ловить любые опечатки для нас. Давайте докажем это, сознательно вставив один. Так вот:
DECLARE_NAMESPACE Whoops
BEGIN_NAMESPACE whoops
END_NAMESPACE
Создает это (я не мог найти лучший способ сформулировать static_assert
, извините):
prog.cc:12:24: error: '_curNamespace' is not a member of 'whoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:27:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (whoops)
^~~~~~~~~~~~~~~
И что еще важнее (вот почему нам нужен макрос BEGIN_NAMESPACE
):
DECLARE_NAMESPACE (Bar)
BEGIN_NAMESPACE (Bar)
DECLARE_NAMESPACE (BarWhoops)
BEGIN_NAMESPACE (Barwhoops)
END_NAMESPACE
END_NAMESPACE
Создает это:
prog.cc:12:24: error: '_curNamespace' is not a member of 'Bar::Barwhoops'
static_assert (ns::_curNamespace, "BEGIN_NAMESPACE: namespace has not been declared");
^~~~~~~~~~~~~
prog.cc:42:5: note: in expansion of macro 'BEGIN_NAMESPACE'
BEGIN_NAMESPACE (Barwhoops)
^~~~~~~~~~~~~~~
Какой просто денди.
Итак, вы знаете, что не нравится?
Демонстрационная версия - раскомментируйте строку 3, чтобы увидеть ошибки компилятора.