如何让Catch2在断言失败时打印对比的C风格字符串内容?
我之前在项目里用Catch2 2.x的时候也遇到过这个糟心的问题——用strcmp做断言失败时,只能看到false的结果,完全不知道实际字符串是什么。结合你的要求(尽量不转std::string、支持非空结尾、代码简洁),给你几个实用的解决方案:
方案1:自定义断言宏(无需转换std::string)
如果不想碰std::string,我们可以自己封装一个宏,利用Catch2的断言接口,在失败时主动打印my_str的内容(为避免越界,我们可以限制打印的最大长度)。
示例代码如下:
#include <cstring> #include <Catch2/catch.hpp> #define CHECK_STRCMP(str, lit, max_print_len) \ do { \ bool cmp_result = !strcmp(str, lit); \ if (!cmp_result) { \ char buf[128] = {0}; // 可根据需求调整缓冲区大小 strncpy(buf, str, max_print_len < sizeof(buf)-1 ? max_print_len : sizeof(buf)-1); Catch::AssertionHandler handler( \ #str " matches " #lit, \ __FILE__, __LINE__, \ Catch::ResultDisposition::Normal \ ); \ handler.handleResult( \ Catch::ResultWas::Failed, \ "String comparison failed", \ Catch::StringRef(buf), \ Catch::StringRef(lit) \ ); \ } \ } while(false)
使用的时候只需要替换原来的CHECK(!strcmp(...)):
CHECK_STRCMP(my_str, "some literal string here", 100);
这个宏会在断言失败时,把my_str的前100个字符(或缓冲区上限)和目标字面量都打印出来,完美适配非空结尾的字符串,也不需要转换std::string。
方案2:用std::string/std::string_view(代码最简洁)
如果你能接受转换为字符串类型,这是最省心的方法——Catch2对std::string的相等断言会自动展开两边的内容。针对非空结尾的情况,我们可以指定字符串长度来构造:
// 如果你知道my_str的准确长度len CHECK(std::string(my_str, len) == "some literal string here"); // 如果不知道长度,想取到第一个空字符或最多N个字符 CHECK(std::string(my_str, strnlen(my_str, 100)) == "some literal string here"); // 或者用C++17的std::string_view,无需拷贝更高效 CHECK(std::string_view(my_str, len) == "some literal string here");
失败时Catch2会直接输出类似这样的信息,一目了然:
/path/to/test_file.cpp:123: FAILED: CHECK( std::string(my_str, len) == "some literal string here" )
with expansion: "actual content from my_str" == "some literal string here"
方案3:升级到Catch2 3.x(长期最优解)
如果你的项目允许升级,Catch2 3.x对字符串断言的支持更友好,尤其是对非空结尾的场景,用std::string_view配合内置断言就能直接得到清晰的失败输出,写法和方案2类似,但底层支持更完善,也不需要担心版本兼容的小问题。
内容的提问来源于stack exchange,提问作者einpoklum

