Фабрики и бибилотеки
26 Mar 2013NB: скучный и технический пост о землях Страуструповых.
Потребовалось засунуть фабрику (см. GoF [1]) в статическую библиотеку и это породило удивительные эффекты, о которых по крайней мере я ранее не знал.
Итак, имеем фабрику в следующем виде (ее устройство не обсуждаем, несмотря на то, что она самописная, ее структура стандартная с небольшими плюшками в виде параметризуемого набора аргументов конструктора, смотри Александреску [2]):
Используется фабрика следующим образом. Допустим, нам необходимо создавать классы производные от `Base` c помощью фабрики по строковому идентификатору.
Определяем класс `Base` и соответствующую фабрику.
Для регистрации класса `Derived` производного от `Base` в фабрике в Derived.cpp файле необходимо определить глобально и разумнее всего статически
конструктор которого зарегистрирует `Derived` в фабрике. Для включения класса в фабрику теперь достаточно просто включить файл Derived.cpp в сборку (скомпилировать и передать линковщику соответствующий объектный файл). Все кул, что хотели – то и получили!
Теперь пытаемся вынести все это в статическую бибилотеку, которая предоставляет следующий интерфейс: – определение класса `Base` – определение фабрики для класса `Base`, в которой зарегистрированно сколько-то конкретных производных классов в зависиости от нашей прихоти (с какими опциями собиралсь бибилотека)
Пользователь библиотеки хочет просто создать сущность с помощью `BaseFactory::instance()->create(“Dervied”)`. Если такой класс не был зарегистрирован в библиотеке, то вернется нулевой указатель, иначе указатель на созданный объект.
Проблема заключается в том, что если засунуть все в статическую бибилотеку и собрать проект с ней, то фабрика всегда будет возвращать ноль. Это было неприятным сюрпризом, ноги которого растут из правил линковки.
Если передать линковщику объектный файл напрямую, то он обязан включить его в исполняемый файл и, соотвественно, проинициализировать все переменные с статической областью видимости. В случае бибилиотеки, являющейся не более чем собранием объекнтых файлов, линковщик обращается к ней в том и только в том случае, если не может разрешить какую-то ссылку. В этом случае он включает объектный код, соответствующий этой функции и объектный код, ответственный за инициализацию объектов с статической областью видимости. Так как в нашем случае нет неразрешенных ссылок на объектный файл `Derived.cpp.o`, то и не происходит инициализации `FactoryRegistrar` и регистрации в фабрике.
Для решения этой проблемы необходимо создавать ссылки на объектный файл, путем вызова функции, скажем, `Derived::dummy()` определенной в `Derived.cpp.o`, чего и хотелось избежать: зависимости фабрики от регистрируемых в ней классов.
Мораль: не помещать фабрики с статические библиотеки.
Библиография:
1. Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес Приемы объектно-ориентированного проектирования. Паттерны проектирования = Design Patterns: Elements of Reusable Object-Oriented Software. — СПб: «Питер», 2007. — С. 366. — ISBN 978-5-469-01136-1
2. Александреску А. Современное проектирование на С++: Обобщенное программирование и прикладные шаблоны проектирования = Modern C++ Design: Generic Programming and Design Patterns Applied. — С. П.: Вильямс, 2008. — 336 с. — (С++ in Depth). — ISBN 978-5-8459-0351-8
P.S. Мой первый пост на re-coders на техническую тему и что я обнружваю? - не работает, использованное <pre> - кое-как, съедает “эллипсисы” (…), блок для кодеров который не может нормально отображать код - пичалька…