Запретить компоновщику удалять глобальные переменные - PullRequest
11 голосов
/ 30 апреля 2009

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

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)

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

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

Ответы [ 6 ]

3 голосов
/ 30 апреля 2009

Чтобы решить это в:

  • Visual studio (в том же решении): компоновщик> Общие> Использовать входы зависимостей библиотеки = да
  • Gcc: ссылка напрямую с .o файлами

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

2 голосов
/ 31 июля 2013

См. Ответ здесь: заставить Visual Studio связать все символы в файле lib

Этот на самом деле работал для меня. Другие предложения не сделали.

1 голос
/ 26 августа 2015

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

constant.h

const char blah[10];

extern const char *get_blah();

constant.c

#include "header.h"

const char *get_blah()
{ return blah; }

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

1 голос
/ 01 мая 2009

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

1 голос
/ 30 апреля 2009

Ознакомьтесь с ответом на «Лучший способ составить список данных по типу»

Здесь есть два ключевых важных понятия. Во-первых:

(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

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

register_Foo register_Foo::register_object( &Foo );

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

1 голос
/ 30 апреля 2009

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

  • превратить библиотеку в DLL

или

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...