Идиома C ++ "без исходного кода" - PullRequest
3 голосов
/ 23 марта 2012

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

Однако в некоторых случаях «источники» должны бытьиспользуемый.Как только один пример, иногда возникают циклические зависимости, и реализация должна быть написана вне определения класса.Вот как я это делаю:

//part of libfoo.h
class Bar
{
  void CircularDependency(void);
};

#ifdef LIBFOO_COMPILE_INLINE
void Bar::CircularDependency(void)
{
  //...
}
#endif

Тогда проект, использующий libfoo, сделает в main.cpp следующее:

//main.cpp
#define LIBFOO_COMPILE_INLINE
#include "libfoo.h"

И в любом другом .cpp:

//other.cpp
#include "libfoo.h"

Дело в том, что секция compile-inline компилируется только один раз (в main.cpp).

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

Сразу: я знаю, что многие кодеры по уважительной причине предпочитают, чтобы их заголовки напоминали интерфейсы ине реализации, но генераторы документации IMHO лучше подходят для описания интерфейсов, потому что мне нравится скрывать все закрытые члены: -)

Ответы [ 3 ]

4 голосов
/ 23 марта 2012

Вы сможете использовать ключевое слово inline, если проблема циклической зависимости будет решена к тому времени, когда определение Bar::CircularDependency() появится в заголовке libfoo.h:

//part of libfoo.h
class Bar
{
  void CircularDependency(void);
};


// other stuff that 'resolves' the circular dependency
// ...
// ...

inline void Bar::CircularDependency(void)
{
  //...
}

Это упростит вашу библиотеку (пользователю не нужно иметь дело с тем фактом, что LIBFOO_COMPILE_INLINE нужно определять точно в одном месте, где включен заголовок).

2 голосов
/ 23 марта 2012

Мои причины, почему это плохая идея.

Увеличение времени компиляции

Каждый отдельный модуль компиляции, который включает заголовок, должен компилировать все файлы заголовков в дополнение к самому источнику. Это, вероятно, увеличит время компиляции и может привести к разочарованию при тестировании кода с небольшими изменениями. Кто-то может утверждать, что компилятор может оптимизировать его, но IMO не может оптимизировать его сверх всякой точки.

Большой сегмент кода

Если все функции написаны inline, это означает, что компилятор должен помещать весь этот код везде, где вызывается функция. Это взорвет сегмент кода, и это повлияет на время загрузки программы, и программа займет больше памяти.

Создает зависимость в клиентском коде с жесткой связью

Всякий раз, когда вы изменяете свою реализацию, каждый клиент должен обновляться (перекомпилируя код). Но если реализация была помещена в независимый общий объект (.so или .dll), клиент должен просто связаться с новым общим объектом.

Также я не уверен, зачем это делать.

//main.cpp
#define LIBFOO_COMPILE_INLINE
#include "libfoo.h"

Если вообще нужно это делать, он (а) мог бы просто поместить код реализации в сам main.cpp. В любом случае, вы можете определить LIBFOO_COMPILE_INLINE только в одном модуле компиляции. В противном случае вы будете дублировать определения.

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

2 голосов
/ 23 марта 2012

Круговые зависимости на самом деле не проблема, так как вы можете переадресовывать decalre, чтобы функции были встроенными. Однако я бы не стал этого предлагать: хотя подход «без исходного кода» изначально облегчает использование библиотеки, поступающей откуда-то, это приводит к увеличению времени компиляции и более тесной связи между файлами. В огромной исходной базе это по сути убивает hOpe для получения кода в разумные сроки. Конечно, огромный код начинается с пары миллионов строк кода, но кого волнуют тривиальные программы? (и да, место, где я работаю, содержит несколько десятков миллионов строк кода, встроенных в отдельные исполняемые файлы)

...