У меня есть одна единица перевода с моим main()
функция и еще один ТУ без основного. Предположим даже, что я управляю только вторым и не могу коснуться первого.
Теперь, по причинам, я не буду вдаваться в подробности, я хочу иметь возможность запустить некоторый код раньше main()
пробеги. Я знаю, что это можно сделать, инициализируя глобальную переменную с помощью вызова функции, но я хочу скрыть это — с минимальным использованием макросов, насколько это возможно (осмелюсь сказать, что использование макросов невозможно — возможно, это невозможно, в C ++ нет надлежащего статического блока) )
Каким будет элегантный или, скажем так, не очень уродливый способ сделать это? Чтобы быть более ясным, я ищу что-то, что обеспечило бы эту функциональность для многократного использования, а не просто что-то, чтобы заставить его работать один раз. Я хочу, чтобы это было как можно ближе к:
// ... at global scope ...
static {
// my code here
}
PS: этот вопрос связан, но не такой, как этот вопрос об инициализации статических членов класса. Это также мотивировано желанием четко опровергнуть это утверждение не может быть сделано в C ++.
Примечание: Да, я знаю о фиаско статического порядка инициализации, нет необходимости напоминать мне об этом … и я не прошу что-то, что обходит это. Очевидно, что выполнение кода статически требует некоторой осторожности.
Пожалуйста, наслаждайтесь статический порядок инициализации фиаско:
int f(/* whatever args you want*/)
{
// code to be ran before main()
return 42;
}
static int _ignore = f(/*...*/);
Обратите внимание, что иногда код может не вызываться, если он не используется где-либо еще (псевдоним «оптимизирован»). Один из таких случаев — когда TU скомпилирован в статическую библиотеку (тогда неиспользуемая переменная и код могут не помещаться в исполняемый файл). (записка Е. Масковского).
Это лучшее, что я мог придумать до сих пор. Это работает, но реализация не совсем понятна.
Если вы напишите:
STATIC_BLOCK {
std::cout << "Hello static block world!" << std::endl;
}
этот код будет выполняться до вашего main()
, Тем не менее, обратите внимание, что запись в std::cout
до main()
На самом деле это не такая уж хорошая идея.
Заметки:
В реализации статического блока используется фиктивная переменная. Чтобы гарантировать, что мы не столкнемся с какой-либо другой фиктивной переменной (например, из другого статического блока — или где-либо еще), нам нужно немного макро-машин.
#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)
#define STATIC_BLOCK_IMPL2(function_name,var_name) \
static void function_name(); \
static int var_name __attribute((unused)) = (function_name(), 0) ; \
static void function_name()
#define STATIC_BLOCK_IMPL1(prefix) \
STATIC_BLOCK_IMPL2(CONCATENATE_FOR_STATIC_BLOCK(prefix,_fn),CONCATENATE_FOR_STATIC_BLOCK(prefix,_var))
#define STATIC_BLOCK STATIC_BLOCK_IMPL1(EXPAND_THEN_CONCATENATE(static_block_,__COUNTER__))
Заметки:
__COUNTER__
(поскольку это расширение стандарта, а не его часть) — вы можете использовать __LINE__
, который тоже работает. Поддержка GCC и Clang __COUNTER__
,__attribute__((unused))
другое расширение компилятора, хотя атрибуты поступают в язык; увидеть это обсуждение например. Если вы уроните его, вы получите предупреждение.Первоначально вдохновленный Андреем Александреску SCOPE_EXIT
трюк.