Случай переключателя «по умолчанию» мешает оптимизации таблицы переходов? - PullRequest
11 голосов
/ 25 ноября 2010

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

switch(mode) {
case ModeA: ... ;
case ModeB: ... ;
case .. /* many of them ... */
default: {
  assert(0 && "Unknown mode!");
  return ADummyValue();
}
};

Теперь мне интересно,случай искусственной проверки по умолчанию будет мешать генерации таблицы переходов?Представьте, что «ModeA» и «ModeB» и т. Д. Являются последовательными, чтобы компилятор мог оптимизироваться в таблицу.Поскольку в случае «по умолчанию» содержится фактический оператор «возврата» (так как утверждение исчезнет в режиме выпуска, а компилятор будет жаловаться на отсутствующий оператор возврата), маловероятно, что компилятор оптимизирует ветку по умолчанию.

Какой лучший способ справиться с этим?Кто-то из моих друзей порекомендовал мне заменить «ADummyValue» на разыменование нулевого указателя, чтобы компилятор при наличии неопределенного поведения мог пропустить предупреждение о пропущенном операторе возврата.Есть ли лучшие способы решить эту проблему?

Ответы [ 7 ]

3 голосов
/ 25 ноября 2010

По крайней мере, с компиляторами, на которые я смотрел, ответ обычно нет. Большинство из них скомпилирует оператор switch, подобный этому, для кода, примерно эквивалентного:

if (mode < modeA || mode > modeLast) {
    assert(0 && "Unknown mode!");
    return ADummyValue();
}
switch(mode) { 
    case modeA: ...;
    case modeB: ...;
    case modeC: ...;
    // ...
    case modeLast: ...;
}
2 голосов
/ 25 ноября 2010

если вы используете «по умолчанию» (ха!) <assert.h>, определение в любом случае связано с макросом NDEBUG, поэтому, возможно, просто

    case nevermind:
#if !defined(NDEBUG)
    default:
        assert("can" && !"happen");
#endif
    }
2 голосов
/ 25 ноября 2010

Если ваш компилятор MSVC, вы можете использовать __assume intrinsic: http://msdn.microsoft.com/en-us/library/1b3fsfxw(v=VS.80).aspx

1 голос
/ 25 ноября 2010

Лучший способ справиться с этим - не отключать assert. Таким образом, вы также можете следить за возможными ошибками. Иногда лучше завершить работу приложения с хорошим сообщением, объясняющим, что именно произошло, а затем продолжить работу.

1 голос
/ 25 ноября 2010

Если у вас есть состояние, которое никогда не должно быть достигнуто, вы должны убить программу, потому что она только что достигла неожиданного состояния, даже в режиме релиза (вы можете просто быть более дипломатичным и реально сохранять данные пользователей и делать все это другие приятные вещи, прежде чем идти вниз).

И, пожалуйста, не зацикливайтесь на микрооптимизациях, если вы не измерили (используя профилировщик), что они вам нужны.

1 голос
/ 25 ноября 2010

Я вижу только 1 решение в случае, если оптимизация действительно нарушена: печально известный «#ifndef NDEBUG» вокруг случая по умолчанию.Не самая приятная уловка, но понятная в этой ситуации.

Кстати: вы уже видели, что делает ваш компилятор с регистром по умолчанию и без него?

0 голосов
/ 14 января 2011

Использовать расширения компилятора:

// assume.hpp
#pragma once

#if defined _MSC_VER
#define MY_ASSUME(e) (__assume(e), (e) ? void() : void())
#elif defined __GNUC__
#define MY_ASSUME(e) ((e) ? void() : __builtin_unreachable())
#else   // defined __GNUC__
#error unknown compiler
#endif  // defined __GNUC__

-

// assert.hpp
#include <cassert>
#include "assume.hpp"

#undef MY_ASSERT
#ifdef NDEBUG
#define MY_ASSERT MY_ASSUME
#else   // NDEBUG
#define MY_ASSERT assert
#endif  // NDEBUG
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...