C++ 如何在不使用 abort() 的情况下 assert()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37473/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I assert() without using abort()?
提问by wilhelmtell
If I use assert()
and the assertion fails then assert()
will call abort()
, ending the running program abruptly. I can't afford that in my production code. Is there a way to assert in runtime yet be able to catch failed assertions so I have the chance to handle them gracefully?
如果我使用assert()
并且断言失败,那么assert()
将调用abort()
,突然结束正在运行的程序。在我的生产代码中我负担不起。有没有办法在运行时断言但能够捕获失败的断言,以便我有机会优雅地处理它们?
回答by wilhelmtell
Yes, as a matter of fact there is. You will need to write a custom assert function yourself, as C++'s assert()
is exactly C's assert()
, with the abort()
"feature" bundled in. Fortunately, this is surprisingly straightforward.
是的,事实上是有的。您需要自己编写自定义断言函数,因为 C++ 的assert()
正是 C 的assert()
,并且abort()
捆绑了“功能”。幸运的是,这非常简单。
Assert.hh
断言.hh
template <typename X, typename A>
inline void Assert(A assertion)
{
if( !assertion ) throw X();
}
The above function will throw an exception if a predicate doesn't hold. You will then have the chance to catch the exception. If you don't catch the exception, terminate()
will be called, which will end the program similarly to abort()
.
如果谓词不成立,上述函数将抛出异常。然后,您将有机会捕获异常。如果你没有捕捉到异常,terminate()
将会被调用,这将类似于abort()
.
You may wonder what about optimizing away the assertion when we're building for production. In this case, you can define constants that will signify that you're building for production and then refer to the constant when you Assert()
.
您可能想知道在我们为生产构建时优化断言怎么样。在这种情况下,您可以定义表示您正在为生产而构建的常量,然后在Assert()
.
debug.hh
调试.hh
#ifdef NDEBUG
const bool CHECK_WRONG = false;
#else
const bool CHECK_WRONG = true;
#endif
main.cc
主文件
#include<iostream>
struct Wrong { };
int main()
{
try {
Assert<Wrong>(!CHECK_WRONG || 2 + 2 == 5);
std::cout << "I can go to sleep now.\n";
}
catch( Wrong e ) {
std::cerr << "Someone is wrong on the internet!\n";
}
return 0;
}
If CHECK_WRONG
is a constant then the call to Assert()
will be compiled away in production, even if the assertion is not a constant expression. There is a slight disadvantage in that by referring to CHECK_WRONG
we type a little more. But in exchange we gain an advantage in that we can classify various groups of assertions and enable and disable each of them as we see fit. So, for example we could define a group of assertions that we want enabled even in production code, and then define a group of assertions that we only want to see in development builds.
如果CHECK_WRONG
是常量,那么Assert()
即使断言不是常量表达式,对 的调用也将在生产中被编译。有一点点缺点,就是参考CHECK_WRONG
我们打字多一点。但作为交换,我们获得了一个优势,因为我们可以对不同的断言组进行分类,并在我们认为合适的情况下启用和禁用它们中的每一个。因此,例如,我们可以定义一组即使在生产代码中也要启用的断言,然后定义一组我们只想在开发版本中看到的断言。
The Assert()
function is equivalent to typing
该Assert()
功能相当于键入
if( !assertion ) throw X();
but it clearly indicates the intent of the programmer: make an assertion. Assertions are also easier to grep for with this approach, just like plain assert()
s.
但它清楚地表明了程序员的意图:做出断言。使用这种方法,断言也更容易 grep,就像普通的assert()
s 一样。
For more details on this technique see Bjarne Stroustrup's The C++ Programming Language 3e, section 24.3.7.2.
有关此技术的更多详细信息,请参阅 Bjarne Stroustrup 的 The C++ Programming Language 3e,第 24.3.7.2 节。
回答by indiv
glib's error reporting functionstake the approach of continuing after an assert. glib is the underlying platform independence library that Gnome (via GTK) uses. Here's a macro that checks a precondition and prints a stack trace if the precondition fails.
glib 的错误报告函数采用断言后继续的方法。glib 是 Gnome(通过 GTK)使用的底层平台独立库。这是一个检查前提条件并在前提条件失败时打印堆栈跟踪的宏。
#define RETURN_IF_FAIL(expr) do { \
if (!(expr)) \
{ \
fprintf(stderr, \
"file %s: line %d (%s): precondition `%s' failed.", \
__FILE__, \
__LINE__, \
__PRETTY_FUNCTION__, \
#expr); \
print_stack_trace(2); \
return; \
}; } while(0)
#define RETURN_VAL_IF_FAIL(expr, val) do { \
if (!(expr)) \
{ \
fprintf(stderr, \
"file %s: line %d (%s): precondition `%s' failed.", \
__FILE__, \
__LINE__, \
__PRETTY_FUNCTION__, \
#expr); \
print_stack_trace(2); \
return val; \
}; } while(0)
Here's the function that prints the stack trace, written for an environment that uses the gnu toolchain (gcc):
这是为使用 gnu 工具链 (gcc) 的环境编写的打印堆栈跟踪的函数:
void print_stack_trace(int fd)
{
void *array[256];
size_t size;
size = backtrace (array, 256);
backtrace_symbols_fd(array, size, fd);
}
This is how you'd use the macros:
这是您使用宏的方式:
char *doSomething(char *ptr)
{
RETURN_VAL_IF_FAIL(ptr != NULL, NULL); // same as assert(ptr != NULL), but returns NULL if it fails.
if( ptr != NULL ) // Necessary if you want to define the macro only for debug builds
{
...
}
return ptr;
}
void doSomethingElse(char *ptr)
{
RETURN_IF_FAIL(ptr != NULL);
}
回答by Ben Childs
Asserts in C/C++ only run in debug builds. So this won't happen at runtime. In general asserts should mark things that if they happen indicate a bug, and generally show assumptions in your code etc.
C/C++ 中的断言仅在调试版本中运行。所以这不会在运行时发生。一般来说,断言应该标记一些事情,如果它们发生就表明存在错误,并且通常会在您的代码中显示假设等。
If you want to have code that checks for errors at runtime (in release) you should probably use exceptions rather than asserts as these are what they are designed to do. Your answer basically wraps an exception thrower in assert syntax. While this will work, there is no particular advantage to this that I can see over just throwing the exception in the first place.
如果您想要在运行时(在发布中)检查错误的代码,您可能应该使用异常而不是断言,因为这些是它们的设计目的。您的答案基本上用 assert 语法包装了一个异常抛出器。虽然这会起作用,但我可以看到这比首先抛出异常没有特别的优势。
回答by rlerallut
Here's what I have my in "assert.h" (Mac OS 10.4):
这是我在“assert.h”(Mac OS 10.4)中的内容:
#define assert(e) ((void) ((e) ? 0 : __assert (#e, __FILE__, __LINE__)))
#define __assert(e, file, line) ((void)printf ("%s:%u: failed assertion `%s'\n", file, line, e), abort(), 0)
Based on that, replace the call to abort() by a throw( exception ). And instead of printf you can format the string into the exception's error message. In the end, you get something like this:
在此基础上,用 throw( exception ) 替换对 abort() 的调用。您可以将字符串格式化为异常的错误消息,而不是 printf。最后,你会得到这样的东西:
#define assert(e) ((void) ((e) ? 0 : my_assert (#e, __FILE__, __LINE__)))
#define my_assert( e, file, line ) ( throw std::runtime_error(\
std::string(file:)+boost::lexical_cast<std::string>(line)+": failed assertion "+e))
I haven't tried to compile it, but you get the meaning.
我没有尝试编译它,但你明白它的意思。
Note: you'll need to make sure that the "exception" header is always included, as well as boost's (if you decide to use it for formatting the error message). But you can also make "my_assert" a function and only declare its prototype. Something like:
注意:您需要确保始终包含“异常”标头以及 boost 标头(如果您决定使用它来格式化错误消息)。但是你也可以让“my_assert”成为一个函数并且只声明它的原型。就像是:
void my_assert( const char* e, const char* file, int line);
And implement it somewhere where you can freely include all the headers you require.
并在您可以自由包含您需要的所有标头的地方实现它。
Wrap it in some #ifdef DEBUG if you need it, or not if you always want to run those checks.
如果需要,请将其包装在一些 #ifdef DEBUG 中,或者如果您总是想运行这些检查,则不用。
回答by Keith A. Lewis
If you want to throw a character string with information about the assertion: http://xll8.codeplex.com/SourceControl/latest#xll/ensure.h
如果你想抛出一个带有断言信息的字符串:http: //xll8.codeplex.com/SourceControl/latest#xll/ensure.h
回答by najoast
_set_error_mode(_OUT_TO_MSGBOX);
believe me, this function can help you.
相信我,这个功能可以帮到你。