Аналог __attribute__((constructor)) в MSVC?
От: Cyberax Марс  
Дата: 06.06.08 20:05
Оценка:
Привет всем!

Не подскажете аналог GCCшного "__attribute__((constructor))" в MSVC для чистого С? Или как вариант способ построить таблицу <имя>-<фабричная_функция>.

Я портирую FUSE на Windows и нужно вот такой макрос перенести:
#define FUSE_REGISTER_MODULE(name_, factory_) \
static __attribute__((constructor)) void name_ ## _register(void) \
{ \
    static struct fuse_module mod = { .name = #name_, .factory = factory_ }; \
    fuse_register_module(&mod); \
}
Sapienti sat!
Re: Аналог __attribute__((constructor)) в MSVC?
От: Кодт Россия  
Дата: 06.06.08 22:57
Оценка: 33 (1)
Здравствуйте, Cyberax, Вы писали:

<>

Ну, можно прибегнуть к такому фокусу
#define FUSE_REGISTER_MODULE(name,factory) \
  int name##_initialize(void) { .......; return 0; } \
  __declspec(dllexport) int name##_init = name##_initialize();

Правда, линкер может повыбрасывать якобы неиспользующиеся переменные вместе с функциями.
Чтобы этого не было, переменная объявлена экспортируемой.
Поскольку в виндах можно экспортировать что угодно в любом модуле — хоть dll, хоть exe — должно прокатить.
Перекуём баги на фичи!
Re[2]: Аналог __attribute__((constructor)) в MSVC?
От: remark Россия http://www.1024cores.net/
Дата: 07.06.08 17:00
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Cyberax, Вы писали:


К><>


К>Ну, можно прибегнуть к такому фокусу

К>
К>#define FUSE_REGISTER_MODULE(name,factory) \
К>  int name##_initialize(void) { .......; return 0; } \
К>  __declspec(dllexport) int name##_init = name##_initialize();
К>

К>Правда, линкер может повыбрасывать якобы неиспользующиеся переменные вместе с функциями.
К>Чтобы этого не было, переменная объявлена экспортируемой.
К>Поскольку в виндах можно экспортировать что угодно в любом модуле — хоть dll, хоть exe — должно прокатить.


MSVC в С не поддерживает динамическую инициализацию глобальных переменных...




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Аналог __attribute__((constructor)) в MSVC?
От: EyeOfHell Россия eyeofhell.habr.ru
Дата: 07.06.08 19:16
Оценка:

Не подскажете аналог GCCшного "__attribute__((constructor))" в MSVC для чистого С?


Нету. В gcc это — расширение языка C. В стандартом C ( не CPP ) вроде нету конструкторов, деструкторов и вызовов функций для глобальных объектов . Можно только константными значениями инициализировать.

Или как вариант способ построить таблицу <имя>-<фабричная_функция>.


в main(), DllMain(), WinMain() или что там еще исполняется:

void main( void )
{
  struct fuse_module mod;
  mod.name = VasjaPRVMC_CBT;
  mod.factory = _abirvalg;
  fuse_register_module( &mod ); // xxx don't check return. kewl!

  mod.name = MySuperModification;
  mod.factory = MyGlobalSuperModificationFactory;
  fuse_register_module( &mod ); // copy paste

  // ну и остальные дописать из портируемой штуки
}


Это честный метод для стандартного С. Макрос для произвольного места кода чтобы что-то регистрировал, насколько я знаю, сделать нельзя. Можно сделать макрос который можно будет использовать строго в одном .c файле, но это ничем не будет отличаться от вышеуказанного кода.
Re[2]: Аналог __attribute__((constructor)) в MSVC?
От: Cyberax Марс  
Дата: 08.06.08 18:29
Оценка:
Здравствуйте, EyeOfHell, Вы писали:

EOH>Нету. В gcc это — расширение языка C. В стандартом C ( не CPP ) вроде нету конструкторов, деструкторов и вызовов функций для глобальных объектов . Можно только константными значениями инициализировать.

А есть ли аналогичные расширения Microsoft для MSVC?
Sapienti sat!
Re[3]: Аналог __attribute__((constructor)) в MSVC?
От: Kluev  
Дата: 08.06.08 19:04
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Здравствуйте, EyeOfHell, Вы писали:


EOH>>Нету. В gcc это — расширение языка C. В стандартом C ( не CPP ) вроде нету конструкторов, деструкторов и вызовов функций для глобальных объектов . Можно только константными значениями инициализировать.

C>А есть ли аналогичные расширения Microsoft для MSVC?

самое простое обявить нужные функции как dllexport и проанализировать таблицу экспорта dll/exe
Re: Аналог __attribute__((constructor)) в MSVC?
От: Кодт Россия  
Дата: 09.06.08 08:23
Оценка: +1
Здравствуйте, Cyberax, Вы писали:

C>Не подскажете аналог GCCшного "__attribute__((constructor))" в MSVC для чистого С? Или как вариант способ построить таблицу <имя>-<фабричная_функция>.


Есть ещё вариант с препроцессингом.
В исходном тексте ты разбрасываешь (по месту) объявления вида
#define FUSE_REGISTER_MODULE(name,factory)   void name##_register(void) { ..... }

Потом натравляешь на исходники утилиту, которая собирает все вхождения и рожает файл
void register_all()
{
    hello_register();
    world_register();
    .....
}

А вызов register_all() запихиваешь в main.

При известной ловкости рук, можно даже на бат-файле сделать. Но конечно, perl или хотя бы grep+awk здесь будут сподручнее.

Ставишь на pre-build step или в соответствующее место makefile. И вуаля.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: Аналог __attribute__((constructor)) в MSVC?
От: Cyberax Марс  
Дата: 09.06.08 13:14
Оценка:
Здравствуйте, Кодт, Вы писали:

К>При известной ловкости рук, можно даже на бат-файле сделать. Но конечно, perl или хотя бы grep+awk здесь будут сподручнее.

Не пойдёт... FUSE_REGISTER_MODULE делается из сторонних библиотек, которые собираются своими системами.

Видимо, действительно проще всего проанализировать таблицу экспорта.
Sapienti sat!
Re[3]: Аналог __attribute__((constructor)) в MSVC?
От: Кодт Россия  
Дата: 09.06.08 13:57
Оценка:
Здравствуйте, Cyberax, Вы писали:

К>>При известной ловкости рук, можно даже на бат-файле сделать. Но конечно, perl или хотя бы grep+awk здесь будут сподручнее.

C>Не пойдёт... FUSE_REGISTER_MODULE делается из сторонних библиотек, которые собираются своими системами.

... а ты хочешь подсунуть им свой макрос? Т.е. подменить их зависимости?
Но если это в твоих силах, то почему бы тебе их исходники не пропарсить?

C>Видимо, действительно проще всего проанализировать таблицу экспорта.


Таблицу экспорта у объектников? А что, хорошая мысль. Сделать то же, что и __attribute__((constructor)) — всё равно линкер должен собрать ссылки на конструкторы, а так это сделаешь ты за линкера.
Только лучше делать сигнатуру префиксом имени, а не суффиксом — парсить будет чуть быстрее
#define FUSE_REGISTER_MODULE(name,factory) void ATTRCTOR_register_##name(void) { ..... }

ATTRCTOR 8 байт — если будешь не dumpbin+grep, а руками, то qword-ная сигнатура быстрее проверяется
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[4]: Аналог __attribute__((constructor)) в MSVC?
От: Cyberax Марс  
Дата: 09.06.08 14:54
Оценка:
Здравствуйте, Кодт, Вы писали:

К>... а ты хочешь подсунуть им свой макрос? Т.е. подменить их зависимости?

Да.

К>Но если это в твоих силах, то почему бы тебе их исходники не пропарсить?

Я портирую OpenSource-библиотеку FUSE (Filesystem in User SpacE). Её уже использует куча проектов на Линуксе. Хотелось бы, чтобы на Windows их можно было перенести простой перекомпиляцией с моим заголовочником и библиотекой. Т.е. хочется source level compatibility — с остальным кодом оно, вроде бы, вполне получается.

По крайней мере, "Hello world" из примеров к FUSE у меня уже работает на Windows.

К>Таблицу экспорта у объектников? А что, хорошая мысль. Сделать то же, что и __attribute__((constructor)) — всё равно линкер должен собрать ссылки на конструкторы, а так это сделаешь ты за линкера.

Я прямо в рантайме хочу это сделать

К>Только лучше делать сигнатуру префиксом имени, а не суффиксом — парсить будет чуть быстрее

К>
К>#define FUSE_REGISTER_MODULE(name,factory) void ATTRCTOR_register_##name(void) { ..... }
К>

К>ATTRCTOR 8 байт — если будешь не dumpbin+grep, а руками, то qword-ная сигнатура быстрее проверяется
Да, естественно с префиксом удобнее.
Sapienti sat!
Re[5]: Аналог __attribute__((constructor)) в MSVC?
От: Кодт Россия  
Дата: 09.06.08 15:31
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Я прямо в рантайме хочу это сделать


Тогда __declspec(dllexport), и пожалуйста, любыми средствами делаешь дамп таблицы экспорта и ищешь эти самые конструкторы.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: Аналог __attribute__((constructor)) в MSVC?
От: alexeiz  
Дата: 10.06.08 07:22
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Привет всем!


C>Не подскажете аналог GCCшного "__attribute__((constructor))" в MSVC для чистого С? Или как вариант способ построить таблицу <имя>-<фабричная_функция>.


C>Я портирую FUSE на Windows и нужно вот такой макрос перенести:

C>
C>#define FUSE_REGISTER_MODULE(name_, factory_) \
C>static __attribute__((constructor)) void name_ ## _register(void) \
C>{ \
C>    static struct fuse_module mod = { .name = #name_, .factory = factory_ }; \
C>    fuse_register_module(&mod); \
C>}
C>


Не совсем понял, что должен делать этот attribute. Вызывать эту функцию в crt init что-ли? Если так, то посмотри, может тебе подойдет вот это: http://www.rsdn.ru/Forum/message/1972258.flat.aspx
Автор: alexeiz
Дата: 25.06.06
Re[2]: Аналог __attribute__((constructor)) в MSVC?
От: Кодт Россия  
Дата: 10.06.08 09:07
Оценка:
Здравствуйте, alexeiz, Вы писали:

A>Не совсем понял, что должен делать этот attribute. Вызывать эту функцию в crt init что-ли? Если так, то посмотри, может тебе подойдет вот это: http://www.rsdn.ru/Forum/message/1972258.flat.aspx
Автор: alexeiz
Дата: 25.06.06


Так ведь речь идёт о Си. Там ни шаблонов, ни — что существенно — динамической инициализации статических/глобальных переменных.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re: Аналог __attribute__((constructor)) в MSVC?
От: lazyden  
Дата: 10.06.08 11:16
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>Не подскажете аналог GCCшного "__attribute__((constructor))" в MSVC для чистого С? Или как вариант способ построить таблицу <имя>-<фабричная_функция>.


Может подойдет способ, который используется в ATL для создания object map (OBJECT_ENTRY_AUTO). Там вначале объявляются две переменных-ограничителя, каждый в своем сегменте.
__declspec(allocate("FUSE$__a")) void *map_first = NULL;
__declspec(allocate("FUSE$__z")) void *map_last = NULL;


Макрос для регистрации помещает указатель на структуру также в отдельный сегмент.
#define FUSE_REGISTER_MODULE(name_, factory_) \
  static struct fuse_module name_ ## mod = { ... } \
  __declspec(allocate("FUSE$__m")) struct fuse_module *name_ ## mod_ptr = &name_ ## mod;


И где-то в инициализации библиотеки вставить код который явно вызывает функцию регистрации.

...
struct fuse_module **curr=(struct fuse_module **)(&map_first+1)
struct fuse_module **sentinel=&map_last;
while (curr<sentinel) { fuse_register_module(*curr); curr++; };
Re[2]: Аналог __attribute__((constructor)) в MSVC?
От: Cyberax Марс  
Дата: 10.06.08 14:39
Оценка:
Здравствуйте, lazyden, Вы писали:

L>Макрос для регистрации помещает указатель на структуру также в отдельный сегмент.

L>
L>#define FUSE_REGISTER_MODULE(name_, factory_) \
L>  static struct fuse_module name_ ## mod = { ... } \
L>  __declspec(allocate("FUSE$__m")) struct fuse_module *name_ ## mod_ptr = &name_ ## mod;
L>

L>И где-то в инициализации библиотеки вставить код который явно вызывает функцию регистрации.
А как оно с DLLями работает?
Sapienti sat!
Re[3]: Аналог __attribute__((constructor)) в MSVC?
От: lazyden  
Дата: 10.06.08 19:20
Оценка:
Здравствуйте, Cyberax, Вы писали:

C>А как оно с DLLями работает?


Ээ, не совсем понял

Фактически, такой способ
Автор: Кодт
Дата: 09.06.08
уже предлагал Кодт. В качестве "утилиты, которая собирает все вхождения и рожает файл" используется майкрософтовский линкер который собирает и упорядочивает секции данных в исполняемом модуле. В этом смысле оно будет работать с длл.
Сам инициализирующий цикл (как и register_all у Кодта) надо вызывать ручками, вставив его, например, в DllMain (так делает ATL). IMHO для портирования это вполне приемлемо, поскольку DllMain всё равно придется реализовывать.
Re[4]: Аналог __attribute__((constructor)) в MSVC?
От: Cyberax Марс  
Дата: 10.06.08 19:24
Оценка:
Здравствуйте, lazyden, Вы писали:

C>>А как оно с DLLями работает?

L>Ээ, не совсем понял
Что будет, если регистрации будут разбросаны по нескольким DLL?

L>Фактически, такой способ
Автор: Кодт
Дата: 09.06.08
уже предлагал Кодт. В качестве "утилиты, которая собирает все вхождения и рожает файл" используется майкрософтовский линкер который собирает и упорядочивает секции данных в исполняемом модуле. В этом смысле оно будет работать с длл.

Ок, буду смотреть.
Sapienti sat!
Re: .CRT$XCU
От: remark Россия http://www.1024cores.net/
Дата: 10.06.08 20:17
Оценка: 41 (3)
Здравствуйте, Cyberax, Вы писали:

C>Не подскажете аналог GCCшного "__attribute__((constructor))" в MSVC для чистого С? Или как вариант способ построить таблицу <имя>-<фабричная_функция>.



Ран-тайм MSVC использует ряд магических секций в исполняемом файле для своих целей. В частности есть секция, которая содержит указатели на функции, которые инициализируют глобальные объекты и выполняют динамическую инициализацию глобальных переменных. Точнее это не одна секция, а множество секций. Секции от ".CRT$XIA" до .CRT$XIZ" (по алфавиту, исключая границы) инициализируют С ран-тайм, секции от ".CRT$XA" до .CRT$XCZ" (по алфавиту, исключая границы) инициализируют С++ глобальные объекты и динамически инициализируемые глобальные объекты.
Правда в выполняемом файле все эти секции в итоге объединяются в секцию ".data":
#pragma comment(linker, "/merge:.CRT=.data")

Но это уже не важно, главное ран-тайм может найти и выполнить все функции, которые изначально содержались в этих секциях. Ищет он их до-тупого просто: размещает "граничные" переменные в секциях ".CRT$XIA"/".CRT$XIZ" и ".CRT$XCA"/".CRT$XCZ", а потом находит все, что расположено между ними:
__declspec(allocate((".CRT$XIA")) void* c_initializers_begin;
__declspec(allocate((".CRT$XIZ")) void* c_initializers_end;
__declspec(allocate((".CRT$XCA")) void* cpp_initializers_begin;
__declspec(allocate((".CRT$XCZ")) void* cpp_initializers_end;



Ну это, так, лирическое отступление. Собственно тебе нужен следующий макрос:
#define CTOR(X) \
    int X(); \
    __declspec(dllexport) \
    __declspec(allocate(".CRT$XCU")) \
    int(*my_ctors_##X)() = &X; \
    int X() \
/**/


__declspec(allocate(".CRT$XCU")) размещает указатель на функцию в сегменте функций инициализации.
__declspec(dllexport) подавляет оптимизации линкера в релиз-сборке.

Вот полный пример:
#include <stdio.h>

#pragma section(".CRT$XCU")
#define CTOR(X) \
    int X(); \
    __declspec(dllexport) \
    __declspec(allocate(".CRT$XCU")) \
    int(*my_ctors_##X)() = &X; \
    int X() \
/**/

int volatile x = 0;

CTOR(ctor1)
{
    x += 1;
    return 0;
}

CTOR(ctor2)
{
    x += 10;
    return 0;
}

int main()
{
    printf("%d\n", x);
}



Метод это, в принципе, полу-документированный, но положиться на него можно. Так как уже слишком много людей всё это расхачило и используют в своём коде. Например, в boost.thread используется аналогичный приём дабы получать нотификации о создании/завершении потоков:
[Trick] DllMain нотификации без DLL
Автор: remark
Дата: 29.03.08




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.