web-dev-qa-db-fra.com

Comment exécuter un morceau de code une seule fois?

J'ai une application qui a plusieurs fonctions. Chaque fonction peut être appelée plusieurs fois en fonction des entrées de l'utilisateur. Cependant, je dois exécuter un petit segment du code dans une fonction une seule fois, initialement lorsque l'application est lancée. Lorsque cette même fonction est appelée à nouveau à un moment ultérieur, ce morceau de code particulier ne doit pas être exécuté. Le code est en VC++. Veuillez me dire la manière la plus efficace de gérer cela.

20
Darzen

Utiliser des objets statiques globaux avec des constructeurs (qui sont appelés avant main)? Ou juste dans une routine

static bool initialized;
if (!initialized) {
   initialized = true;
   // do the initialization part
}

Il y a très peu de cas où ce n'est pas assez rapide!


addenda

Dans un contexte multithread, cela pourrait ne pas être suffisant:

Vous pouvez également être intéressé par la fonction pthread_once ou constructor__attribute__ de GCC .

Avec C++ 11, vous voudrez peut-être std :: call_once .

Vous pouvez utiliser <atomic> et peut-être déclarer static volatile std::atomic_bool initialized; (mais vous devez être prudent) si votre fonction peut être appelée à partir de plusieurs threads.

Mais ceux-ci peuvent ne pas être disponibles sur votre système; ils sont disponibles sur Linux!

24

Version compacte utilisant la fonction lambda:

void foo()
{
    static bool once = [](){
        cout << "once" << endl;
        return true;
    } ();
    cout << "foo" << endl;
}

Le code dans la fonction lambda n'est exécuté qu'une seule fois, lorsque la variable statique est initialisée à la valeur de retour de la fonction lambda. Il doit être thread-safe tant que votre compilateur prend en charge l'initialisation statique thread-safe.

23
Bediver

Utilisation de C++ 11 - utilisez le std::call_once

#include <mutex>

std::once_flag onceFlag;

{
    ....
    std::call_once ( onceFlag, [ ]{ /* my code body here runs only once */ } );
    ....
}
21
Soren

Vous pouvez utiliser une variable statique locale:

void foo()
{
     static bool wasExecuted = false;
     if (wasExecuted)
         return;
     wasExecuted = true;

     ...
}
16
Abyx

pourriez-vous faire ça

avoir une fonction qui renvoie un booléen ou un type de données appelé init

Je l'ai fait de cette façon, vous avez besoin d'un bool statique pour que cela se produise

bool init()
{
  cout << "Once " <<endl;
  return true||false;// value isn't matter
}

void functionCall()
{
    static bool somebool = init(); // this line get executed once
    cout << "process " <<endl;
}

int main(int argc, char *argv[])
{
    functionCall();
    functionCall();
    functionCall();

    return EXIT_SUCCESS;
}
5
aah134

En plus de la réponse de @ Basile, vous pouvez utiliser un lambda pour encapsuler la variable statique comme suit:

if ([] {
    static bool is_first_time = true;
    auto was_first_time = is_first_time;
    is_first_time = false;
    return was_first_time; } ()) 
{ 
    // do the initialization part
}

Cela facilite la conversion en macro à usage général:

#define FIRST_TIME_HERE ([] { \
    static bool is_first_time = true; \
    auto was_first_time = is_first_time; \
    is_first_time = false; \
    return was_first_time; } ())

Qui peut être placé où vous voulez appel par besoin :

if (FIRST_TIME_HERE) {
    // do the initialization part
}

Et pour faire bonne mesure, atomics raccourcir l'expression et la rendre thread-safe:

#include <atomic>
#define FIRST_TIME_HERE ([] { \
    static std::atomic<bool> first_time(true); \
    return first_time.exchange(false); } ())
4
John McFarlane

Depuis C++ 11, les variables locales statiques sont thread-safe et généralement suffisantes pour la plupart des cas, donc std::call_once() et al. peut très bien être exagéré.

Cela semble particulièrement élégant lorsque vous utilisez l'initialisation-dans -if et std::exchange() de C++ 17:

#include <utility>

void
do_something_expensive_once()
{
    if ( static auto called = false; !std::exchange(called, true) ) {
        do_something_expensive();
    }
}

S'il s'agit d'un modèle que vous utilisez beaucoup, nous pouvons l'encapsuler via un type de tag:

template <typename T>
auto
call_once()
{
    static auto called = false;
    return !std::exchange(called, true);
}

void
do_something_expensive_once()
{
    struct TagForSomethingExpensive final {};

    if ( call_once<TagForSomethingExpensive>() ) {
        do_something_expensive();
    }
}

Alternativement, vous pouvez créer un modèle sur l'adresse d'une fonction, un entier unique, etc.

Vous pouvez ensuite également passer un appelable à call_once(), et ainsi de suite, etc. Comme d'habitude pour C++: les possibilités sont infinies!

0
underscore_d