Использование связи увеличения пространства имен C++?

Я понимаю, что библиотека C++ должна использовать пространство имен для предотвращения коллизий имени, но так как я уже имею к:

  1. #include корректный заголовок (или вперед объявляют классы, которые я намереваюсь использовать),
  2. Используйте те классы по имени

Не делайте эти два параметра выводят ту же информацию, переданную пространством имен. Используя пространство имен теперь представляет третий параметр - полностью определенное имя. Если реализация изменений библиотеки, существует теперь три потенциальных вещи, я должен измениться. Это не, по определению увеличение связи между кодом библиотеки и моим кодом?


Например, взгляд на Xerces-C: Это определяет чисто-виртуальный названный интерфейс Parser в пространстве имен XERCES_CPP_NAMESPACE. Я могу использовать Parser интерфейс в моем коде включением соответствующего заголовочного файла и затем любым импортом пространства имен using namespace XERCES_CPP_NAMESPACE или снабжение предисловием объявлений/определений с XERCES_CPP_NAMESPACE::.

Поскольку код развивается, возможно, существует потребность отбросить Xerces в пользу другого синтаксического анализатора. Я частично "защищен" от изменения в реализации библиотеки чисто-виртуальным интерфейсом (еще больше, если я использую фабрику для построения моего Синтаксического анализатора), но как только я переключаюсь от Xerces до чего-то еще, я должен прочесать свой код и изменить все мой using namespace XERCES_CPP_NAMESPACE и XERCES_CPP_NAMESPACE::Parser код.


Я столкнулся с этим недавно, когда я осуществил рефакторинг существующий проект C++ разделить некоторую существующую полезную функциональность на библиотеку:

foo.h

class Useful;  // Forward Declaration

class Foo
{
public:

    Foo(const Useful& u);
    ...snip...

}

foo.cpp

#include "foo.h"
#include "useful.h" // Useful Library

Foo::Foo(const Useful& u)
{
    ... snip ...
}

В основном из незнания (и частично из лени) в то время, вся функциональность useful.lib был помещен в глобальное пространство имен.

Как содержание useful.lib вырос (и больше клиентов начало использовать функциональность), было решено переместить весь код от useful.lib в его собственное названное пространство имен "useful".

Клиент .cpp файлы было легко зафиксировать, просто добавить a using namespace useful;

foo.cpp

#include "foo.h"
#include "useful.h" // Useful Library

using namespace useful;

Foo::Foo(const Useful& u)
{
    ... snip ...
}

Но .h файлы были действительно трудоемкими. Вместо того, чтобы загрязнить глобальное пространство имен путем помещения using namespace useful; в заголовочных файлах я перенес существующие предописания в пространство имен:

foo.h

namespace useful {
    class Useful;  // Forward Declaration
}

class Foo
{
public:

    Foo(const useful::Useful& u);
    ...snip...
}

Были десятки (и десятки) файлов, и это закончило тем, что было сильной болью! Это не должно было быть настолько трудно. Очевидно я сделал что-то не так с любым дизайн и/или реализация.

Хотя я знаю, что код библиотеки должен быть в его собственном пространстве имен, был бы он быть выгодным для кода библиотеки остаться в глобальном пространстве имен и вместо этого попытаться справиться #includes?

7
задан Mike Willekes 5 February 2010 в 03:30
поделиться

7 ответов

Мне кажется, что ваша проблема в первую очередь связана с тем, как вы (ab) используете пространства имен, а не с самими этими пространствами.

  1. Звучит так, как будто ты бросаешь много минимально связанных "вещей" в одно пространство имен, в основном (когда ты к этому приступаешь), потому что они случайно были разработаны одним и тем же человеком. По крайней мере, IMO, пространство имён должно отражать логическую организацию кода, а не просто случайность, что кучу утилит написал один и тот же человек.

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

  3. Большинство клиентского кода не нуждается (и часто не должно) использовать настоящее имя пространства имен напрямую. Вместо этого он должен определить псевдоним пространства имен, и только псевдоним должен использоваться в большинстве кода.

Складывая точки два и три вместе, мы можем получить в итоге что-то вроде кода:

#include "jdate.h"

namespace dt = Jerry_Coffin_Julian_Date_Dec_21_1999;

int main() {

    dt::Date date;

    std::cout << "Please enter a date: " << std::flush;
    std::cin>>date;

    dt::Julian jdate(date);
    std::cout   << date << " is " 
                << jdate << " days after " 
                << dt::Julian::base_date()
                << std::endl;
    return 0;
}

Это устраняет (или, по крайней мере, резко уменьшает) связь между клиентским кодом и конкретной реализацией классов дата/время. Например, если бы я хотел переопределить одни и те же классы даты/времени, я мог бы поместить их в другое пространство имен и переключаться между ними, просто меняя псевдоним и перекомпилируя.

На самом деле, я использовал это иногда как своего рода механизм полиморфизма времени компиляции. Например, я написал пару версий небольшого класса "display", один из которых отображает вывод в списке Windows, а другой - через iostreams. Затем код использует псевдоним вроде:

#ifdef WINDOWED
namespace display = Windowed_Display
#else
namespace display = Console_Display
#endif

Остальной код просто использует display::whatever, так что пока оба пространства имен реализуют весь интерфейс, я могу использовать любой из них, вообще не меняя остальной код, и без каких-либо накладных расходов на выполнение от использования указателя/ссылки на базовый класс с виртуальными функциями для реализации.

