Есть ли лучший способ выразить вложенные пространства имен в C++ в заголовке
я переключился с C++ на Java и C# и думаю, что использование пространств имен/пакетов там намного лучше (хорошо структурировано). Затем я вернулся к C++ и попытался использовать пространства имен таким же образом, но требуемый синтаксис ужасен в заголовочном файле.
namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
мне тоже кажется странным следующее (Чтобы избежать глубокого отступа):
namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
{
есть ли более короткий способ выразить вышеизложенное? Мне чего-то не хватает как
namespace MyCompany::MyModule::MyModulePart::...
{
public class MyClass
обновление
хорошо, некоторые говорят, что концепция использования в Java / C# и C++ отличается. Правда? Я думаю, что (динамическая) загрузка классов не является единственной целью для пространств имен (это очень техническая аргументированная перспектива). Почему бы мне не использовать его для читаемости и структурирования, например, подумайте о "IntelliSense".
в настоящее время нет логики / клея между пространством имен и тем, что вы можете там найти. Java и C# делают это намного лучше... Почему в том числе <iostream> и имея пространство имен std?
Хорошо, если вы говорите, что логика должна полагаться на заголовок для включения, почему #include не использует дружественный синтаксис "IntelliSense", такой как #include <std::io::stream> или <std/io/stream>? Я думаю, что отсутствие структурирования в библиотеках по умолчанию является одной из слабостей C++ по сравнению с Java / C#.
если уникальность для avid конфликтов-это одна точка (которая также является точкой C# и Java), хорошей идеей является использование имени проекта или названия компании в качестве пространства имен, не так ли так думаешь?
С одной стороны, говорят, что C++ является наиболее гибким... но все говорили "Не делай этого"?
Мне кажется C++ может сделать много вещей, но имеет ужасный синтаксис даже для самых простых вещей, во многих случаях по сравнению с C#.
обновление 2
большинство пользователей говорят, что это нонсенс, чтобы создать более глубокую вложенность, чем два уровня. Итак, как насчет Windows::UI::Xaml и Windows::UI::Xaml::элементы управления:: пространства имен примитивов в разработке Win8? Я думаю Использование Microsoft пространств имен имеет смысл, и это действительно глубже, чем просто 2 Уровня.
Я думаю, что большие библиотеки / проекты нуждаются в более глубоком вложении (я ненавижу имена классов, такие как ExtraLongClassNameBecauseEveryThingisinthesamenamespace... тогда Вы тоже можете поместить все в глобальное пространство имен.)
Обновление 3 - Вывод
большинство говорят "Не делай этого", но... даже boost имеет более глубокую вложенность, чем один или два уровня. Да, это библиотека, но: если вы хотите многоразовый код-рассматривайте свой собственный код как библиотеку, которую вы бы дали кому-то другому. Я также использую более глубокую вложенность для целей обнаружения, используя пространства имен.
9 ответов:
C++17 может упростить определение вложенного пространства имен:
namespace A::B::C { }эквивалентно
namespace A { namespace B { namespace C { } } }см. (8) на странице пространства имен в cppreference:
http://en.cppreference.com/w/cpp/language/namespace
чтобы избежать действительно глубокого отступа, я обычно делаю это так:
namespace A { namespace B { namespace C { class X { // ... }; }}}
пространства имен C++ используются для группировки интерфейсов, а не для разделения компонентов или выражения политического разделения.
стандарт выходит из своего пути, чтобы запретить Java-подобное использование пространств имен. Например, псевдонимы пространства имен обеспечить способ легко использовать глубоко вложенные или длинные имена пространств имен.
namespace a { namespace b { namespace c {} } } namespace nsc = a::b::c;но
namespace nsc {}тогда будет ошибкой, потому что пространство имен может быть определено только с помощью его original-namespace-name. По существу стандарт делает вещи легко для пользователей из такой библиотеки, но трудно для разработчик. Это отпугивает людей от написания таких вещей, но смягчает последствия, если они это делают.вы должны иметь одно пространство имен для каждого интерфейса определяется набор связанных классов и функций. Внутренние или необязательные подинтерфейсы могут входить во вложенные пространства имен. Но более чем на два уровня глубже должен быть очень серьезный красный флаг.
рассмотрите возможность использования символов подчеркивания и префиксы идентификаторов, где
::оператора не требуется.
полностью поддерживаю peterchen это но хочу добавить что-то, что касается другой части вашего вопроса.
объявление пространств имен является одним из очень редких случаев в C++, где мне действительно нравится использование
#defines.#define MY_COMPANY_BEGIN namespace MyCompany { // begin of the MyCompany namespace #define MY_COMPANY_END } // end of the MyCompany namespace #define MY_LIBRARY_BEGIN namespace MyLibrary { // begin of the MyLibrary namespace #define MY_LIBRARY_END } // end of the MyLibrary namespaceЭто также устраняет необходимость в комментариях рядом с закрывающей скобкой пространства имен (вы когда-нибудь прокручивали вниз до нижней части большого исходного файла и пытались добавить/удалить / сбалансировать фигурные скобки, в которых отсутствовали комментарии какая скобка закрывает какую область? Не весело.).
MY_COMPANY_BEGIN MY_LIBRARY_BEGIN class X { }; class Y { }; MY_LIBRARY_END MY_COMPANY_ENDесли вы хотите поместить все объявления пространства имен в одну строку, вы можете сделать это также с помощью немного (довольно уродливой) магии препроцессора:
// helper macros for variadic macro overloading #define VA_HELPER_EXPAND(_X) _X // workaround for Visual Studio #define VA_COUNT_HELPER(_1, _2, _3, _4, _5, _6, _Count, ...) _Count #define VA_COUNT(...) VA_HELPER_EXPAND(VA_COUNT_HELPER(__VA_ARGS__, 6, 5, 4, 3, 2, 1)) #define VA_SELECT_CAT(_Name, _Count, ...) VA_HELPER_EXPAND(_Name##_Count(__VA_ARGS__)) #define VA_SELECT_HELPER(_Name, _Count, ...) VA_SELECT_CAT(_Name, _Count, __VA_ARGS__) #define VA_SELECT(_Name, ...) VA_SELECT_HELPER(_Name, VA_COUNT(__VA_ARGS__), __VA_ARGS__) // overloads for NAMESPACE_BEGIN #define NAMESPACE_BEGIN_HELPER1(_Ns1) namespace _Ns1 { #define NAMESPACE_BEGIN_HELPER2(_Ns1, _Ns2) namespace _Ns1 { NAMESPACE_BEGIN_HELPER1(_Ns2) #define NAMESPACE_BEGIN_HELPER3(_Ns1, _Ns2, _Ns3) namespace _Ns1 { NAMESPACE_BEGIN_HELPER2(_Ns2, _Ns3) // overloads for NAMESPACE_END #define NAMESPACE_END_HELPER1(_Ns1) } #define NAMESPACE_END_HELPER2(_Ns1, _Ns2) } NAMESPACE_END_HELPER1(_Ns2) #define NAMESPACE_END_HELPER3(_Ns1, _Ns2, _Ns3) } NAMESPACE_END_HELPER2(_Ns2, _Ns3) // final macros #define NAMESPACE_BEGIN(_Namespace, ...) VA_SELECT(NAMESPACE_BEGIN_HELPER, _Namespace, __VA_ARGS__) #define NAMESPACE_END(_Namespace, ...) VA_SELECT(NAMESPACE_END_HELPER, _Namespace, __VA_ARGS__)теперь вы можете сделать это:
NAMESPACE_BEGIN(Foo, Bar, Baz) class X { }; NAMESPACE_END(Baz, Bar, Foo) // order doesn't matter, NAMESPACE_END(a, b, c) would work equally well Foo::Bar::Baz::X x;для вложенности глубже трех уровней вам придется добавить вспомогательные макросы до нужного количества.
нет, и, пожалуйста, не делай этого.
целью пространств имен является в первую очередь разрешение конфликтов в глобальном пространстве имен.
вторичной целью является локальная аббревиатура символов; например, комплекс
UpdateUIметод может использовать элементusing namespace WndUIдля использования более коротких символов.Я работаю над проектом 1.3 MLoc, и единственными пространствами имен, которые у нас есть, являются:
- импортированные внешние библиотеки COM (в основном для изоляции конфликтов заголовков между
#importи#include windows.h)- один уровень пространств имен "public API" для определенных аспектов (UI, доступ к БД и т. д.)
- пространства имен"детали реализации", которые не являются частью общедоступного API (анонимные пространства имен .cpp, или
ModuleDetailHereBeTygersпространства имен в только заголовочные библиотеки)- перечисления являются самой большой проблемой в моем опыте. Они загрязняют как сумасшедшие.
- я все еще чувствую, что это слишком много пространств имен
в этом проекте, имена классов и т. д. использовать двух-или трехбуквенный код региона (например
CDBNodeвместоDB::CNode). Если вы предпочитаете последнее, есть место для второго уровня "публичных" пространств имен, но не более того.класс-конкретных перечислений и т. д. могут быть членами этих классов (хотя я согласен, что это не всегда хорошо, и иногда трудно сказать, следует ли)
редко требуется пространство имен "компания", за исключением случаев, когда у вас возникают большие проблемы с сторонними библиотеками, которые распространяются как binary, не предоставляют свое собственное пространство имен и не могут быть легко помещены в него (например, в двоичном распределении). Тем не менее, по моему опыту заставляя их в пространство имен гораздо проще сделать.
[edit] в соответствии с последующим вопросом Стеги:
ок, так что насчет Windows::UI::Xaml и Windows::UI::Xaml::элементы управления:: примитивы пространства имен в разработке Win8? Я думаю, что использование Microsoft пространств имен имеет смысл и это действительно глубже, чем просто 2 уровнях
Извините, если я не был достаточно ясен: два уровня-это не жесткий предел, и больше не является по своей сути плохим. Я просто хотел отметить, что вы редко нужно более двух, по моему опыту, даже на большой кодовой базе. Гнездование глубже или более мелкое-это компромисс.
теперь случай Microsoft, возможно, отличается. Предположительно гораздо большая команда, и все код библиотеки.
Я бы предположил, что Microsoft имитирует здесь успех библиотеки .NET, где пространства имен способствуют поиска из обширной библиотеки. (.Сеть около 18000 типы.)
Я бы также предположил, что существует оптимальный (порядок величины) символов в пространстве имен. скажем, 1 не имеет смысла, 100 звучит правильно, 10000 явно много.
TL; DR: это компромисс, и мы не есть жесткие цифры. Будьте осторожны, не переусердствуйте в любом направлении. "Не делай этого "происходит просто от" У вас есть проблемы с этим, у меня были бы проблемы с этим, и я не вижу причины, почему вам это нужно.".
цитата Lzz (Lazy C++) docs:
Lzz распознает следующие конструкции C++:
определение пространства имен
Безымянное пространство имен и все вложенные объявления выводятся в исходный файл. Это правило переопределяет все остальные.
имя именованного пространства имен может быть уточнено.
namespace A::B { typedef int I; }эквивалентно:
namespace A { namespace B { typedef int I; } }конечно качество источников, которое зависит от таких инструментов, является спорным... Я бы сказал, что это скорее любопытство, показывая, что синтаксическая болезнь, вызванная C++, может принимать много форм (у меня тоже есть моя...)
оба стандарта (C++2003 и C++11) очень явно указывают, что имя пространства имен является идентификатором. Это означает, что требуются явные вложенные заголовки.
мое впечатление, что это не так уж и важно, чтобы разрешить размещение квалифицированного идентификатора помимо простого имени пространства имен, но по какой-то причине это не допускается.
да, вам придется сделать это как
namespace A{ namespace B{ namespace C{} } }однако вы пытаетесь использовать пространства имен так, как они не должны использоваться. Проверьте этой вопрос, может быть, вы найдете ее полезной.
вы можете использовать следующий синтаксис:
namespace MyCompany { namespace MyModule { namespace MyModulePart //e.g. Input { namespace MySubModulePart { namespace ... { class MyClass; } } } } } // Here is where the magic happens class MyCompany::MyModule::MyModulePart::MySubModulePart::MyYouGetTheIdeaModule::MyClass { ... };обратите внимание, что этот синтаксис действителен даже в C++98 и почти аналогичен тому, что теперь доступно в C++17 с определения вложенных пространств имен.
счастливая неинтересно!
источники:
Comments