Ответ 1
Если ваш компилятор MSVC, вы можете использовать __assume
intrinsic: http://msdn.microsoft.com/en-us/library/1b3fsfxw(v=VS.80).aspx
В моем коде я использую, чтобы писать случайные случаи по умолчанию, содержащие утверждения вроде следующего, чтобы защитить меня от забывания обновить переключатель в случае изменения семантики
switch(mode) {
case ModeA: ... ;
case ModeB: ... ;
case .. /* many of them ... */
default: {
assert(0 && "Unknown mode!");
return ADummyValue();
}
};
Теперь я задаюсь вопросом, повлияет ли случай стандартного исправления по умолчанию на скачкообразное преобразование? Представьте, что "ModeA" "ModeB" и т.д. Являются последовательными, поэтому компилятор может оптимизировать таблицу. Поскольку "случай по умолчанию" содержит фактический оператор "return" (поскольку assert исчезнет в режиме деблокирования, и компилятор будет стонать о отсутствующем операторе return), кажется маловероятным, что компилятор оптимизирует ветку по умолчанию.
Какой лучший способ справиться с этим? Некоторый друг рекомендовал мне заменить "ADummyValue" на разыменование нулевого указателя, чтобы компилятор, в присутствии поведения undefined, мог опустить, чтобы предупредить о отсутствующем операторе return. Есть ли лучшие способы решить эту проблему?
Если ваш компилятор MSVC, вы можете использовать __assume
intrinsic: http://msdn.microsoft.com/en-us/library/1b3fsfxw(v=VS.80).aspx
По крайней мере, с компиляторами, на которые я смотрел, ответа обычно нет. Большинство из них будут скомпилировать оператор switch, подобный этому, примерно так же, как:
if (mode < modeA || mode > modeLast) {
assert(0 && "Unknown mode!");
return ADummyValue();
}
switch(mode) {
case modeA: ...;
case modeB: ...;
case modeC: ...;
// ...
case modeLast: ...;
}
если вы используете "default" (ha!) <assert.h>
, определение, привязанное к макросу NDEBUG, возможно, просто
case nevermind:
#if !defined(NDEBUG)
default:
assert("can" && !"happen");
#endif
}
Я вижу только 1 решение, если оптимизация фактически нарушена: печально известный "#ifndef NDEBUG" вокруг случая по умолчанию. Не самый приятный трюк, но ясный в этой ситуации.
BTW: вы уже посмотрели, что делает ваш компилятор с и без случая по умолчанию?
Если у вас есть состояние, которое никогда не должно быть достигнуто, вы должны убить программу, потому что она просто достигла неожиданного состояния даже в режиме выпуска (вы можете просто быть более дипломатичным и фактически сохранять данные пользователей и делать все это другие приятные вещи перед спуском).
И, пожалуйста, не зацикливайтесь на микро оптимизации, если вы на самом деле не измерили (используя профайлер), что они вам нужны.
Лучший способ справиться с этим - не отключать assert. Таким образом, вы также можете следить за возможными ошибками. Иногда для приложения лучше срабатывать с хорошим сообщением, объясняющим, что именно произошло, а затем продолжить работу.
Использовать расширения компилятора:
// 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