Как назвать разделы / группы для трех объектов C++ при использовании init_seg?
Я использую init_seg
управлять созданием трех объектов класса C++. Каждый объект находится в отдельном исходном файле / модуле перевода. Отладка показывает, что объекты создаются должным образом во время инициализации CRT.
Объекты инициализируются в алфавитном порядке их исходного файла. Я хотел бы изменить это, потому что это не совсем правильно. Я посетил страницу MSDN на init_seg
, и это заявляет, что использование:
#pragma init_seg({ compiler | lib | user | "section-name" [, func-name]} )
Похоже, использование lib
а также section-name
являются взаимоисключающими, поэтому мне не ясно, как использовать init_seg(lib)
и укажите название раздела / группы, чтобы получить правильный алфавитный порядок.
Когда я пытаюсь использовать алфавитную строку для управления порядком:
#pragma init_seg(lib, "01")
Это приводит к предупреждению, которое, я предполагаю, означает, что все не будет работать так, как ожидалось:
warning C4081: expected ')'; found ','
Когда я пытаюсь вставить непосредственно в код запуска CRT непосредственно с помощью ".CRT$XCB"
, ".CRT$001"
, а также ".CRT$XCB001"
(и другие варианты использования алфавитизации):
#pragma init_seg(".CRT$XCB")
Это приводит к другому предупреждению, которое, как я предполагаю, означает, что все не будет работать так, как ожидалось:
warning C4075: initializers put in unrecognized initialization area
Я нашел один вопрос о переполнении стека по этому поводу, но ответ был предположительным, и он не охватывает несколько единиц перевода. Я также нашел архив KB104248 на Wayback Machine, но это не сильно помогает, потому что он показывает только использование compiler
, lib
а также user
,
Итак, мой вопрос, как я могу использовать init_seg
контролировать точный порядок создания моих трех объектов в трех разных исходных файлах?
2 ответа
Вот что я нашел в ходе тестирования на XP и VS2002/VS2003, Vista и VS2005/VS2008, Windows 7 и VS2008/VS2010, Windows 8 и VS2010/VS2012/VS2013 и Windows 10 с использованием VS2015. #pragma_init(<name>)
был доступен с VC++ 1.0 дней. MS не публикует слишком много информации об этом, но мы знаем, что она документирована с VC++1.0 (в архиве KB104248) до VS2017.
#pragma init_seg(lib)
почти идеально. Однако в VS2008 и более ранних версиях объектные файлы расположены в алфавитном порядке, поэтому порядок инициализацииa-b-c
(нежелательный), а неc-b-a
(По желанию). Его хорошо на VS2010 и выше. Что не очевидно, так это порядок,c-b-a
вvcproj
файлы.#pragma init_seg(".CRT$XCB-0NN")
казалось, работает. нашstd::strings
STRING_A
а такжеSTRING_B
были созданы рано (и объекты были в правильном порядке), ноSTRING_B
вызвал аварию на suhutdown. Адрес был0x0000000d
и оказываетсяstd::string
(и его vtable) были уничтожены слишком рано.#pragma init_seg(".CRT$XCU-0NN")
работал как положено во время запуска и выключения. Если я правильно прочитал, тоU
в названии группыXCU
указывает определенные пользователем объекты. Это означает, что наши объекты были созданы где-то между#pragma init_seg(lib)
а также#pragma init_seg(user)
обеспечивает.
Итак, вот как инициализировать объект C, затем объект B, затем объект A из исходных файлов. a.cpp
, b.cpp
а также c.cpp
,
Исходный файл a.cpp
:
class A
{
...
};
#pragma warning(disable: 4075)
#pragma init_seg(".CRT$XCU-030")
A a; // created 3rd
#pragma warning(default: 4075)
Исходный файл b.cpp
:
class B
{
...
};
#pragma warning(disable: 4075)
#pragma init_seg(".CRT$XCU-020")
const B b; // created 2nd
#pragma warning(default: 4075)
Исходный файл c.cpp
:
#pragma warning(disable: 4075)
#pragma init_seg(".CRT$XCU-010")
const std::string c; // created 1st
const std::string d; // created 1st
#pragma warning(default: 4075)
Наш вариант использования состоял в том, чтобы создать три объекта только для чтения и избежать проблем с фиаско статического порядка инициализации C++ и локального хранилища потоков Microsoft.
Техника позволяет избежать отсутствия динамических инициализаторов C++ в C++03. Это также побуждает Microsoft к неспособности обеспечить параллельную динамическую инициализацию и разрушение в C++11 (или, точнее, неспособность Microsoft предоставить базовую языковую функцию в течение 10 лет).
Вот ссылка на проблему с локальным хранилищем потоков (TLS) в MSDN:
В операционных системах Windows до Windows Vista __declspec(поток) имеет некоторые ограничения. Если DLL объявляет какие-либо данные или объект как __declspec(поток), это может вызвать сбой защиты при динамической загрузке. После загрузки библиотеки DLL с помощью LoadLibrary она вызывает системный сбой всякий раз, когда код ссылается на данные __declspec( thread). Поскольку пространство глобальных переменных для потока выделяется во время выполнения, размер этого пространства основан на расчете требований приложения плюс требований всех статически связанных библиотек DLL. Когда вы используете LoadLibrary, вы не можете расширить это пространство, чтобы разрешить локальные переменные потока, объявленные с помощью __declspec( thread). Используйте API-интерфейсы TLS, такие как TlsAlloc, в вашей DLL для выделения TLS, если библиотека DLL может быть загружена с помощью LoadLibrary.
Стоит также упомянуть, что количество символов в названии раздела или группы не ограничено. В архиве KB 104248 используется имя "user_defined_segment_name"
с 26 символами.
Вам нужно использовать #pragma section
( https://msdn.microsoft.com/en-us/library/50bewfwa.aspx), чтобы указать атрибуты раздела, если вы используете пользовательское имя раздела
#pragma section("foo",long,read,write)
#pragma init_seg("foo")
Вы можете убедиться, что пользовательские сегменты из нескольких файлов упорядочены путем добавления суффикса после знака доллара.
// tu1.cpp
#pragma section("foo$1",long,read,write)
#pragma init_seg("foo$1")
// tu2.cpp
#pragma section("foo$2",long,read,write)
#pragma init_seg("foo$2")
Данные из tu1.cpp теперь будут до этого из tu2.cpp
Вы можете упорядочить вещи относительно библиотеки времени выполнения C, добавив суффикс к сегментам CRT
// tu1.cpp
#pragma section(".CRT$XCU1",long,read,write)
#pragma init_seg("foo$1")
// tu2.cpp
#pragma section(".CRT$XCU2",long,read,write)
#pragma init_seg("foo$2")
TU1 теперь перед TU2 и сгруппирован с другими данными