Глобальная переменная C ++ не инициализируется при связывании через статические библиотеки, но в порядке при компиляции с исходным кодом - PullRequest
20 голосов
/ 27 февраля 2012

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

В каждом файле cpp, который определяет функтор, есть глобальный экземпляр экземпляра класса регистратора для регистрации функтора в одноэлементном 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 не сказал, решил он это или нет.

Я что-то упустил?


Редактировать


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

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

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

Ответы [ 2 ]

9 голосов
/ 05 июля 2012

Краткий ответ для работы Android NDK. Любые статические библиотеки, на которые влияет эта проблема, должны быть добавлены в переменную LOCAL_WHOLE_STATIC_LIBRARIES - на них будет ссылаться, используя флаг -Wl,--whole-archive, и они не будут подвергаться удалению.

Более длинный ответ для MSVC:

Статические переменные в блоке перевода инициализируются перед любым обычным кодом в перевод блока выполняется. На практике инициализация происходит, когда содержащий исполняемая или динамическая библиотека загружена. Когда вызывается ваш \ c main () или ваш вызов LoadLibrary()/dlopen() завершено, любые статические переменные будут инициализированы.

Проблема, как описано MSDN :

Конструкторы и присваивание глобальными функциями или статическими методами в объявлении Не создавать ссылки и не будет препятствовать / OPT: REF устранение. Побочные эффекты от таких Код не должен зависеть от того, когда нет других ссылок на данные.

Может быть удобно разместить объектный код из нескольких единиц перевода в одном file, статическая библиотека, условно названная с суффиксом \ c .lib или \ c .a. Компоновщик MSVC делает анализ зависимостей от статических библиотек и не включает код, на который нет ссылок включающим субъектом.

Общий шаблон использования статической переменной для объявления и регистрации регистрации заводской объект может потерпеть неудачу в этом случае - компоновщик MSVC считает статический быть недоступным и лишает его результата.

* Решения 1022 *

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

0 голосов
/ 27 февраля 2012

Я считаю, что ваш объектный файл из библиотеки не связывается. Посмотрите, как Microsoft обрабатывает используемый символ (поиск не зависит от регистра, я не помню регистр, и на этом компьютере нет MSVC).

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

Обновлюсь, если найду ответ.

Вот несколько возможностей принудительно связать вещи и инициализировать их в несколько вынужденном порядке.

Посмотрите здесь для ответа GCC.

Смотрите здесь для MSVC10.

...