Глобальная переменная С++ не инициализируется при связывании через статические библиотеки, но ОК при компиляции с исходным кодом
Я создал систему, которая автоматически регистрирует объекты функции (функторы) в карту на основе конструктора глобального экземпляра.
В каждом файле cpp, который определяет функтор, есть глобальный экземпляр экземпляра класса регистратора для регистрации функтора для объекта singleton std::map<int, std::function<...> >
.
Это определение класса регистратора:
template
<
typename map_type,
typename handler_type
>
struct registrar
{
registrar
(
map_type& map_object,
boost::uint16_t cmd_code,
const handler_type& handler
)
{
map_object.insert(std::pair<boost::uint16_t, handler_type>(cmd_code, handler));
}
};
в каждом файле .cpp. Глобальный экземпляр определяется следующим образом:
namespace one_way
{
static registrar <in_out_map_type, handler>
post_receiver(in_out_map_type::instance(), command, handlers());
}
Все работает отлично, если я скомпилирую все cpp вместе с main.cpp. Но если я скомпилирую файл cpp в статическую библиотеку и привяжу его к main.cpp
, регистрация не будет работать.
Я тестировал VC10 и GCC4.61 как на Windows, так и на Ubuntu 11.10. Оба не работают.
Я нашел поток с той же проблемой, но OP не сказал, решил ли он это или нет.
Я что-то пропустил?
Edit
Спасибо за ответы, включая комментарии.
Каждый ответ действительно помог мне больше подумать и глубоко изучить этот метод. После всех исследований и испытаний я, наконец, отказался от идеи полагаться на глобальную/статическую переменную для саморегистрации по двоичным границам, потому что нет портативного способа гарантировать это будет работать.
Мой последний способ - сохранить регистрацию в пределах одного двоичного файла.
Ответы
Ответ 1
Я считаю, что ваш объектный файл из библиотеки не связан. Посмотрите, как Microsoft обрабатывает символ acrtused (регистр регистра нечувствителен, я не помню этот случай, и на этом компьютере нет MSVC).
Как только вы знаете, как они обрабатывают acrtused, сделайте то же самое с вашей глобальной переменной, чтобы заставить ее подключиться.
Будет обновлен, если я найду ответ.
Вот несколько возможностей заставить вещи связываться и инициализировать в несколько принудительном порядке.
Посмотрите здесь для ответа GCC.
Посмотрите здесь для MSVC10.
Ответ 2
Короткий ответ для работы NDK для Android, любые статические библиотеки, которые подвержены этой проблеме, должны быть добавлены в переменную LOCAL_WHOLE_STATIC_LIBRARIES - после этого они будут ссылаться с использованием флага -Wl,--whole-archive
и не будут подвергаться удалению.
Более длинный ответ для MSVC:
Статические переменные в блоке трансляции инициализируются перед любым регулярным кодом в
выполняется переводная единица. На практике инициализация происходит, когда содержащая
загружается исполняемая или динамическая библиотека. Когда вы вызываете свой \c main() или ваш вызов
LoadLibrary()/dlopen()
завершается, любые статические переменные будут инициализированы.
Проблема, описанная MSDN:
Конструкторы и назначение по глобальной функции или статические методы в декларации делают не создавать ссылки и не будут препятствовать /OPT: устранение REF. Побочные эффекты от таких код не должен зависеть от того, когда не существует других ссылок на данные.
Удобно разместить объектный код из нескольких единиц перевода в одном
файл, статическую библиотеку, условно названную с суффиксом \c.lib или\c.a. Линейщик MSVC
анализ зависимостей в статических библиотеках и не будет включать код, на который не ссылаются
включением объекта.
Общая схема использования статической переменной для объявления и вызывания регистрации
объект factory может потерпеть неудачу при этом обстоятельстве - линкер MSVC считает статичным как
будучи недостижимым и удаляет его из результата.
Решения
Полезный поиск google: http://www.google.com/search?q=msvc+factory+static+library
Одним из решений является установка флага компоновщика /OPT:NOREF
для объекта включения. Однако,
это настройка "все или ничего" и потребует, чтобы все включенные библиотеки были полностью связаны.
Если что-то в файле, содержащем статику, ссылается (прямо или косвенно) на
включающий объект, то по языковым правилам статичность должна быть сохранена.
Самый простой подход - поставить в файл фиктивную функцию и ссылаться на
где-то известное считается доступным.
Другой подход - использовать флаг компоновщика /INCLUDE
для ссылки на объект в файле проблемы.
Предполагая, что объект с именем DummyForLinkProblem, это можно сделать в исходном объекте:
#pragma comment(linker, "/include:DummyForLinkProblem")
Решение ZooLib
Объекты ZooLib, затронутые этой проблемой,
ZFile_Win.cpp, ZGRgnRep_HRGN.cpp, ZNet_Internet_WinSock.cpp, ZStreamRWCon_SSL_Win.cpp,
ZTextCoder_Win.cpp и ZUnicode_Normalize_Win.cpp.
Мы #include ZCompat_MSVCStaticLib.h
в соответствующих файлах заголовка и помещаем в каждый
ZMACRO_MSVCStaticLib_Reference(ModifiedFileName)
. В файлах cpp мы помещаем
ZMACRO_MSVCStaticLib_cpp(ModifiedFileName)
. ModifiedFileName
, как правило,
имя файла с удаленным расширением Z и файла, тот же стиль, что и
используется в макросах ZCONFIG_API_XXX
.
Чтобы убедиться, что ваш исполняемый файл или библиотека не разделяет эти объекты, просто #include
соответствующий заголовочный файл из известного ссылочного кода в вашей включенной сущности. Это будет
вызывают нерабочую ссылку, и все будет работать должным образом.
Ответ 3
Не уверен, что это будет какая-то помощь или нет, но я задал вопрос, который звучит очень похоже:
Знание классов среды выполнения С++
Возможно стоит взглянуть?