【问题标题】:Add custom messages in assert?在断言中添加自定义消息?
【发布时间】:2010-09-11 22:39:24
【问题描述】:

有没有办法添加或编辑断言抛出的消息?我想使用类似的东西

assert(a == b, "A must be equal to B");

然后,编译器添加linetime等...

有可能吗?

【问题讨论】:

  • 你可以定义一个宏,比如this

标签: c++ assert


【解决方案1】:

我见过的一个技巧是使用&& 运算符。由于如果指针不为空,则“为真”,因此您可以在不改变条件的情况下执行以下操作:

assert(a == b && "A is not equal to B");

由于assert 显示失败的条件,它也会显示您的消息。如果还不够,您可以编写自己的 myAssert 函数或宏来显示您想要的任何内容。

【讨论】:

  • 另一种选择是反转操作数并使用逗号运算符。您需要额外的括号,因此逗号不会被视为参数之间的分隔符:assert(("A must be equal to B", a == b));
  • 如果能够打印变量的值就好了,例如:assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");
  • @Frank, printf 如果打印任何内容,则返回非零值,因此您可以执行assert(a == b && printf("a (%i) is not equal to b (%i)", a, b)) 之类的操作,尽管此时您可能应该编写自己的断言包装器。跨度>
  • 代码错误!我不明白这个!如果 a==b 为假,则 and 表达式也应为假,因此不应计算字符串。
  • @TUIlover,这不是 C 字符串文字的工作方式;它们是编译时常量,它们在这种情况下的使用被简单地优化掉了。没有运行时成本。
【解决方案2】:

另一种选择是反转操作数并使用逗号运算符。您需要额外的括号,因此逗号不会被视为参数之间的分隔符:

assert(("A must be equal to B", a == b));

(这是从上面的 cmets 复制的,为了更好的可见性)

【讨论】:

  • 这是一个很好的方法,有一个小问题,当使用 `-Wunused-value 在 g++ 中编译时会显示“警告:逗号运算符的左操作数无效”
  • 或使用宏:#ifndef m_assert #define m_assert(expr, msg) assert((msg, expr)) #endif
  • 使用宏包装器可以避免 gcc 警告:#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))
【解决方案3】:

这是我的断言宏版本,它接受消息并以清晰的方式打印出所有内容:

#include <iostream>

#ifndef NDEBUG
#   define M_Assert(Expr, Msg) \
    __M_Assert(#Expr, Expr, __FILE__, __LINE__, Msg)
#else
#   define M_Assert(Expr, Msg) ;
#endif

void __M_Assert(const char* expr_str, bool expr, const char* file, int line, const char* msg)
{
    if (!expr)
    {
        std::cerr << "Assert failed:\t" << msg << "\n"
            << "Expected:\t" << expr_str << "\n"
            << "Source:\t\t" << file << ", line " << line << "\n";
        abort();
    }
}

现在,你可以使用它了

M_Assert(ptr != nullptr, "MyFunction: requires non-null argument");

如果失败,您将收到如下消息:

断言失败:MyFunction:需要非空参数

预期:ptr != nullptr

来源:C:\MyProject\src.cpp,第 22 行

又好又干净,您可以随意在您的代码中使用它 =)

【讨论】:

  • 不错的一个。很有用
  • 我有点困惑。 #Expr 是否被视为直接替换的字符串? #Expr 和 Expr 有什么区别?
  • @MinhTran 假设您的断言条件是x == y。然后,Expr 将扩展为if( !(x == y)),这是检查条件的地方,而#Expr 将扩展为字符串文字"x == y",然后我们将其放入错误消息中。
  • 不幸的是,由于使用了保留的标识符,此解决方案会导致未定义的行为。
【解决方案4】:
BOOST_ASSERT_MSG(expre, msg)

http://www.boost.org/doc/libs/1_51_0/libs/utility/assert.html

您可以直接使用它,也可以复制 Boost 的代码。另请注意,Boost 断言只是标题,因此如果您不想安装所有 Boost,则可以只获取该单个文件。

【讨论】:

  • @Jichao,你实现assert接口是什么意思?
【解决方案5】:

由于 zneak 的回答使代码有些复杂,因此更好的方法是仅注释您正在谈论的字符串文本。即:

assert(a == b); // A must be equal to B

由于断言错误的读者会从错误消息中查找文件和行,因此他们将在此处看到完整的解释。

因为,归根结底,这是:

assert(number_of_frames != 0); // Has frames to update

比这更好读:

assert(number_of_frames != 0 && "Has frames to update");

就人类对代码的解析而言,即。可读性。也不是语言黑客。

【讨论】:

  • “既然断言错误的阅读者会从错误消息中查找文件和行,”——只有在他们勤奋的情况下。
  • 只有当他们想修复你的意思的错误时......多么愚蠢的评论
  • 没有。您越容易让人们看到问题,他们就越有可能采取行动。
  • 耸耸肩不同意。
【解决方案6】:

assert 是一个宏/函数组合。您可以使用__FILE____BASE_FILE____LINE__ 等定义自己的宏/函数,并使用您自己的接收自定义消息的函数

【讨论】:

    【解决方案7】:

    如果断言是在一个类中完成的,另一种方法是调用具有自描述名称的静态谓词函数。如果断言失败,则消息将已经包含谓词的漂亮和自描述名称。

    例如:

    static bool arguments_must_be_ordered(int a, int b) {return a <= b;}
    
    void foo(int a, int b)
    {
        assert(arguments_must_be_ordered(a, b));
        // ...
    }
    

    您甚至可能希望公开该谓词函数,以便该类的用户可以自己验证前提条件。

    即使 assert 没有为发布版本禁用,编译器也可能会内联谓词,如果它相当简单的话。

    同样的方法可用于需要注释的复杂if 条件。无需注释,只需调用自描述谓词函数即可。

    【讨论】:

    • 我知道这并不能直接回答这个问题,但它仍然是一种应该考虑的方法。
    【解决方案8】:

    您也可以编写自己的自定义断言函数。一个很简单的例子:

    bool print_if_false(const bool assertion, const char* msg) {
        if(!assertion) {
            // endl to flush
            std::cout << msg << std::endl;
        }
        return assertion;
    }
    
    int main()
    {
        int i = 0;
        int j = 1;
        assert(print_if_false(i == j, "i and j should be equal"));
        return 0;
    }
    

    play with the code.

    断言为Assertion print_if_false(i == j, "i and j should be equal")

    【讨论】:

      【解决方案9】:

      对于vc,在assert.h中添加如下代码,

      #define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )
      

      【讨论】:

      • 修改编译器的头文件是个坏主意。
      猜你喜欢
      • 2016-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-27
      相关资源
      最近更新 更多