Подтвердить что ты не робот

Глобальная переменная С++ не инициализируется при связывании через статические библиотеки, но ОК при компиляции с исходным кодом

Я создал систему, которая автоматически регистрирует объекты функции (функторы) в карту на основе конструктора глобального экземпляра.

В каждом файле 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


Спасибо за ответы, включая комментарии.

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

Мой последний способ - сохранить регистрацию в пределах одного двоичного файла.

4b9b3361

Ответ 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 соответствующий заголовочный файл из известного ссылочного кода в вашей включенной сущности. Это будет вызывают нерабочую ссылку, и все будет работать должным образом.