Предотвратить удаление линков

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

typedef int (*FuncPtr)(int);

struct RegHelper
{
    RegHelper(const char * Name, FuncPtr Func)
    {
        Register(Name, Func);
    }
}

#define REGISTER(func) RegHelper gRegHelper_ ## func (#func, func);

Теперь я могу зарегистрировать функции таким образом (я использую его для реализации какого-то отражения):

int Foo(int a)
{
    return a * 123;
}

REGISTER(Foo)

int Bar(int a)
{
    return a * 456;
}

REGISTER(Bar)

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

Мой вопрос: что я могу сделать, чтобы обойти это? Вызов фиктивных функций в каждом модуле компиляции во время инициализации, похоже, вызывает построение глобальных переменных, но это не очень безопасно. Любое другое предложение?

Ответ 1

Чтобы решить эту проблему, выполните следующие действия:

  • Visual studio (в том же решении): Linker > General > Use library Dependency Inputs = yes
  • Gcc: ссылка непосредственно с .o файлами

Я не нашел решение, которое мне действительно нравится.

Ответ 3

Да, у меня тоже была эта проблема. Единственные верные пути вокруг него, которые я нашел, были:

  • сделать библиотеку в DLL

или

  • переместить объекты регистрации в исполняемый файл

Ни один из них не идеален, хотя решение DLL в порядке, если вы не против использования DLLS, и мне тоже было бы интересно узнать о других решениях.

Ответ 4

Обратите внимание на "Лучший способ создания списка данных по типу"

Есть два важных важных понятия. Во-первых:

(void) register_object;  

использует объект, чтобы гарантировать, что компоновщик не отделяет его, и

template<typename D> typename automatic_register<D>::exec_register 
    automatic_register<D>::register_object;

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

// Global list of objectsh
struct FpList
{
   FpList( FuncPtr func ) :
      func(func)
   {
      head = next;
      next = this
   }
   FpList* next;
   FuncPtr func;

   static FpList* head;
};
// In .cxx:
FpList* FpList::head = 0;  

Затем измените свой макрос регистрации так, чтобы REGISTER (Foo), чтобы он создавал:

struct register_Foo : FpList
{
   register_Foo( FuncPtr fn ): FpList(fn)
   {
      (void) register_object;  
   }
   static register_Foo register_object;
};

Я думаю, этого недостаточно. Вам все равно нужно установить шаблон, передать if & Foo и убедиться, что

register_Foo register_Foo::register_object
Экземпляр

создается где-то. Код шаблона для auto_register показывает, как это сделать в заголовке. Если вы можете поместить свой макрос в .cxx, просто объявите:

register_Foo register_Foo::register_object( &Foo );

как часть вашего макроса. Я думаю, что это может сработать. (все из памяти, так кто знает).

Ответ 5

Если вы находитесь в среде UNIX, вызывающей ld с параметром "весь архив", принудительно включайте все объектные файлы в статическую библиотеку независимо от использования.

Ответ 6

Еще одно возможное решение этой проблемы. Получите постоянное значение через вызов функции. Вот так:

constant.h

const char blah[10];

extern const char *get_blah();

constant.c

#include "header.h"

const char *get_blah()
{ return blah; }

Это помогло мне сделать трюк!