Предотвращение чрезмерного использования пространств имен - PullRequest
7 голосов
/ 02 мая 2011

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

Library name
    Class name 1
    Class name 2
    Class name 3
    [...]
    Utilities
        Class name 1
            [...]
        Class name 2
            [...]
        Class name 3
            [...]
        [...]

Пространство имен "Утилиты" содержит полезные расширения для каждого из классов, которые не требуют включения в сам фактический класс..

Пространство имен «Имя библиотеки» необходимо, поскольку оно позволяет избежать широких конфликтов с другими библиотеками, а пространство имен «Утилиты» необходимо, чтобы избежать двусмысленности, возникающей из таких вещей, как ,и пространства имен «Class name» внутри него позволяют избежать столкновений имен между утилитами, написанными для похожих классов.

Несмотря на это, на практике это все еще является огромной проблемой.Возьмем, к примеру, следующее:

MyLibrary::MyContainer<int> Numbers = MyLibrary::Utilities::MyContainer::Insert(OtherContainer, 123, 456);
// Oh God, my eyes...

Это заставляет меня думать, что я делаю что-то серьезно неправильно.Есть ли более простой способ держать вещи организованными, интуитивно понятными и однозначными?

Ответы [ 4 ]

12 голосов
/ 02 мая 2011

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

Boost помещает большинство вещей в boost, в то время как major библиотеки получают одно подпространство имен (boost::mpl или boost::filesystem, например).И библиотеки обычно определяют единственное подпространство имен aux для внутренних деталей реализации.

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

Вот несколько полезных правил:

Вспомогательные функции, связанные с конкретным классом, должны находиться в том же пространстве имен, что и класс, чтобы позволить ADL работать.Тогда вам вообще не нужно уточнять имя вспомогательной функции при ее вызове.(Например, как вы можете вызывать sort вместо std::sort на итераторах, определенных в std).

Для всего остального, помните, что целью пространств имен является предотвращение столкновений имен и ничего больше.Таким образом, вся ваша библиотека должна находиться в пространстве имен a , чтобы избежать конфликтов с пользовательским кодом, но в этом пространстве имен нет технической необходимости в дополнительных подпространствах имен, если только вы не планируете вводить конфликтующие имена.

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

Но, как правило, я бы предложил всего несколькокак можно больше вложенных пространств имен.

И, наконец, я стремлюсь использовать короткие, удобные для ввода и легко читаемые имена для своих пространств имен (опять же, std - хороший примерСледуйте. Коротко и по существу, и почти всегда без дальнейших вложенных пространств имен, так что вам не будет скучно от необходимости часто писать его, и поэтому он не будет слишком загромождать ваш исходный код.)

Только первое правило о вспомогательных функциях и ADL позволило бы переписать ваш пример следующим образом:

MyLibrary::MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

Тогда мы могли бы переименовать MyLibrary, скажем, Lib:

Lib::MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

и вы готовы к чему-то довольно управляемому.

Не должно быть никаких столкновений между похожими служебными функциями для разных классов.C ++ позволяет вам перегружать функции и специализировать шаблоны, так что вы можете иметь как Insert(ContainerA), так и Insert(ContainerB) в одном и том же пространстве имен.

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

Помните, что в вашем Library пространстве имен только вы диктует, какие имена вводить.И поэтому вы можете избежать столкновений имен, просто не создавая никаких конфликтующих имен.Пространство имен для отделения кода пользователя от кода библиотеки важно, потому что они могут не знать друг о друге, и поэтому столкновения могут произойти непреднамеренно.

Но в вашей библиотеке вы можете просто дать всем не конфликтующие имена.

7 голосов
/ 02 мая 2011

Если что-то болит, прекратите это делать.Абсолютно нет необходимости использовать глубоко вложенные пространства имен в C ++ - они не предназначены для архитектурных устройств.Мой собственный код всегда использует один уровень пространств имен.

Если вы настаиваете на использовании вложенных пространств имен, вы всегда можете создать для них короткие псевдонимы:

namespace Util = Library::Utility;

затем:

int x = Util::somefunc();   // calls Library::Utility::somefunc()
3 голосов
/ 02 мая 2011

Объявление в заголовочном файле требует пространства имен, чтобы не загрязнять глобальное пространство имен:

MyLibrary::Utilities::MyContainer<int> Numbers;

Но в исходном файле вы можете использовать следующие символы:

using namespace MyLibrary::Utilities;

...

MyContainer<int> Numbers;
Numbers.Insert(OtherContainer, 123, 456);
2 голосов
/ 02 мая 2011

Полностью определенное имя на самом деле не так уж плохо для меня, но мне нравится быть явным в именах методов и классов. Но using может помочь:

Возможно, вам не помешает глобальная область действия using namespace MyLibrary в ваших исходных файлах:

MyContainer<int> Numbers = Utilities::MyContainer::Insert(OtherContainer, 123, 456);

И затем вы можете импортировать нужные вам функции:

using MyLibrary::Utilities::MyContainer::Insert

и затем MyContainer<int> Numbers = Insert(OtherContainer, 123, 456);

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