Есть ли способ, в C, чтобы гарантировать, что функция вызывается только один раз без pthread_once? - PullRequest
0 голосов
/ 13 февраля 2019

В C, есть ли способ обеспечить вызов функции только один раз без использования pthread_once?

Следующее работает в C++, но, очевидно, не в C, поскольку инициализациястатическая переменная должна быть постоянной (как я понимаю, ошибка компиляции)

// main.c

int func()
{
  return 42;
}

int main( int argc, char* argv[] )
{
  static int i = func();
  return 0;
}

Я думал, что использование оператора запятой может обойти это, но это тоже не работает:

// main.c

int func()
{
  return 42;
}

int main( int argc, char* argv[] )
{
  static int i = ( func(), 42 );
  return 0;
}

Компиляция обоих приводит к следующей ошибке компиляции:

> gcc -g main.c
main.c: In function 'main':
main.c:10:18: error: initializer element is not constant

Есть ли способы обойти это и гарантировать, что функция вызывается только один раз (из области вызывающей функции) без использования pthread_once?

Если быть точным, я не хочу возвращать рано с func(), если он был вызван один раз, меня интересует заверение во время компиляции, что func() только вызывается один раз из области вызывающей функции - то есть аналогично тому, как C++ будет обрабатывать вышеуказанный код.
(Другими словами, приведенный выше код допустим для компилятора C++, который обеспечивает func() вызывается только один раз - яs есть эквивалентный способ сделать это в C без pthread_once?)

РЕДАКТИРОВАТЬ:
Я не сформулировал это идеально в оригинальном сообщении: я искалдля решения, которое не включает в себя функции или переменные оболочки / помощника;то есть мне было любопытно узнать, существует ли конструкция на языке C , которая позволяет обрабатывать эту ситуацию эквивалентно тому, как она обрабатывается в C++.Решение jxh подходит лучше всего, используя расширение gcc.

Ответы [ 3 ]

0 голосов
/ 13 февраля 2019

Вы можете сделать это с флагом static.

// main.c

int func()
{
  return 42;
}

int main( int argc, char* argv[] )
{
    static int initialized = 0;
    if(!initialized) {
        func();
        initialized = 1;
    }
}

При первом вызове кода вызова флаг initialized не устанавливается, поэтому функция будет запущена.Для любых последующих вызовов флаг уже установлен, поэтому функция вызываться не будет.

0 голосов
/ 13 февраля 2019

Нет способа использовать инициализацию статической переменной

Ваша попытка использовать инициализацию статической переменной не будет работать.C допускает только инициализацию переменной static с помощью констант, поэтому отсутствует вызов функции.

Вызов функции одним выстрелом при запуске программы (или при загрузке библиотеки)

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

#include <stdio.h>

__attribute__((constructor)) 
void func()
{
  puts(__func__);
}

int main () {}

Это предложение не выполняет ваш конкретный запрос:

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

Вместо этого он гарантирует, что функция вызывается ровно один раз при запуске программы (или при загрузке библиотеки, в которую она входит).

Использование статической переменной в качестве защиты

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

void caller_of_func()
{
    static bool func_already_called;
    if (!func_already_called) {
        func();
        func_already_called = true;
    }
    /*...*/
}

Используйте указатель функции!

Еще один способ достичь вашей цели - вызвать функцию через указатель функции,Первоначальный вызов функции выполнял бы реальную работу, а затем переключал бы указатель на функцию, указывающую на функцию, которая ничего не делает.

void nothing_func(int *x);
void initial_func(int *x);
void (*func)(int *x) = initial_func;

void initial_func(int *x) {
    *x = 42;
    puts(__func__);
    func = nothing_func;
}

void nothing_func(int *x) {
    puts(__func__);
}

void foo(void) {
    static int x;
    func(&x);
    printf("%s: %d\n", __func__, x);
    ++x;
}

int main(void) {
    foo();
    foo();
}
0 голосов
/ 13 февраля 2019

Вы можете заключить вашу функцию в другую функцию, которая проверяет статическую переменную и вызывает func, только если она не была вызвана ранее как -

static int func_internal() {
    ...
}

int func() {
    static int guard = 0;
    if (guard)
        return 0;
    guard = 1;
    return func();
}

Теперь, выставьте только func другиммодули.

...