Переклад статті Using Namespaces Properly від Dejan Jelovic
Простори імен – дуже потужна особливість мови C++. Ця стаття не вчитиме синтаксису просторів імен, вона просто покаже як їх варто використовувати.
Простори імен лише обгортають всі імена, що в нього входять в якесь інше ім’я. Наприклад:
namespace net
{
class Socket { ... };
}
...
net::Socket socket;
Завдяки цьому ми впевнюємося, що якщо дві бібліотеки матимуть свої реалізації класу Socket, то якщо вони назвуть свої простори імен по-різному, ваша програма зможе їх використовувати без яких би то не було конфліктів.
Але це викликає ще одне питання: якщо дві незалежні компанії вирішать писати мережеві бібліотеки, то який шанс, що вони будуть робити реалізацію класу з назвою Socket? Я так думаю, що вірогідність цього відсотків 100.
Нам також подобається, коли простори імен легко набираються, що означає, що вони мають бути довжиною в 2-4 символи. З урахуванням цього, який шанс, що обидві компанії назвуть свій простір імен “Net”? 5 відсотків? 10 відсотків?
Як би там не було, це показує, що простори імен не вирішують цю проблему повністю – вони просто зменшують вірогідність її виникнення.
Рішення промислового рівня
Розв’язком цієї проблеми є використання довгих, унікальних назв простору імен, а потім їх включення в програму під коротким псевдонімом (alias).
Тож компанія, що розробляє мережеву бібліотеку напише щось типу:
namespace net_33843894
{
class Socket { ... };
}
де номер після “net_” має бути згенеровано генератором випадкових чисел. Нехай цей код розміщено в файлі заголовків з назвою.
Далі бібліотека продається клієнту, який вирішує використати її в проекті. Клієнт тоді описує свій власний спеціалізований для проекту файл заголовків з назвою типу і наступним вмістом:
#include <netlib>
namespace net = net_33843894;
Щойно ми створили власний псевдонім для простору імен для бібліотеки, який діє лише в межах нашого проекту. Якби простір імен “net” вже було зайнято іншою бібліотекою, то ми могли б обрати інше ім’я: net2, sock, чи ще щось. Тоді не виникало б ніяких конфліктів імен.
Спрощуючи все
Розумним було б зробити з бібліотекою щось таке, щоб вона стала простішою для користувача. В ідеалі він мав би клацнути двічі на інсталяційному файлі і бібліотека негайно стала б доступною у його середовищі розробки (IDE). Далі він просто набирав би “#include
Але якщо користувачу доведеться створювати свій заголовок для кожного заголовку вашої бібліотеки, то доведеться постійно страждати, використовуючи її. Не кожному це буде до смаку.
Розв’язком цієї проблеми є забезпечення розумних значень по замовчуванню, але дозволивши йому можливість змінити їх, якщо вони не підійдуть. Це можна зробити за допомогою макродиректив в файлі заголовків:
namespace net_33843894
{
class Socket { ... };
}
#ifndef NO_NET_33843894_ALIAS
namespace net = net_33843894;
#endif
Таким способом можна надати логічне значення по замовчуванню для простору імен, а якщо воно вже зайняте, то користувач зможе введенням макросу NO_NET_33843894_ALIAS
уникнути створення псевдоніму “net”.
Сучасні компілятори
Повідомлення про помилки це справжній кошмар при використанні шаблонів. З довгими назвами просторів імен ми зробимо їх ще жахливішими.
На жаль, жоден з компіляторів, якими я користуюсь, недостатньо розумний, щоб використовувати найкоротший з доступних псевдонімів у повідомленнях про помилки. Тобто навіть коли ви використовуєте аліас “net”, то повідомлення про помилку при використанні класу Socket буде посилатись на net_33843894::Socket
. Що не дуже то читабельно.
Тому я зазвичай йду на маленьку хитрість. Вона працює лише в заголовках, що містять виключно inline-функції (оскільки вона діє лиш на актуальні імена, що використовуються лінковщиком), але в мене їх достобіса. Якщо макрос NO_NET_33843894_ALIAS
не визначено, то я використовую коротке ім’я як ім’я простору імен, а довге – як псевдонім:
#ifdef NO_NET_33843894_ALIAS
namespace net_33843894
{
#else
namespace net
{
#endif
class Socket { ... };
}
#ifndef NO_NET_33843894_ALIAS
namespace net_33843894 = net;
#endif
І повідомлення про помилки стають більш зрозумілими.