Вызов функции перед main в статических библиотеках - PullRequest
0 голосов
/ 19 декабря 2018

У меня есть система регистрации типов для настраиваемой формы информации о типах во время выполнения.До сих пор я использовал следующий макрос для вызова функции регистрации перед main и регистрации типов:

#define REGISTRATION                                    \
static void _register();                                \
namespace { struct temp { temp() { _register(); } }; }  \
static const temp CAT(temp, __LINE__);                  \
static void _register()

Таким образом, я могу сделать это во множестве различных файлов cpp:

REGISTRATION()
{
    RegisterNewType(vec2)
    ->RegisterMember("x", &vec2::x)
    ->RegisterMember("y", &vec2::y);
}

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

Я решил поискать решение и нашел следующее:

#define REGISTRATION                                  \
static void _register() __attribute__((constructor)); \
static void _register()

Это может решить мою проблему, но оказывается, что я могу сделать это только в clang и gcc.Я использую MSVC.

Есть ли способ заставить это работать, чтобы я мог иметь предварительные функции в библиотеках?

РЕДАКТИРОВАТЬ: Еще несколько поисков привели меня к этому:

#if defined(_MSC_VER)
#pragma section(".CRT$XCU",read)
#define EXT_REGISTRATION_(f,p) \
        static void __cdecl f(void); \
        __declspec(allocate(".CRT$XCU")) void (__cdecl*f##_)(void) = f; \
        __pragma(comment(linker,"/include:" p #f "_")) \
        static void __cdecl f(void)
#ifdef _WIN64
#define EXT_REGISTRATION(f) EXT_REGISTRATION_(f,"")
#else
#define EXT_REGISTRATION(f) EXT_REGISTRATION_(f,"_")
#endif
#else
#define EXT_REGISTRATION(f) \
        static void f(void) __attribute__((constructor)); \
        static void f(void)
#endif

Однако функция регистрации по-прежнему не вызывается.Отладчик утверждает, что No symbols have been loaded.

Ответы [ 2 ]

0 голосов
/ 20 декабря 2018

В итоге я нашел решение этой проблемы в игровом движке под названием Ethereal .Этот парень получает кредит за этот код.

Вместо функции одного регистра у меня есть две:

#define CAT_IMPL(a, b) a##b
#define CAT(a, b) CAT_IMPL(a, b)

#define REGISTER_EXTERN(cls)\
    template <typename T>\
    extern void register_func();\
    template <>\
    void register_func<cls>();\
    static const int CAT(temp, __LINE__) =\
        reg_helper::call<cls>(&register_func<cls>)

#define REGISTER(cls)\
    template <> \
    void register_func<cls>()

И затем у меня есть эта вспомогательная функция:

namespace reg_helper
{
    template <typename T>
    inline int call(void(*f)())
    {
        static const int s = [&f]() {
            f();
            return 0;
        }();
        return s;
    }
}

В функциях заголовка я определяю REGISTER_EXTERN(SomeClass);, а затем в файлах cpp, где-то я использую макрос регистра, как раньше:

REGISTER(SomeClass)
{
    // This code will run before main
}

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

Это решает проблему статических библиотек, поскольку заголовкибудут включены и символы будут существовать.А во-вторых, это гарантирует, что я могу инициализировать вещи в правильном порядке.

0 голосов
/ 19 декабря 2018

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

В Linux используйте --whole-archive, чтобы указать gcc (или clang) интегрировать полную библиотеку в окончательный выходной файл.

В Windows используйте /WHOLEARCHIVE.

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

Итак, два варианта:

  • весь архив, простой, но объединяет все из статической библиотеки
  • вручную ссылается на функции регистра внутри, скажем, main (достаточно просто иметь object;)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...