3 ms·
What impresses me is this: The assertion macro is REQUIRE( expression ) [...] Don't worry - lhs and rhs are captured anyway - more on this later. Phil wrote a
by timrobinson 16y ago
What impresses me is this:
The assertion macro is REQUIRE( expression ) [...] Don't worry - lhs and rhs are captured anyway - more on this later.
Phil wrote a bit about this technique -- capturing the two sides of an assertion -- here: http://www.levelofindirection.com/journal/2010/5/21/the-ultimate-c-unit-test-framework.html http://www.levelofindirection.com/journal/2010/5/21/the-ulti...
- ot 16y agoVery interesting trick. Basically when you write something like CHECK(a == b); the expression-capturing part of the code is something like (these are not the actual names) #define CAPTURE(expr) SomeStrangeObject() ->* expr which for a == b expands to something like SomeStrangeObject() ->* a == b Since ->* has highest priority and is seldom overloaded, this is actually parsed by the compiler as (SomeStrangeObject() ->* a) == b The subexpression in parentheses returns an object which wraps a and overloads the comparison operators (==, >, < ...), so that it can capture also the comparison operator used and b. While very convenient it is kind of easy to "break". For example int one = 2; CHECK( one == 1 ); CHECK( (one == 1) ); the two checks are seemingly the same, but in the second case the trick doesn't work: my_test.cpp(6): one == 1 failed for: 2 == 1 my_test.cpp(7): (one == 1) failed for: 0 Also, expressions not in the '<operand> <comparison operator> <operand>' form are not supported, for example CHECK ( one + 1 == 2 ); causes this obscure error message my_test.cpp:10: error: no match for ‘operator+’ in ‘Catch::ResultBuilder(((const char*)"one + 1 == 2"), 0, ((const std::string&)(& std::basic_string<char, std::char_traits<char>, std::allocator<char> >(((const char*)"my_test.cpp"), ((const std::allocator<char>&)((const std::allocator<char>*)(& std::allocator<char>())))))), 10u, ((const std::string&)(& std::basic_string<char, std::char_traits<char>, std::allocator<char> >(((const char*)"CHECK"), ((const std::allocator<char>&)((const std::allocator<char>*)(& std::allocator<char>()))))))).Catch::ResultBuilder::operator->* [with T = int](((const int&)((const int*)(& one)))) + 1’ Same for CHECK( 1 == 1 && one == 1); which fails with my_test.cpp:9: error: no match for ‘operator&&’ in ‘((Catch::ResultBuilder*)Catch::ResultBuilder(((const char*)"1 == 1 && one == 1"), 0, ((const std::string&)(& std::basic_string<char, std::char_traits<char>, std::allocator<char> >(((const char*)"my_test.cpp"), ((const std::allocator<char>&)((const std::allocator<char>*)(& std::allocator<char>())))))), 9u, ((const std::string&)(& std::basic_string<char, std::char_traits<char>, std::allocator<char> >(((const char*)"CHECK"), ((const std::allocator<char>&)((const std::allocator<char>*)(& std::allocator<char>()))))))).Catch::ResultBuilder::operator->* [with T = int](((const int&)((const int*)(&1)))))->Catch::ResultBuilder::operator== [with RhsT = int](((const int&)((const int*)(&1)))) && (one == 1)’ my_test.cpp:9: note: candidates are: operator&&(bool, bool) <built-in> Not sure if these issues can cause real problems in everyday use, but they may cause headaches if one doesn't want to dig into preprocessor/template metaprogramming code.
- phil_nash 16y agoYou make a good point and I have put in some workarounds for this already. I'll probably put more in over time. The trade-off is that I didn't want to over-use expression templates so I have kept it to one level. But my aim is to at least allow arbitrary expressions to compile and correctly report failures. It should also detect when the expression cannot be decomposed and report this in the output so you can rewrite the expression if you do hit it. I have done this for some operators already.
- ot 16y agoI want to say mine are nitpickings anyway, the library is very nice and I share your dislike for complex unit test libraries. I had to use the Boost Testing framework recently and that was a pain. Don't know if it is relevant, but I think you can also work around the parentheses problem using some preprocessor magic. Boost Preprocessor does something similar in its handling of lists, but I haven't been using it for a very long time and I don't remember how it did the trick.