11

在我的代码中,我习惯于编写包含如下断言的回退默认案例,以防止我在语义发生变化时忘记更新开关

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

现在我想知道人工回退检查默认情况是否会干扰跳转表生成?想象一下“ModeA”和“ModeB”等是连续的,因此编译器可以优化成一个表。由于“默认”案例包含一个实际的“返回”语句(因为断言将在发布模式下消失并且编译器会抱怨缺少的返回语句),编译器似乎不太可能优化默认分支。

处理这个问题的最佳方法是什么?一些朋友建议我将“ADummyValue”替换为空指针取消引用,以便编译器在存在未定义行为时可以省略对缺少返回语句的警告。有没有更好的方法来解决这个问题?

4

7 回答 7

3

至少对于我看过的编译器,答案通常是否定的。他们中的大多数会编译这样的 switch 语句,代码大致相当于:

if (mode < modeA || mode > modeLast) {
    assert(0 && "Unknown mode!");
    return ADummyValue();
}
switch(mode) { 
    case modeA: ...;
    case modeB: ...;
    case modeC: ...;
    // ...
    case modeLast: ...;
}
于 2010-11-25T16:07:53.317 回答
3

如果您的编译器是 MSVC,您可以使用__assume内在: http: //msdn.microsoft.com/en-us/library/1b3fsfxw (v=VS.80).aspx

于 2010-11-25T16:12:35.857 回答
2

如果您使用的是 "default" (ha!) <assert.h>,那么定义无论如何都与 NDEBUG 宏相关联,所以也许只是

    case nevermind:
#if !defined(NDEBUG)
    default:
        assert("can" && !"happen");
#endif
    }
于 2010-11-25T17:19:48.967 回答
1

如果优化实际上受到干扰,我只看到 1 个解决方案:臭名昭著的“#ifndef NDEBUG”围绕默认情况。不是最好的技巧,但在这种情况下很清楚。

顺便说一句:您是否已经看过您的编译器在使用和不使用默认情况的情况下做了什么?

于 2010-11-25T16:00:51.297 回答
1

如果你有一个永远不应该达到的状态,那么你应该杀死程序,因为它刚刚达到了一个意想不到的状态,即使在发布模式下(你可能只是更外交,实际上保存用户数据并做所有其他好东西下山前)。

除非您实际上已经测量(使用分析器)您需要它们,否则请不要沉迷于微优化。

于 2010-11-25T16:01:13.143 回答
1

处理这个问题的最好方法是不要禁用断言。这样,您还可以密切关注可能的错误。有时,最好让应用程序崩溃并显示一条解释到底发生了什么的好消息,然后继续工作。

于 2010-11-25T16:06:40.323 回答
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
于 2011-01-14T13:00:53.503 回答