自定义 C++ 断言宏

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5252375/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:45:52  来源:igfitidea点击:

Custom C++ assert macro

c++macrosassert

提问by Steven Lu

I stumbled upon an informative article: http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/which pointed out a great number of problems that exist in my current suite of debugging macros.

我偶然发现了一篇内容丰富的文章:http: //cnicholson.net/2009/02/stupid-c-tr​​icks-adventures-in-assert/,它指出了我当前的调试宏套件中存在的大量问题。

The full code for the final version of the macro is given near the end of the article if you follow the link.

如果您点击链接,则在文章末尾附近给出了宏最终版本的完整代码。

The general form as presented is like this (somebody please correct me if i am wrong in transposing it):

呈现的一般形式是这样的(如果我在转置它时有错误,请有人纠正我):

#ifdef DEBUG
#define ASSERT(cond) \  
    do \  
    { \  
        if (!(cond)) \  
        { \  
            ReportFailure(#cond, __FILE__, __LINE__, 0); \
            HALT(); \
        } \  
    } while(0)  
#else  
#define ASSERT(cond) \  
    do { (void)sizeof(cond); } while(0) 

While thinking about modifying my code with what I have learned, I noticed a couple of interesting variations posted in the comments for that article:

在考虑用我学到的知识修改我的代码时,我注意到该文章的评论中发布了一些有趣的变化:

One was that you cannot use this macro with the ternary operator (i.e. cond?ASSERT(x):func()), and the suggestion was to replace the if()with the ternary operator and some parentheses as well as the comma operator. Later on another commenter provided this:

一个是您不能将此宏与三元运算符(即cond?ASSERT(x):func())一起使用,建议将 替换if()为三元运算符和一些括号以及逗号运算符。后来另一位评论者提供了这个:

#ifdef DEBUG
#define ASSERT(x) ((void)(!(x) && assert_handler(#x, __FILE__, __LINE__) && (HALT(), 1)))
#else
#define ASSERT(x) ((void)sizeof(x))
#endif

I thought the use of logical and &&is particularly smart in this case and it seems to me that this version is more flexible than one using the ifor even the ternary ?:. Even nicer is that the return value of assert_handlercan be used to determine if the program should halt. Although I am not sure why it is (HALT(), 1)instead of just HALT().

我认为&&在这种情况下使用逻辑和特别聪明,在我看来,这个版本比使用if甚至三元的版本更灵活?:。更好的是,assert_handler可以使用的返回值来确定程序是否应该停止。虽然我不确定为什么它是(HALT(), 1)而不是HALT().

Are there any particular shortcomings with the second version here that I have overlooked? It does away with the do{ } while(0)wrapped around the macros but it seems to be unnecessary here because we don't need to deal with ifs.

我忽略了这里的第二个版本有什么特别的缺点吗?它消除了do{ } while(0)环绕宏的包装,但在这里似乎没有必要,因为我们不需要处理ifs。

What do you think?

你怎么认为?

回答by AnT

In C and C++ standard library, assertis a macro that is required to act as a function. A part of that requirement is that users must be able to use it in expressions. For example, with standard assertI can do

在 C 和 C++ 标准库中,assert是一个需要充当函数的宏。该要求的一部分是用户必须能够在表达式中使用它。例如,使用标准assert我可以做

int sum = (assert(a > 0), a) + (assert(b < 0), b);

which is functionally the same as

在功能上与

assert(a > 0 && b < 0)
int sum = a + b;

Even though the former might not be a very good way to write an expression, the trick is still very useful in many more appropriate cases.

尽管前者可能不是编写表达式的好方法,但该技巧在许多更合适的情况下仍然非常有用。

This immediately means that if one wants their own custom ASSERTmacro to mimic standard assertbehavior and usability, then using ifor do { } while (0)in the definition of ASSERTis out of question. One is limited to expressions in that case, meaning using either ?:operator or short-circuiting logical operators.

这立即意味着,如果您希望自己的自定义ASSERT宏模仿标准assert行为和可用性,那么在 的定义中使用if或是不可能的。一种仅限于这种情况下的表达式,这意味着使用运算符或短路逻辑运算符。do { } while (0)ASSERT?:

Of course, if one doesn't care about making a standard-like custom ASSERT, then one can use anything, including if. The linked article doesn't even seem to consider this issue, which is rather strange. In my opinion, a function-like assert macro is definitely more useful than a non-function-like one.

当然,如果一个人不关心制作一个标准的 custom ASSERT,那么你可以使用任何东西,包括if. 链接的文章似乎甚至没有考虑这个问题,这很奇怪。在我看来,类似函数的 assert 宏肯定比非类似函数的宏更有用。

As for the (HALT(), 1)... It is done that way because &&operator requires a valid argument. The return value of HALT()might not represent a valid argument for &&. It could be voidfor what I know, which means that a mere HALT()simply won't compile as an argument of &&. The (HALT(), 1)always evaluates to 1and has type int, which is always a valid argument for &&. So, (HALT(), 1)is always a valid argument for &&regardless of the type of HALT().

至于(HALT(), 1)... 这样做是因为&&操作符需要一个有效的参数。的返回值HALT()可能不代表 的有效参数&&。这可能是void我所知道的,这意味着仅仅HALT()不会编译为&&. 该(HALT(), 1)结果始终为1,具有类型int,这始终是一个有效的论据&&。因此,(HALT(), 1)始终是一个有效的参数&&不管类型HALT()

Your last comment about do{ } while(0)does not seem to make much sense. The point of enclosing a macro into do{ } while(0)is to deal with external ifs, not the ifs inside the macro definition. You alwayshave to deal with external ifs, since there's always a chance that your macro will be used in an external if. In the latter definition do{ } while(0)is not needed because that macro is an expression. And being an expression, it already naturally has no problems with external ifs. So, there's no need to do anything about them. Moreover, as I said above, enclosing it into do{ } while(0)would completely defeat its purpose, turning it into a non-expression.

你最后的评论do{ } while(0)似乎没有多大意义。将宏封装到其中的目的do{ } while(0)是处理外部ifs,而不是if宏定义中的s。您总是必须处理 external ifs,因为您的宏总是有可能在 external 中使用if。在后者中do{ } while(0)不需要定义,因为该宏是一个表达式。而且作为一个表达式,已经自然没有外部ifs的问题了。所以,没有必要对它们做任何事情。而且,正如我上面所说的,将它封闭起来do{ } while(0)会完全违背它的目的,将它变成一个非表达式。

回答by Gregory Pakosz

For the sake of completeness, I published a drop-in 2 files assert macro implementation in C++:

为了完整起见,我在 C++ 中发布了一个插入的 2 个文件 assert 宏实现:

#include <pempek_assert.h>

int main()
{
  float min = 0.0f;
  float max = 1.0f;
  float v = 2.0f;
  PEMPEK_ASSERT(v > min && v < max,
                "invalid value: %f, must be between %f and %f", v, min, max);

  return 0;
}

Will prompt you with:

将提示您:

Assertion 'v > min && v < max' failed (DEBUG)
  in file e.cpp, line 8
  function: int main()
  with message: invalid value: 2.000000, must be between 0.000000 and 1.000000

Press (I)gnore / Ignore (F)orever / Ignore (A)ll / (D)ebug / A(b)ort:

Where

在哪里

  • (I)gnore: ignore the current assertion
  • Ignore (F)orever: remember the file and line where the assertion fired and ignore it for the remaining execution of the program
  • Ignore (A)ll: ignore all remaining assertions (all files and lines)
  • (D)ebug: break into the debugger if attached, otherwise abort()(on Windows, the system will prompt the user to attach a debugger)
  • A(b)ort: call abort()immediately
  • (I)gnore:忽略当前断言
  • Ignore (F)orever:记住断言触发的文件和行,并在程序的剩余执行过程中忽略它
  • Ignore (A)ll:忽略所有剩余的断言(所有文件和行)
  • (D)ebug:如果附加了就闯入调试器,否则abort()(在Windows上,系统会提示用户附加调试器)
  • A(b)ort:abort()立即调用

You can find out more about it there:

你可以在那里找到更多关于它的信息:

Hope that helps.

希望有帮助。

回答by Chris Lutz

Although I am not sure why it is (HALT(), 1)instead of just HALT().

虽然我不确定为什么它是(HALT(), 1)而不是HALT().

I imagine HALTmay be a macro (or other stand-in name) for exit. Say we wanted to use exit(1)for our HALTcommand. exitreturns void, which cannot be evaluated as the second argument to &&. If you use the comma operator, which evaluates its first argument, then evaluates and returns the value of it's second argument, we have an integer (1) to return to &&, even though we never reach that point because HALT()will cause us to stop long before then.

我想HALT可能是exit. 假设我们想exit(1)用于我们的HALT命令。exit返回void,不能作为 的第二个参数计算&&。如果您使用逗号运算符,它计算第一个参数,然后计算并返回它的第二个参数的值,我们有一个整数 (1) 返回到&&,即使我们从未到达那个点,因为这HALT()会导致我们很久之前停止然后。

Basically, any function that fills in for HALTis probably going to have a return value of void, because it would make no sense for it to return any value. We couldmake it return an int, just for the sake of the macro, but if we're already hacking around with a macro a little more hackery can't hurt, can it?

基本上,任何填入 for 的函数HALT都可能有一个返回值void,因为它返回任何值都是没有意义的。我们可以让它返回一个int,只是为了宏,但是如果我们已经在用宏来攻击,那么更多的黑客就不会受到伤害,不是吗?