Функция Call Guard - PullRequest
       19

Функция Call Guard

2 голосов
/ 21 июля 2009

Предположим, у меня есть бесплатная функция с именем InitFoo. Я хотел бы защитить эту функцию от случайного вызова несколько раз. Не задумываясь, я написал следующее:

void InitFoo()
{
    {
        static bool flag = false;
        if(flag) return;
        flag = true;
    }

    //Actual code goes here.
}

Это похоже на большую бородавку. InitFoo не не необходимо сохранять любую другую информацию о состоянии. Может кто-нибудь предложить способ достичь той же цели без уродства?

Макросы, конечно, не в счет.

Ответы [ 8 ]

10 голосов
/ 21 июля 2009

Вы можете сделать это с другим уродством:

struct InitFoo
{
     InitFoo()
     {
         // one-time code goes here
     }
};

void Foo()
{
    static InitFoo i;
}

Вы по-прежнему используете static, но теперь вам не нужно выполнять собственную проверку флага - static уже устанавливает флаг и проверяет его, поэтому он создает i только один раз.

3 голосов
/ 21 июля 2009

Ну, конструктор вызывается автоматически только один раз. Если вы создаете один экземпляр этого класса:

class Foo
{
public:
    Foo(void)
    {
        // do stuff
    }
}

Тогда //do stuff будет выполняться только один раз. Единственный способ выполнить его дважды - создать еще один экземпляр класса.

Вы можете предотвратить это, используя Singleton . В действительности, //do stuff может быть вызван только один раз.

1 голос
/ 21 июля 2009

Я бы хотел защитить эту функцию от многократного случайного вызова

Для меня это звучит как проблема, которая возникает только во время отладки. Если это так, я бы просто сделал следующее:

void InitFoo()
{
    #ifndef NDEBUG
       static bool onlyCalledOnce = TRUE;
       assert(onlyCalledOnce);
       onlyCalledOnce = FALSE;
    #endif

    ...
}

Цель этой конкретной бородавки легко понять, просто взглянув на нее, и она вызовет приятную, большую, броскую ошибку утверждения, если программист когда-либо совершит ошибку, вызывая InitFoo более одного раза. Это также полностью исчезнет в производственном коде. (когда определено NDEBUG).

edit : краткая заметка о мотивации:
Вызов функции init более одного раза, вероятно, является большой ошибкой. Если конечный пользователь этой функции дважды вызвал ее по ошибке, то игнорирование этой ошибки, вероятно, не тот путь. Если вы не пойдете по маршруту assert(), я бы по крайней мере рекомендовал вывести сообщение на stdout или stderr.

1 голос
/ 21 июля 2009

Именно так я и сделаю. Вы можете использовать перетасовку указателя на функцию, если вы хотите альтернативу:

static void InitFoo_impl()
{
    // Do stuff.

    // Next time InitFoo is called, call abort() instead.
    InitFoo = &abort;
}

void (*InitFoo)() = &InitFoo_impl;
0 голосов
/ 21 июля 2009

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

#import <iostream>

class CallOnce {
private:
    bool called;
public:
    CallOnce() {
        called = false;
    }
    void operator()(void) {
        if (called) {
            std::cout << "too many times, pal" <<std::endl;
            return;
        }
        std::cout << "I was called!" << std::endl;
        called = true;
    }

};

int main(void) {
    CallOnce call;

    call();
    call();
}
0 голосов
/ 21 июля 2009

Хммм ... если вы не возражаете против использования Boost , посмотрите на boost :: call_once :

namespace { boost::once_flag foo_init_flag = BOOST_ONCE_INIT; }

void InitFoo() {
    // do stuff here
}

void FooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}

void AnotherFooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}
0 голосов
/ 21 июля 2009

Я делаю именно это все время с ситуациями, которые требуют единовременного, но не стоящего создания целого класса. Конечно, предполагается, что вы не беспокоитесь о проблемах, связанных с потоками. Обычно я ставлю префикс имени переменной "s_", чтобы было ясно, что это статическая переменная.

0 голосов
/ 21 июля 2009

Вам также нужно, чтобы он был многопоточным? Посмотрите на шаблон Singleton с двойной проверкой блокировки (что удивительно легко ошибиться).

Если вам не нужен целый класс для этого, есть еще один простой способ:

В .cpp (не объявляйте InitBlah в .h)

 // don't call this -- called by blahInited initialization
static bool InitBlah() 
{
   // init stuff here
   return true;
}
bool blahInited = InitBlah();

Никто не может вызвать его за пределами этого .cpp, и он вызывается. Конечно, кто-то может назвать это в этом .cpp - зависит от того, насколько вы заботитесь о том, что это невозможно против неудобно и задокументировано.

Если вы заботитесь о заказе или делаете это в определенное время, то, вероятно, Singleton для вас.

...