2010-11-25 4 views
11

В моем коде я привык писать откате дела по умолчанию, содержащие Утверждает как следующий, чтобы защитить меня от забывая обновлять переключатель в случае семантика изменитьВыполняет ли «по умолчанию» случай переключения оптимизацию таблицы перехода?

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

Теперь интересно ли искусственное fall- back check default case будет вмешиваться в генерации таблиц Jump Table? Представьте, что «ModeA» «ModeB» и т. Д. Являются последовательными, поэтому компилятор может оптимизировать таблицу. Поскольку «случай по умолчанию» содержит фактический оператор «return» (поскольку assert исчезнет в режиме деблокирования, и компилятор будет стонать о отсутствующем операторе return), кажется маловероятным, что компилятор оптимизирует ветку по умолчанию.

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

+0

Учитывая `assert`, это, вероятно, лучше либо` throw` или `terminate` вместо` return`. – 2014-07-11 00:06:15

ответ

1

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

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

+0

Еще не просмотрели сгенерированный код. – 2010-11-25 16:07:23

+0

@ Джоханн: ну, по крайней мере, Джерри Коффин. – stefaanv 2010-11-25 16:24:20

1

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

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

+0

Но я не хочу проверять недопустимое состояние в режиме деблокирования. Мое предположение заключается в том, что код не содержит ошибок. Конечно, это, скорее всего, ложное предположение, но мне кажется, что мне нужно избавиться от проверки недостижимого состояния (и это очень важно для производительности: подпрограмма поиска имен, неявная программа преобразования и т. Д. Времени исполнения скриптового языка). – 2010-11-25 16:04:31

1

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

3

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

if (mode < modeA || mode > modeLast) { 
    assert(0 && "Unknown mode!"); 
    return ADummyValue(); 
} 
switch(mode) { 
    case modeA: ...; 
    case modeB: ...; 
    case modeC: ...; 
    // ... 
    case modeLast: ...; 
} 
+0

Какой позор. Я должен пожертвовать валидацией для скорости :( – 2010-11-25 16:55:22

+0

@Johannes: будет ли массив указателей функций работать так же, как таблицы перехода? Посмотрите на указатель функции из массива и вызовите его. (Таблица перехода состоит из одного прыжка в регистре и одного фиксированного перехода; массив указателя функции был бы одной загрузкой регистра и одним прыжком в регистр, я полагаю?) – rwong 2011-05-26 07:26:50

2

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

case nevermind: 
#if !defined(NDEBUG) 
    default: 
     assert("can" && !"happen"); 
#endif 
    } 
0

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

// 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 
Смежные вопросы