Question

J'ai deux fonctions:

vide préparer () et la finition void () qui sera appelée séquentiellement comme:

prepare();
<do something>;
finish(); 
... 
prepare(); 
<do something>; 
finish();

Je veux faire une simple affirmation simplement vérifier qu'ils sont en fait appelé de cette façon et qu'ils ne sont pas appelés en même temps ou hors de l'ordre dans l'application.

Cette application est une application mono-thread. C'est un simple développement / test test de cohérence pour vous assurer que ces fonctions sont appelées en ordre et que, pour une raison quelconque, ils ne sont pas appelés simultanément. En outre, ces affirmations / vérifications de bonne santé devraient être omis à partir du code de production des performances est cruciale!

serait un assert simple () comme ce travail mieux?

int test = 0;

void prepare() {
   assert(++test == 1);

   .
   .
   .
}

void finish() {
    assert(--test == 0);

    .
    .
    .
}
Était-ce utile?

La solution

Votre code est OK, sauf si vous devez autoriser les appels prepare et finish nidification.

Si la nidification n'est pas autorisé, vous pouvez utiliser un bool au lieu d'un int:

bool locked = false;;

void prepare() {
    assert( ! locked );
    locked = true;
    ...
}

void finish() {
    assert( locked );
    locked = false;
    ...
}

Autres conseils

Vous pouvez modifier

int test = 0;

à

#ifndef NDEBUG
int test = 0;
#endif

pour satisfaire votre exigence que « tout code relatif à ce test doit être omis de la production ».

vous voulez probablement:

int test = 0;

void prepare() {
    // enter critical section
    assert(test++ == 0);

    .
    .
    .
    // leave critical section 
}

void finish() {
    // enter critical section
    assert(--test == 0);

    .
    .
    .
    // leave critical section
}

Il y a une condition de course ici. Deux instances simultanées de prepare peut prendre la valeur de test en même temps, alors à la fois incrément dans un registre à la fois obtenir 1, puis faire la comparaison pour obtenir true

Rendre volatile est pas va aider . , Vous devriez plutôt mettre un mutex sur test, comme suit:

boost::mutex mtx;
int test = 0;

void prepare()
{
    boost::mutex::scoped_try_lock lock(&mtx);
    assert(lock.owns_lock());
    assert(test++ == 0);
    // ...
}

void finish()
{
    boost::mutex::scoped_try_lock lock(&mtx);
    assert(lock.owns_lock());
    assert(--test == 0);
}

Si vous mettez <do something>; dans un class vous pouvez réduire la nécessité d'un contrôle du tout:

Il suffit que l'appel constructeur prepare et le finish d'appel destructor. Ensuite, il est appliqué automatiquement on les appelle de façon appropriée.

Notez que la simultanéité et les questions de nidification appliquent encore: Si vous voulez éviter l'imbrication alors vous auriez encore besoin d'une sorte d'état global (? Membre de classe statique) de garder une trace de cela, et si elle est utilisée dans plus d'un fil l'accès à ce compteur devra être protégé mutex.

Notez également que vous pouvez aussi faire operator new/delete privé pour empêcher quelqu'un de créer un sur le tas et non le détruire.

Puisque vous utilisez C ++, pourquoi ne pas utiliser RAII? Vous auriez encore besoin de vérifier pour rentrante, mais RAII de considérablement les choses. Combiné avec larsmans' mutex et élimination de Raedwald dans NDEBUG :

struct Frobber {
  Frobber() {
    assert(mtx.try_lock());
#ifndef NDEBUG
    try {  // in case prepare throws
#endif
      prepare();
#ifndef NDEBUG
    }
    catch (...) {
      mtx.unlock();
      throw;
    }
#endif
  }

  void something();
  // And the other actions that can be performed between preparation and finishing.

  ~Frobber() {
    finish();
#ifndef NDEBUG
    mtx.unlock();
#endif
  }

private:
#ifndef NDEBUG
  static boost::mutex mtx;
#endif

  Frobber(Frobber const&);  // not defined; 0x: = delete
  Frobber& operator=(Frobber const&);  // not defined; 0x: = delete
};
#ifndef NDEBUG
boost::mutex Frobber::mtx;
#endif

void example() {
  Frobber blah;  // instead of prepare()
  blah.something();
  // implicit finish()
}

exemple à l'intérieur, il vous suffit ne peuvent pas faire quelque chose sans avoir d'abord la préparation et la finition sera toujours le cas, même si une exception est levée.

Note Side à propos NDEBUG: si vous utilisez cette façon, assurez-vous qu'il est soit toujours définie ou toujours pas défini dans tous unités de traduction, par opposition à la façon dont il est utilisé pour assert (lui permettant d'être définis et non définis à différents points).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top