10
ответ дан 6 December 2019 в 06:36
поделиться

Я хотел бы расширить второй абзац Дэвида Родригеса - ответ dribeas (одобрено):

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

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

Я могу понять вашу "лень": это неправильно для сверхинженера ( enterprise HelloWorld.java), но если вы вначале будете сдерживать свой код (что не обязательно ошибочен), и код окажется успешным, успех поднимет его выше своей лиги. уловка состоит в том, чтобы уловить подходящий момент для переключения (или применить с первого момента возникновения необходимости) технику, которая устраняет зуд с прямой совместимостью .

Искрометные предварительные заявления по проекту - это просто напоминание о втором и последующих раундах. Вам не обязательно быть программистом на C ++, чтобы прочитать совет «не объявляйте стандартные потоки вперед, используйте вместо них » (хотя это было актуально несколько лет; 1999 - эпоха VC6, определенно). Вы можете услышать множество болезненных воплей от программистов, которые не прислушались к совету, если немного задержаться.

Я могу понять побуждение держать его в тени, но вы должны признать, что #include <полезныйfwd.h> не больше боли, чем класс Полезные и шкалы . Это простое делегирование избавит вас от N-1 изменений с класса Полезный на класс Полезный :: Полезный .

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

src / libuseful / полезныйfwd.h

#ifndef GUARD
#define GUARD
namespace useful {
    class Useful;
} // namespace useful
#endif

src / myapp / myapp-полезныйfwd.h

#ifndef GUARD
#define GUARD
#include <usefulfwd.h>
using useful::Useful;
#endif

В основном, это вопрос сохранения кода DRY . Возможно, вам не понравятся броские TLA, но этот описывает действительно основной принцип программирования.

2
ответ дан 6 December 2019 в 06:36
поделиться

Что ж, правда в том, что нет способа легко избежать запутывания кода на C ++. Однако использование глобального пространства имен - худшая идея, потому что тогда у вас не будет возможности выбирать между реализациями. Вы получаете Object вместо Object. Это нормально работает внутри компании, потому что вы можете редактировать исходный код по ходу дела, но если кто-то отправляет такой код клиенту, им не следует ожидать, что он будет долгим.

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

0
ответ дан 6 December 2019 в 06:36
поделиться

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

Чтобы упростить пересылку, вы могли бы написать заголовок forward (есть пара в STL, вы наверняка можете найти его в библиотеках), например полезныйfwd.h , который только пересылает определили интерфейс библиотеки (или реализующие классы, или что вам нужно). Но это не имеет отношения к сцеплению.

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

9
ответ дан 6 December 2019 в 06:36
поделиться

(a) интерфейсы/классы/функции из библиотеки

Не больше, чем у вас уже есть. Использование пространства имён-ed компонентов библиотеки поможет вам избавиться от загрязнения пространства имён.

(b) подробности реализации, выведенные из пространства имён?

Почему? Все, что вам нужно включить - это заголовок useful.h. Реализация должна быть скрыта (и находиться в useful.cpp и, вероятно, в форме динамической библиотеки).

Вы можете выборочно включить только те классы, которые вам нужны из useful.h, имея с помощью полезных::Useful деклараций.

6
ответ дан 6 December 2019 в 06:36
поделиться

Если у вас есть несколько реализаций вашей «полезной» библиотеки, то разве не одинаково вероятно (если не под вашим контролем), что они будут использовать одно и то же пространство имен, будь то глобальное пространство имен или полезное пространство имен?

Иными словами, использование именованного пространства имен в сравнении с глобальным пространством имен не имеет ничего общего с тем, как вы «связаны» с библиотекой/реализацией.

Любая согласованная стратегия эволюции библиотеки должна поддерживать одно и то же пространство имен для API. Реализация может использовать различные пространства имен, скрытые от вас, и они могут изменяться в различных реализациях. Не уверен, что это то, что вы имеете в виду под «деталями реализации, выведенными пространством имен».

-121--3783522-

Я делаю следующее...

Нажмите правую кнопку мыши на панели TaskBar в инструменте для разработчиков de IE 8, чтобы выбрать перемещение.

Затем попытайтесь перемещаться с помощью мыши или с помощью стрелок

-121--840152-

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

потребитель может выбрать, чтобы сделать

 using useful;
 using useful::Foo;
 useful::Foo = new useful::Foo();

мой голос всегда за последний - его наименее загрязняющий

ТО первый следует решительно отказаться (путем расстрела)

0
ответ дан 6 December 2019 в 06:36
поделиться

Если у Вас несколько реализаций Вашей "полезной" библиотеки, то не одинаково ли вероятно (если не под Вашим контролем), что они будут использовать одно и то же пространство имён, будь то глобальное пространство имён глобальное или пространство имён полезное ?

Другими словами, использование пространства имён в сравнении с глобальным пространством имён не имеет никакого отношения к тому, насколько Вы "связаны" с библиотекой/реализации.

Любая стратегия развития когерентной библиотеки должна поддерживать одно и то же пространство имён для API. Реализация может использовать различные скрытые от вас пространства имен, которые могут меняться в различных реализациях. Не уверен, что это то, что вы подразумеваете под "деталями реализации, выведенными из пространства имен"

.
0
ответ дан 6 December 2019 в 06:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: