Как сделать загрузку объекта без ссылки в C ++? - PullRequest
1 голос
/ 21 декабря 2009

У меня есть .cpp файл (назовем его statinit.cpp), скомпилированный и связанный с моим исполняемым файлом, используя gcc. Моя main() функция не в statinit.cpp.

statinit.cpp имеет некоторые статические инициализации, которые мне нужно запустить. Тем не менее, я никогда явно не ссылаюсь на что-либо из statinit.cpp в моем main() или во всем, на что оно ссылается. Что происходит (я полагаю), это то, что связанный объект, созданный из statinit.cpp, никогда не загружается во время выполнения, поэтому мои статические инициализации никогда не запускаются, что вызывает проблему в другом месте кода (это было очень трудно отладить, но я в конечном итоге отследил его ).

Существует ли стандартная библиотечная функция, опция компоновщика, опция компилятора или что-то, что может позволить мне принудительно загрузить этот объект во время выполнения без ссылки на один из его элементов?

Я подумал, что нужно определить фиктивную функцию в statinit.cpp, объявить ее в заголовочном файле, который видит main(), и вызвать эту фиктивную функцию из main(). Однако это очень уродливое решение, и я бы очень хотел избежать внесения изменений в сам statinit.cpp.

Спасибо, Daniel

Ответы [ 8 ]

4 голосов
/ 21 декабря 2009

Не совсем понятно, в чем проблема:

C ++ не имеет понятия статических инициализаторов.
Поэтому можно предположить, что у вас есть объект в «Области файла».

  • Если этот объект находится в глобальном пространстве имен, он будет создан до вызова main () и уничтожен после выхода из main () (при условии, что он находится в приложении).
  • Если этот объект находится в пространстве имен, то при желании реализация может выбрать отложенную инициализацию переменной. Это просто означает, что он будет полностью инициализирован перед первым использованием. Поэтому, если вы полагаетесь на побочный эффект от конструкции, поместите объект в глобальное пространство имен.

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

Для решения этой проблемы у вас есть несколько вариантов.

  • Не используйте статические библиотеки.
    • Компилировать в динамические библиотеки (сегодня это норма).
    • Скомпилируйте весь исходный код прямо в приложение.
  • Сделать явную ссылку на объект изнутри main.
2 голосов
/ 21 декабря 2009

Я столкнулся с той же проблемой.

Написать файл, DoNotOptimizeAway.cpp:

void NoDeadcodeElimination()
{
    // Here use at least once each of the variables that you'll need.
}

Затем позвоните NoDeadcodeElimination() из main.

РЕДАКТИРОВАТЬ : в качестве альтернативы вы можете редактировать параметры компоновщика и указывать ему всегда связывать все, даже если он не используется. Мне не нравится такой подход, так как исполняемые файлы станут намного больше.

1 голос
/ 21 декабря 2009

Один C ++ способ сделать это с помощью Singletons.

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

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

class MyClass {
   static MyClass& getObject()
   {
        static MyObject obj;
        return obj;
    }
};
1 голос
/ 21 декабря 2009

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

Явно инициализируйте данные статической функцией типа «InititalizeLibrary». Теперь вы гарантируете, что это происходит, и вы гарантируете, когда это произойдет по отношению к другому коду, в зависимости от того, когда вы совершаете вызов.

0 голосов
/ 22 декабря 2009

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

-Wl,-whole-archive

перед библиотекой -l и

-Wl,-no-whole-archive

после него (чтобы не включать и другие библиотеки в целом).

0 голосов
/ 22 декабря 2009

Прочтите страницу руководства по команде ld и посмотрите опцию -u. Если statinit.cpp определяет что-либо похожее на функцию, попробуйте назвать его в -u. В противном случае выберите объект данных, определенный в statinit.cpp, и назовите его -u, и надеемся, что он работает. Я думаю, что лучше всего, если вы напишите командную строку, так что параметр -u будет указан непосредственно перед параметром -l для вашей библиотеки, в которой есть объектный код statinit.

0 голосов
/ 21 декабря 2009

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

Предполагается, что вся статическая инициализация будет завершена до запуска main (). Однако вы можете столкнуться с проблемами, если инициализируете статический объект другим статическим объектом. (Примечание: это не применяется, если вы используете примитивы, такие как int)

Например, если у вас есть файл x.cpp:

static myClass x(someVals);

А в y.cpp:

static myClass y = x * 2; 

Возможно, что система попытается создать экземпляр y перед созданием x. В этом случае переменная "y", вероятно, будет 0, так как x, вероятно, 0, прежде чем она будет инициализирована.

В общем, лучшим решением для этого является создание объекта при его первом использовании (если это возможно). Однако, как я заметил выше, вам не разрешено изменять этот файл. Используются ли значения из этого файла в другом месте, и, возможно, вы можете изменить способ доступа к этим значениям?

0 голосов
/ 21 декабря 2009

Поскольку вы используете C ++, вы всегда можете объявить глобальный объект (т. Е. Глобальную переменную, которая ссылается на класс в statinit.cpp). Как всегда, конструктор будет вызываться при инициализации, и поскольку объект является глобальным, это будет вызывается до запуска main ().

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

...