是否有可能有一个零成本的 assert() 使得在调试和发布构建之间不必修改代码?
Is it possible to have a zero-cost assert() such that code should not have to be modified between debug and release builds?
我注意到有些代码通常是这样的:
#ifdef DEBUG
assert(i == 1);
#endif //DEBUG
而且您的原始代码中可能有几个这样的块。必须写出每个块是乏味和混乱的。
有这样的功能是否合理:
auto debug_assert = [](auto expr) {
#ifdef DEBUG
assert(expr);
#endif //DEBUG
};
或类似这样的内容:
#ifdef DEBUG
auto debug_assert = [](bool expr) {
assert(expr);
};
#else //DEBUG
void debug_assert(bool expr) {}
#endif //DEBUG
在未指定 DEBUG 标志时获得零成本断言? (也就是说,它应该具有与没有将 lambda 运行 等放入代码中相同的效果,并由 g++/clang 编译器优化)。
如@KerrekSB 所述,您已经可以通过在包含 <cassert>
之前定义 NDEBUG
来禁用断言。确保在包含头文件之前定义它的最佳方法是将其列为编译器的参数(对于 gcc,它是 -DNDEBUG
)
注意:assert
通过用空操作表达式替换它来删除,并且在那里,参数根本没有被评估 (这是不同的从你建议的解决方案)!这就是为什么不调用任何在 assert
.
中有副作用的函数至关重要的原因
为了完整性:以下是 assert
的实现方式:
#include <cstdio>
#include <cstdlib>
#ifndef NDEBUG
#define assert(EXPRESSION) ((EXPRESSION) ? (void)0 : (printf("assertion failed at line %d, file %s: %s\n", __LINE__, __FILE__, #EXPRESSION), exit(-1)))
#else
#define assert(EXPRESSION) (void)0
#endif
引入您自己的 assert
风格的宏是很常见的做法。您可能想要这样做的原因有很多:
- 您想包含有关计算表达式的更多信息(请参阅 Catch 的
REQUIRE
以及它们如何使用表达式模板将表达式分解为单个元素并将它们字符串化)
- 您想执行
exit()
程序以外的操作,例如抛出异常、给开发人员发邮件、记录到文件、闯入调试器
- 你甚至想在发布版本上评估表达式,这比根本不评估它更不容易出错(毕竟,如果它没有副作用,它可以通过编译器优化来消除,如果是的,你只是避免了 heisenbug)
- 等等,等等(有想法可以post评论,我会加在答案里)
我注意到有些代码通常是这样的:
#ifdef DEBUG
assert(i == 1);
#endif //DEBUG
而且您的原始代码中可能有几个这样的块。必须写出每个块是乏味和混乱的。
有这样的功能是否合理:
auto debug_assert = [](auto expr) {
#ifdef DEBUG
assert(expr);
#endif //DEBUG
};
或类似这样的内容:
#ifdef DEBUG
auto debug_assert = [](bool expr) {
assert(expr);
};
#else //DEBUG
void debug_assert(bool expr) {}
#endif //DEBUG
在未指定 DEBUG 标志时获得零成本断言? (也就是说,它应该具有与没有将 lambda 运行 等放入代码中相同的效果,并由 g++/clang 编译器优化)。
如@KerrekSB 所述,您已经可以通过在包含 <cassert>
之前定义 NDEBUG
来禁用断言。确保在包含头文件之前定义它的最佳方法是将其列为编译器的参数(对于 gcc,它是 -DNDEBUG
)
注意:assert
通过用空操作表达式替换它来删除,并且在那里,参数根本没有被评估 (这是不同的从你建议的解决方案)!这就是为什么不调用任何在 assert
.
为了完整性:以下是 assert
的实现方式:
#include <cstdio>
#include <cstdlib>
#ifndef NDEBUG
#define assert(EXPRESSION) ((EXPRESSION) ? (void)0 : (printf("assertion failed at line %d, file %s: %s\n", __LINE__, __FILE__, #EXPRESSION), exit(-1)))
#else
#define assert(EXPRESSION) (void)0
#endif
引入您自己的 assert
风格的宏是很常见的做法。您可能想要这样做的原因有很多:
- 您想包含有关计算表达式的更多信息(请参阅 Catch 的
REQUIRE
以及它们如何使用表达式模板将表达式分解为单个元素并将它们字符串化) - 您想执行
exit()
程序以外的操作,例如抛出异常、给开发人员发邮件、记录到文件、闯入调试器 - 你甚至想在发布版本上评估表达式,这比根本不评估它更不容易出错(毕竟,如果它没有副作用,它可以通过编译器优化来消除,如果是的,你只是避免了 heisenbug)
- 等等,等等(有想法可以post评论,我会加在答案里)