Невозможно ли написать библиотеку только для заголовков?

Есть ли такой шаблон зависимостей, что невозможно хранить все только в заголовочных файлах? Что делать, если мы применяем правило только одного класса для каждого заголовка?

Для целей этого вопроса, пусть игнорируют статические вещи:)

Ответы

Ответ 1

Я не знаю никаких функций в стандартном С++, кроме статики, о которой вы уже упоминали, для чего требуется, чтобы библиотека определяла полную единицу перевода (а не только заголовки). Однако это не рекомендуется делать, потому что когда вы это делаете, вы вынуждаете всех своих клиентов перекомпилировать всю свою базу кода всякий раз, когда изменяется ваша библиотека. Если вы используете исходные файлы или статическую библиотеку или динамическую библиотечную форму распространения, ваша библиотека может быть изменена/обновлена ​​/изменена без принуждения всех к перекомпиляции.

Ответ 2

Возможно, я бы сказал, в явном состоянии не использовать ряд языковых особенностей: как вы заметили, несколько применений ключевого слова static.

Это может потребовать несколько трюков, но их можно просмотреть.

  • Вам нужно сохранить разницу заголовков и источников, когда вам нужно сломать цикл зависимостей, даже если эти файлы будут на практике на практике.
  • Свободные функции (не шаблоны) должны быть объявлены inline, компилятор не может их встроить, но если они объявлены, они не будут жалуяться, что они были переопределены, когда клиент построил свою библиотеку/исполняемый файл.
  • Глобальные общие данные (глобальные переменные и статические атрибуты класса) должны эмулироваться с использованием локального статического атрибута в методах функций/классов. На практике это мало что касается вызывающего абонента (просто добавляет ()). Обратите внимание, что в С++ 0x это становится предпочтительным способом, потому что он гарантированно является потокобезопасным, сохраняя при этом защиту от фиаско порядка инициализации, до тех пор... он не является потокобезопасным;)

Уважая эти три очка, я считаю, что вы сможете написать полноценную библиотеку только для заголовков (кто-нибудь видит что-то еще, что я пропустил?)

Несколько библиотек Boost использовали аналогичные трюки только для заголовков, хотя их код не был полностью шаблоном. Например, Asio делает очень сознательно и предлагает альтернативу с использованием флагов (см. примечания к выпуску для Asio 1.4.6):

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

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

Примечание. Мне интересно, могут ли быть встроены функции static, я предпочитаю использовать анонимные пространства имен, поэтому никогда не смотрел на него...

Ответ 3

Правило одного класса для заголовка не имеет смысла. Если это не работает:

#include <header1>
#include <header2>

то некоторые изменения этого будут:

#include <header1a>
#include <header2>
#include <header1b>

Это может привести к менее чем одному классу на каждый заголовок, но вы всегда можете использовать (void *) и casts и встроенные функции (в этом случае "inline", вероятно, будет должным образом проигнорирован компилятором). Поэтому мне кажется, что вопрос может быть сведен к:

class A
{
// ...
void *pimpl;
}

Возможно ли, что частная реализация, pimpl, зависит от объявления A? Если это так, то pimpl.cpp(как заголовок) должен предшествовать и следовать A.h. Но так как вы всегда можете использовать (void *) и casts и встроенные функции в предыдущих заголовках, это можно сделать.

Конечно, я мог ошибаться. В любом случае: Ick.

Ответ 4

В моей долгой карьере я не сталкивался с шаблоном зависимостей, который запретил бы реализацию только для заголовка.

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

Это не означает, что вы ДОЛЖНЫ всегда стремиться к библиотекам только для заголовков. Хорошо, как и они, они должны быть зарезервированы для шаблона и встроенного кода. Они НЕ ДОЛЖНЫ включать существенные сложные вычисления.