Support us

Шаблон для проектов на С++ с помощью Boost, CMake. Namespace guidelines

Оставить комментарий
Шаблон для проектов на С++ с помощью Boost, CMake. Namespace guidelines

Я никого не призываю делать именно так. Я предлагаю ответы на некоторые вопросы и пытаюсь описать, какие плюсы вы получите.
Естественно все плюсы и минусы будут описаны исходя из условий возможности/необходимости разработки в различных IDE и компиляции различных компиляторов.
Когда начинаешь писать новый проект (модуль), задумываешься, как составить иерархию. Да ещё такую, которую не придётся переделывать хотя бы раз в месяц.
Посмотрим, какие вопросы создания иерархии namespace и именования мне удалось решить.

Читать дальше

1) Использование дополнительного namespace для каждого перечисления (enum).
Пример:

namespace message_level
{
    enum value
    {
        note = 0,
        warn = 1,
        error = 2,
        debug = 3,
        fatal = 4
    };
}

Плюсы:
* все перечисления имеют строго определённый префикс;
* IDE может подсказывать, какие константы возможны для сравнения с данной переменной. (Набираем message_level::)

Если не использовать такой namespace то, скорее всего ваш код будет выглядеть следующим образом:

void my_method( const message_level_value msg_level, const error& e )
{
    if ( msg_level == ERROR_MESSAGE_LEVEL ) // возможно просто ERROR
        print_error( e );
}

При таком использовании, можно случайно использовать ERROR из другого перечисления (который будет иметь совершенно другое числовое значение).
Также такое использование никак не показывает человеку, читающему код, что сравниваются величины, которые действительно могут сравниваться.
В дополнение поиск необходимых констант с помощью IDE становится затруднительным.
А вот пример, как сделать этот код читаемым:

void my_method( const message_level::value msg_level, const error& e )
{
    if ( msg_level == message_level::error )
        print_error( e );
}

 

2) Использование одного общего namespace для всех файлов вашего проекта (solution для msvc).
Обязательное Использование namespace второго уровня для модулей проекта. Это позволяет не засорять namespace проекта и классифицировать используемые типы/классы/функции модулями.
Даже если весь проект состоит всего из одного модуля (что уже плохо из-за отсутствия TDD да и тестирования в целом).
(Самый плохой) Пример:

namespace system_utilities
{
    namespace core
    {
        class a_type{};
    }
}

int main()
{
    system_utilities::core::a_type a;
}

Плюсы:
* Выстраивание взаимодействия модулей для любых проектов;
* Простота поиска созданных вами модулей и их составных частей с помощью IDE. (Набираем system_utilities:: и видим все модули и все классы модуля).

3) Категорически не использовать using namespace, using, typedef в *.h, *.hpp, если вы точно не уверены в том, что делаете.
Чем меньше типов и методов в вашем namespace, тем проще будет использовать подсказки IDE и тем проще будет искать ранее созданные классы/функции.

Для MSVC (стандартный IntelliSence) посоветую также не использовать using namespace, using, typedef вне классов/функций *.cpp файлов.
IntelliSence добавляет в список все возможные классы из всех файлов.
Поэтому набрав "system_utilities::" вы увидите не только те классы, которые реально доступны из данного файла (фактически включены при помощи #include); и даже не те классы, которые могут быть доступны при добавлении #include;
а вообще все классы/функции/под-namespace/typedef/enum/... которые внесены хоть в одном файле исходного кода.

Пример:

// cat stdafx.h
namespace A
{
    namespace B
    {
        class O
        {
        public:
            explicit O( const int a );
        };
    }
}
//-----------------------------------------------------------------------
// cat stdafx.cpp
namespace A
{
    namespace B
    {
        O::O(){}
        class C
        {
            C(){}
        };
        class D
        {
            D(){}
        };
    }
}

Теперь для использования подсказок IDE вводим: A::B:: и видим выбор из C, D, O. А оно вам надо?

4) Используйте namespace details для вспомогательных типов, которые нет необходимости знать пользователю вашего модуля - но они используются в модуле.

