Простое утверждение для упорядоченного не повторного вызова?

StackOverflow https://stackoverflow.com/questions/4864645

  •  28-10-2019
  •  | 
  •  

Вопрос

У меня две функции:

void prepare () и void finish (), которые будут называться последовательно похожими на:

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

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

Это приложение представляет собой однопоточное приложение. Это простая проверка здравомыслия разработки/тестирования, чтобы убедиться, что эти функции называются в порядке, и что по какой-либо причине их не называют одновременно. Кроме того, эти утверждения/проверки здравомыслия должны быть опущены из производственного кода, поскольку производительность имеет решающее значение!

Будет ли простой Assert (), как эта работа лучше всего?

int test = 0;

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

   .
   .
   .
}

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

    .
    .
    .
}
Это было полезно?

Решение

Ваш код в порядке, если вам не нужно разрешить гнездовать prepare а также finish вызовы

Если гнездование не разрешено, вы можете использовать bool вместо int:

bool locked = false;;

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

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

Другие советы

Вы можете захотеть изменить

int test = 0;

к

#ifndef NDEBUG
int test = 0;
#endif

Чтобы удовлетворить ваше требование, что «любой код, относящийся к этому тесту, должен быть опущен из производства».

Вы, наверное, хотите:

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
}

Здесь есть условие гонки: два одновременных случая prepare может взять ценность test В то же время, тогда оба увеличивают его в регистр, чтобы получить 1, затем сделайте сравнение, чтобы получить true.

Делая это volatile является не помогу. Анкет Вместо этого вы должны надеть мутекс на test, вот так:

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);
}

Если вы положите <do something>; в а class Вы можете сократить потребность в чеке вообще:

Просто попросите конструктора prepare И звонок деструктора finish. Анкет Затем автоматически применяется, что их называют соответствующим образом.

Обратите внимание, что все еще применяются проблемы с параллелизмом и гнездованием: если вы хотите предотвратить гнездо Счетчик должен быть защищен мутекс.

Также обратите внимание, что вы также можете сделать личный operator new/delete Чтобы кто -то не создавал его на куче и не разрушал его.

Поскольку вы используете C ++, почему бы не использовать RAII? Вам все равно нужно проверить использование повторного входа, но Raii значительно упрощает вещи. В сочетании с Larsmans 'Mutex а также Устранение Raedwald's в 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()
}

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

Примечание о ndebug: если вы используете его таким образом, убедитесь, что оно всегда определено или всегда не определен в все Переводные единицы, в отличие от того, как он используется для ASSERT (позволяя определить и не определен в различных точках).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top