Nano Hash - криптовалюты, майнинг, программирование

Статическая переменная-член для динамически загружаемого класса

Если бы мне нужно было загрузить некоторые символы, используя что-то вроде dlopen в C++, в то время как другие классы в этой единице перевода имели бы static переменных-членов, каково именно поведение этих статических переменных-членов. Они инициализируются или нет, потому что библиотека на самом деле не загружает только те символы, которые вы искали (что, я думаю, последнее неверно, потому что, если символ, который вы искали, нуждается в них, они также должны быть загружены)?


  • возможный дубликат Когда инициализируются статические члены класса C++? 09.07.2014
  • Не дублировать это связано с загрузкой библиотеки во время выполнения и с тем, что делают статические члены. 09.07.2014
  • Достаточно справедливо, близкое голосование отозвано - возможно, вам следует пометить свою ОС, потому что у меня есть ощущение, что это будет зависеть от реализации, а не от чего-то, указанного в стандарте. 09.07.2014

Ответы:


1

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

Во-первых, некоторые стандарты и объяснение того, почему это поведение undefined, а затем некоторые обходные пути.

Слово «статический», к сожалению, перегружено в Стандарте, так что потерпите меня. Стандарт ссылается как на длительность статического хранения, так и на статическую инициализацию. Типы продолжительности хранения, определенные Стандартом, являются статическими, потоковыми, автоматическими и динамическими. Они такие, как звучат. Статическая продолжительность хранения означает, что время жизни такой переменной равно всей продолжительности программы.

Статическая инициализация — это отдельная концепция. Хотя переменная может быть сохранена только один раз за выполнение программы, значение, с которым она будет инициализирована, может быть неизвестно при запуске программы. В начале программы все переменные со статической продолжительностью хранения будут инициализированы нулями, а те, которые могут быть, затем будут инициализированы константами. Тонкие моменты описаны в §3.6.2, но грубо говоря, статическая переменная будет инициализирована константой, если ее инициализация зависит только от константных выражений. Вместе инициализация нуля и инициализация констант называются статической инициализацией. Аналогом является динамическая инициализация. Это интересные, но, к сожалению, нет переносимого способа заставить динамическую инициализацию выполняться до первого выполнения main() в случае динамической компоновки или до возврата dlopen() в случае динамической загрузки. C++ просто не требует такого.

Ключевая часть стандарта С++ 11 находится в §3.6.2:

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

Тем не менее, если вы экспериментировали, вы заметили, что иногда это работает. Иногда вы можете получить произвольный код для запуска при загрузке библиотеки, вставив его в конструкторы статических переменных. Произойдет ли это, зависит только от компилятора (а не компоновщика). Man-страница для dlopen объясняет.

Если динамическая библиотека экспортирует подпрограмму с именем _init(), то этот код выполняется после загрузки, прежде чем dlopen() вернется.

Изучая вывод asm небольшого общего объекта, написанного на стандартном C++, я вижу, что clang 3.4 и g++ 4.8 добавляют раздел _init, однако они не обязаны это делать.

Что касается обходных путей, расширение gcc, которое стало обычным явлением, позволяет контролировать это поведение. Добавляя к функциям атрибут конструктора, мы можем настаивать на том, чтобы они запускались при инициализации библиотеки. Связанная справочная страница для dlopen предлагает использовать этот метод.

См. документацию GCC по атрибутам функций и этот вопрос SO, который имеет пример использования. Это расширение поддерживается gcc, clang, IBM XL, и я предполагаю, что icc также его поддерживает. MSVC не поддерживает это, но я понимаю, что есть что-то подобное.

Действительно портативное решение неуловимо. Как говорится в стандарте, если вы можете каким-то образом вызвать использование odr в той же единице перевода, что и статическая переменная, тогда статическая переменная должна быть инициализирована. Вызов функции, даже фиктивной, только для этой цели сработает.

15.09.2014
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..