2017-02-14 3 views
1

Скажем, я хочу написать API (для C/Linux), который предлагает настраиваемый выходной поток, например stdout, но мой должен быть вызван not_stdout. Поэтому я могу потребовать, чтобы люди, использующие мой API, всегда начинали свою основную программу, вызывая функцию init_the_stream(), которая инициализирует extern FILE* not_stdout.Инициализация пользовательского потока вывода до основного

Но мне бы очень хотелось, чтобы мой поток был инициализирован до main(), так что он работает так же, как stdout.

Я бы предположил, что это несколько сложно сделать переносимым образом, так как стандарт C хочет, чтобы первичные инициализированные переменные были константами или строковыми литералами, а stdout получает специальную обработку компилятора. Но я не уверен, поэтому я хочу спросить:

Возможно ли написать библиотеку C, чтобы начальная информация, например, extern FILE* not_stdout, была инициализирована до первой строки main() всякий раз, когда библиотека включена?

+2

Как стандарт, определяемый 'main' как отправная точка языка, по-видимому, нет стандартного способа. Это зависит от вашей платформы. Но вы должны быть осторожны с такими автоматизмами. Используйте их только в том случае, если обычный способ имеет большое значение для кода. (чего я не вижу для вашей собственной библиотеки). – Olaf

+0

Вы можете скрыть 'main()' в своей библиотеке и написать пользователю 'myMain', который вызывается оттуда. Так появился Microsoft 'WinMain()'. – tofro

ответ

1

На gcc и clang вы можете использовать __attribute__((constructor)) (не стандарт C).

Пример:

#include <stdio.h> 
__attribute__((constructor)) 
static void not_stdout__init(void) 
{ 
    puts("initializing not_stdout"); 
} 
int main() 
{ 
    puts("main"); 
} 

Он хорошо работает с динамически связаны (.so) или загружены (dlopen) ELF библиотеки - если библиотека предоставляет такие крючки, они будут ссылаться, когда библиотека компонуется в

.

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

+2

Это прекрасно, и так идиоматично подходит в его хакерстве! –

+0

Вы используете 'stdout' в своем конструкторе; есть ли гарантии, что 'stdout' был инициализирован до вызова вашего конструктора? В общем, есть ли гарантия последовательности таких конструкторов? Что делать, если гипотетический 'stdlog' и' not_stdout' должен быть инициализирован, но 'stdlog' зависит от инициализации' not_stdout'? Вы должны убедиться, что конструктор является идемпотентным, поэтому, если его следует использовать только один раз, это не наносит вреда второму вызову? Должен ли он вызывать предварительные конструкторы (поэтому гипотетический конструктор stdlog вызывает конструктор 'not_stdout')? –

+0

Вы можете создавать конструкторы, назначая приоритет вызову, так что это не проблема. Кроме того, очевидно, что он использует вызов stdout в качестве заполнителя. Также убедитесь, что конструктор дважды не вызывается, обертывая заголовок в #IFDEF. Это стандартный материал. Я понижаю ваш рейтинг вниз. –

Смежные вопросы