Подтвердить что ты не робот

Есть ли лучший способ выразить вложенные пространства имен в С++ внутри заголовка

Я переключился с С++ на Java и С# и считаю, что использование пространств имен/пакетов намного лучше (хорошо структурировано). Затем я вернулся на С++ и попытался использовать пространства имен одинаково, но требуемый синтаксис ужасен внутри файла заголовка.

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/С# и С++ отличается. В самом деле? Я думаю, что (динамическая) загрузка классов не является единственной целью для пространств имен (это очень технически обоснованная перспектива). Почему бы мне не использовать его для удобочитаемости и структуризации, например, подумайте о "IntelliSense".

В настоящее время нет никакой логики/клея между пространством имен и тем, что вы можете найти там. Java и С# делают это намного лучше... Зачем включать <iostream> и иметь пространство имен std? Хорошо, если вы говорите, что логика должна полагаться на заголовок для включения, почему #include не использует дружественный синтаксис "IntelliSense", например #include <std::io::stream> или <std/io/stream>? Я думаю, что недостающее структурирование в libs по умолчанию - это одна слабость С++ по сравнению с Java/С#.

Если уникальность для авидных конфликтов - это одна точка (которая также является точкой С# и Java), хорошая идея - использовать имя проекта или название компании в качестве пространства имен, не так ли считаете?

С одной стороны, он сказал, что С++ является наиболее гибким... но все сказали "не делайте этого"? Мне кажется, что С++ может делать много вещей, но имеет ужасный синтаксис даже для самых простых вещей во многих случаях по сравнению с С#.

Обновление 2

Большинство пользователей говорят, что глупо создавать более глубокое гнездование, чем два уровня. Итак, как насчет Windows: UI:: Xaml и Windows:: UI:: Xaml:: Controls:: Primitives пространства имен в разработке Win8? Я думаю, что использование пространства имен Microsoft имеет смысл, и это действительно глубже, чем только 2 уровня. Я думаю, что более крупным библиотекам/проектам требуется более глубокое вложение (я ненавижу имена классов, такие как ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace... тогда вы могли бы поместить все в глобальное пространство имен тоже.)

Обновление 3 - Заключение

Большинство говорят, что "не делай этого", но... даже усиление имеет более глубокое гнездование, а затем один или два уровня. Да, это библиотека, но: Если вы хотите использовать многоразовый код - обрабатывайте свой собственный код, как библиотеку, которую вы дадите кому-то еще. Я также использую более глубокое вложение для целей поиска с использованием пространств имен.

4b9b3361

Ответ 1

С++ 17 может упростить определение вложенного пространства имен:

namespace A::B::C {
}

эквивалентно

namespace A { namespace B { namespace C {
} } }

См. (8) на странице пространства имен на cppreference:
http://en.cppreference.com/w/cpp/language/namespace

Ответ 2

Чтобы избежать глубоких отступов, я обычно делаю это следующим образом:

namespace A { namespace B { namespace C
{
    class X
    {
        // ...
    };
}}}

Ответ 3

пространства имен С++ используются для группировки интерфейсов, а не для разделения компонентов или выражения политического разделения.

Стандарт запрещает использование Java-пространства имен. Например, псевдонимы пространства имен предоставляют возможность легко использовать имена вложенных или длинных имен имен.

namespace a {
namespace b {
namespace c {}
}
}

namespace nsc = a::b::c;

Но namespace nsc {} будет ошибкой, потому что пространство имен может быть определено только с использованием имени-namespace namespace. По сути, этот стандарт облегчает пользователям такую ​​библиотеку, но для разработчика это сложно. Это мешает людям писать такие вещи, но смягчает последствия, если они это делают.

У вас должно быть одно пространство имен для каждого интерфейса, определенное набором связанных классов и функций. Внутренние или дополнительные суб-интерфейсы могут входить в вложенные пространства имен. Но более двух уровней в глубину должны быть очень серьезным красным флагом.

Рассмотрим использование символов подчеркивания и префиксов идентификаторов, где оператор :: не нужен.

Ответ 4

Нет, и, пожалуйста, не делайте этого.

Цель пространств имен в основном заключается в разрешении конфликтов в глобальном пространстве имен.

Вторичная цель - это локальная аббревиатура символов; например сложный метод UpdateUI может использовать using namespace WndUI для использования более коротких символов.

Я работаю над проектом 1.3MLoc, и единственные пространства имен, которые у нас есть:

  • импортированные внешние COM-библиотеки (в основном для изоляции конфликтов заголовков между #import и #include windows.h)
  • Один уровень пространств имен "public API" для определенных аспектов (пользовательский интерфейс, доступ к БД и т.д.).
  • "Реализация детали" пространства имен, которые не являются частью общедоступного API (анонимные пространства имен в .cpp, или ModuleDetailHereBeTygers пространства имен в только для заголовков)
  • перечисления - самая большая проблема в моем опыте. Они загрязняют, как сумасшедшие.
  • Я все еще чувствую, что это слишком много пространств имен.

В этом проекте имена классов и т.д. используют двух- или трехбуквенный "региональный" код (например, CDBNode вместо DB::CNode). Если вы предпочитаете последний, есть место для второго уровня "публичных" пространств имен, но не более того.

Перечисления, специфичные для класса и т.д., могут быть членами этих классов (хотя я согласен, что это не всегда хорошо, и иногда сложно сказать, нужно ли вам)

Редко требуется также пространство имен "компания", за исключением случаев, когда у вас возникают большие проблемы с сторонними библиотеками, которые распространяются как двоичные, не предоставляют собственное пространство имен и не могут быть легко помещены в один (например, в двоичном распределении). Тем не менее, по моему опыту заставить их в пространство имен сделать гораздо проще.


[edit] В соответствии с последующим вопросом Stegi:

Итак, как насчет пространств имен Windows:: UI:: Xaml и Windows:: UI:: Xaml:: Controls:: Primitives в разработке Win8? Я думаю, что использование пространства имен Microsoft имеет смысл, и это действительно глубже, чем просто 2 уровня

Извините, если я не был достаточно ясен: два уровня не являются жестким пределом, и больше не является внутренне плохим. Я просто хотел указать, что вы редко нуждаетесь более двух, по моему опыту, даже на большой базе кода. Вложение более глубокое или более мелкое является компромиссом.

Теперь, случай Microsoft, возможно, отличается. Предположительно, гораздо большая команда, и весь код является библиотекой.

Я бы предположил, что Microsoft имитирует успех .NET-библиотеки, где пространства имён способствуют обнаружению обширной библиотеки. (.NET имеет около 18000 типов.)

Я бы предположил, что в пространстве имен есть оптимальные (порядковые величины) символы. скажем, 1 не имеет смысла, 100 звуков правы, 10000 явно много.


TL; DR: Это компромисс, и у нас нет жестких чисел. Играйте в безопасное место, не переусердствуйте в любом направлении. "Не делай этого" происходит просто из "У тебя проблемы с этим, у меня были бы проблемы с этим, и я не вижу причин, по которым тебе это нужно".

Ответ 5

Я полностью поддерживаю peterchen answer, но хочу добавить что-то, что касается другой части вашего вопроса.

Объявление пространств имен является одним из очень редких случаев в С++, где мне действительно нравится использование #define s.

#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;

Для вложенности более трех уровней вам нужно будет добавить вспомогательные макросы до нужного количества.

Ответ 6

Здесь цитата из Lzz (Lazy С++) docs:

Lzz распознает следующие конструкции С++:

определение пространства имен

Неименованное пространство имен и все вложенные объявления выводятся в исходный файл. Это правило отменяет все остальные.

Можно присвоить имя именованного пространства имен.

   namespace A::B { typedef int I; }

эквивалентно:

   namespace A { namespace B { typedef int I; } }

Конечно, качество источников, зависящих от таких инструментов, является спорным... Я бы сказал, что это более любопытство, показывая, что синтаксическое заболевание, вызванное С++, может иметь много формы (у меня тоже есть...)

Ответ 7

Оба стандарта (С++ 2003 и С++ 11) очень ясны, что имя пространства имен является идентификатором. Это означает, что требуются явные вложенные заголовки.

Мое впечатление, что это не большая проблема, позволяющая размещать квалифицированный идентификатор помимо простого имени пространства имен, но по какой-то причине это не разрешено.

Ответ 8

Да, вам придется сделать это, как

namespace A{ 
namespace B{
namespace C{} 
} 
}

Однако вы пытаетесь использовать пространства имен так, как они не должны использоваться. Проверьте этот вопрос, возможно, вы найдете его полезным.