是否“默认”? switch case 会干扰跳转表优化吗?

发布于 2024-10-04 14:15:36 字数 489 浏览 2 评论 0 原文

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

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

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

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

In my code I'm used to write fall-back default cases containing asserts like the following, to guard me against forgetting to update the switch in case semantics change

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

Now I wonder whether the artificial fall-back check default case will interfere with jump table generations? Imagine "ModeA" an "ModeB" etc are consecutive so the compiler could optimize into a table. Since the "default" case contains an actual "return" statement (since the assert will disappear in release mode and the compiler will moan about a missing return statement), it seems unlikely the compiler optimizes the default branch away.

What's the best way to handle this? Some friend recommended me to replace "ADummyValue" with a null pointer dereference, so that the compiler, in presence of undefined behavior, could omit to warn about a missing return statement. Are there better ways to solve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

满意归宿 2024-10-11 14:15:37

使用编译器扩展:

// 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

Use compiler extensions:

// 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
夜雨飘雪 2024-10-11 14:15:36

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

If your compiler is MSVC, you can use __assume intrinsic : http://msdn.microsoft.com/en-us/library/1b3fsfxw(v=VS.80).aspx

↙温凉少女 2024-10-11 14:15:36

至少对于我看过的编译器来说,答案通常是否定的。他们中的大多数人都会将这样的 switch 语句编译为大致相当于以下内容的代码:

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

At least with the compilers I've looked at, the answer is generally no. Most of them will compile a switch statement like this to code roughly equivalent to:

if (mode < modeA || mode > modeLast) {
    assert(0 && "Unknown mode!");
    return ADummyValue();
}
switch(mode) { 
    case modeA: ...;
    case modeB: ...;
    case modeC: ...;
    // ...
    case modeLast: ...;
}
因为看清所以看轻 2024-10-11 14:15:36

如果您使用“默认”(哈!),则该定义无论如何都与 NDEBUG 宏相关联,所以也许只是

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

if you're using "default" (ha!) <assert.h>, the definition's tied to the NDEBUG macro anyway, so maybe just

    case nevermind:
#if !defined(NDEBUG)
    default:
        assert("can" && !"happen");
#endif
    }
千紇 2024-10-11 14:15:36

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

顺便说一句:您是否已经了解过编译器在有默认情况下和没有默认情况下会做什么?

I only see 1 solution in case the optimization actually is disturbed: the infamous "#ifndef NDEBUG" round the default case. Not the nicest trick, but clear in this situation.

BTW: did you already have a look what your compiler does with and without the default case?

甲如呢乙后呢 2024-10-11 14:15:36

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

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

If you have a state that should never be reached, then you should kill the program, because it just reached an unexpected state, even in the release mode (you might just be more diplomatic and actually save users data and do all that other nice stuff before going down).

And please don't obsess over micro optimizations unless you actually have measured (using a profiler) that you need them.

美人如玉 2024-10-11 14:15:36

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

The best way to handle this is not to disable the assert. That way you can also keep an eye on possible bugs. Sometimes it is better for the application to crash with a good message explaining what exactly happened, then to continue working.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文