Раньше я пытался использовать неименованные namespace для этого или складывать такие типы в namespace самого модуля, но в итоге модуль сильно разрастался.
Неименованные namespace ужасно отображаются в MSVC. Два неименованных под-namespace одного namespace ломают IntelliSence 2005-ой студии до того, что она перестаёт показывать внутренности namespace.
Например:

namespace example
{
    namespace
    {
        // ...
    }
}

Если такой код написать в двух разных файлах и 2005 IntelliSence их проиндексирует, то IDE перестанет показывать все внутренности example.
В целом видеть `anonymous-namespace' в подсказках IDE никак не помогает.

Если же использовать namespace details то мы гарантированно можем в любой момент вынести реализацию функций/методов в *.cpp файл, что далеко не так хорошо выглядит для неименованных namespace.

Пример по использованию namespace details: https://github.com/sidorovis/system_utilities/blob/master/sources/logger/logger.h
Пользователю нет необходимости знать о message_level, logger_streamer и я не отвлекаю его внимание на особенности реализации.

5) При использования системы тестирования или TDD, тоже может возникнуть вопрос, в какой namespace лучше размещать тесты.
Я размещаю все тесты в дополнительный namespace tests_ (пример: https://github.com/sidorovis/system_utilities/blob/master/tests/logger_tests/logger_tests.cpp)
В качестве примера модуль logger можно декларировать следующим образом:

namespace system_utilities
{
    namespace common
    {
        class logger;
    }
}

Тогда соответствующие тесты для модуля будут распологаться в:

namespace system_utilities
{
    namespace tests_
    {
        namespace common
        {
            void logger_constructor_tests()
            {
                //...
            }
        }
    }
}

Почему я использую иерархию "system_utilities::tests_::common", а не например "system_utilities::common::tests_"? Таким образом, я выделяю тесты, как отдельный модуль для всего solution и не создаю множество вхождения tests_ для всех модулей. Они не нужны пользователю модулей. Чем меньше упоминаний о tests_ тем лучше.
Вопрос подчёркивания в конце наименования tests_ я оставлю для другой статьи.

К слову в последней ссылке c примером теста (logger_tests), используется namespace details. Как раз в случае тестов я рекомендую использовать не namespace details, а неименованный namespace.
Кто знает почему, можете предполагать или с уверенностью говорить в комментариях.

По-прежнему вопросы guide-lines, необходимости использования Boost, CMake оставлю на будущее.

Читайте также
10 курсов по C++ (июнь 2023)
10 курсов по C++ (июнь 2023)
10 курсов по C++ (июнь 2023)
С++, несмотря на свой солидный возраст, остается одним из основных языков программирования, который применется очень широко: от разработки ПО до создания игр. В сети много ресурсов, которые помогут освоить этот язык. Советуем обратить внимаение на подборку команды Digitaldefynd, котрую мы дополнили. В ней как платные, так и бесплатные ресурсы для людей с разным уровнем подготовки и знаний С++.
1 комментарий
Как оплачиваются самые популярные языки GitHub и какой прогноз
Как оплачиваются самые популярные языки GitHub и какой прогноз
Как оплачиваются самые популярные языки GitHub и какой прогноз
Google создала «убийцу» С++
Google создала «убийцу» С++
Google создала «убийцу» С++
4 комментария
С++ по последним рейтингам растет больше всех языков программирования. Кажется, время пройти курсы
С++ по последним рейтингам растет больше всех языков программирования. Кажется, время пройти курсы
С++ по последним рейтингам растет больше всех языков программирования. Кажется, время пройти курсы

Хотите сообщить важную новость? Пишите в Telegram-бот

Главные события и полезные ссылки в нашем Telegram-канале

Обсуждение
Комментируйте без ограничений

Релоцировались? Теперь вы можете комментировать без верификации аккаунта.

Комментариев пока нет.