rollbear / trompeloeil

Header only C++14 mocking framework
Boost Software License 1.0
802 stars 85 forks source link

Lambads inside SIDE_EFFECT #294

Closed eduardvoronkin closed 1 year ago

eduardvoronkin commented 1 year ago

Following code:


#include <catch2/catch_all.hpp>
#include <catch2/trompeloeil.hpp>

class i
{
  public:
    virtual void abs() = 0;
};

class test : public i
{
    MAKE_MOCK0(abs, void());
};
void
f()
{
    test mock;
    REQUIRE_CALL(mock, abs()).SIDE_EFFECT([] {
        CHECK_FALSE(true);
    }());
}

Fails to build with Catch2 v3.1.1 and g++ (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0, but works just fine with Clang.

After investigating preprocessor output of both, it was observed that GCC actually emits pragmas in incorrect place: GCC output:

void
f()
{
    test mock;
    auto trompeloeil_c_call_obj_1 = ::trompeloeil::call_validator_t<decltype((mock).trompeloeil_self_abs())>{(mock)} + ::trompeloeil::detail::conditional_t<false, decltype((mock).abs()), decltype((mock).trompeloeil_tag_abs())> {"test.cpp", static_cast<unsigned long>(20), "mock" "." "abs()"}.abs().

#pragma GCC diagnostic push
# 20 "test.cpp"

#pragma GCC diagnostic ignored "-Wparentheses"
# 20 "test.cpp"

#pragma GCC diagnostic pop
# 20 "test.cpp"
   sideeffect([=](auto& trompeloeil_x) { auto&& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); auto&& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); auto&& _3 = ::trompeloeil::mkarg<3>(trompeloeil_x); auto&& _4 = ::trompeloeil::mkarg<4>(trompeloeil_x); auto&& _5 = ::trompeloeil::mkarg<5>(trompeloeil_x); auto&& _6 = ::trompeloeil::mkarg<6>(trompeloeil_x); auto&& _7 = ::trompeloeil::mkarg<7>(trompeloeil_x); auto&& _8 = ::trompeloeil::mkarg<8>(trompeloeil_x); auto&& _9 = ::trompeloeil::mkarg<9>(trompeloeil_x); auto&&_10 = ::trompeloeil::mkarg<10>(trompeloeil_x); auto&&_11 = ::trompeloeil::mkarg<11>(trompeloeil_x); auto&&_12 = ::trompeloeil::mkarg<12>(trompeloeil_x); auto&&_13 = ::trompeloeil::mkarg<13>(trompeloeil_x); auto&&_14 = ::trompeloeil::mkarg<14>(trompeloeil_x); auto&&_15 = ::trompeloeil::mkarg<15>(trompeloeil_x); ::trompeloeil::ignore(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15); [] { do { (void)__builtin_constant_p(true); Catch::AssertionHandler catchAssertionHandler( "CHECK_FALSE"_catch_sr, ::Catch::SourceLineInfo( "test.cpp", static_cast<std::size_t>( 21 ) ), "true", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest ); try { catchAssertionHandler.handleExpr( Catch::Decomposer() <= true ); } catch(...) { catchAssertionHandler.handleUnexpectedInflightException(); } catchAssertionHandler.complete(); } while( (void)0, (false) && static_cast<const bool&>( !!(true) ) ); }(); })

        ;
}

Clang output:

void
f()
{
    test mock;
    auto trompeloeil_c_call_obj_1 = ::trompeloeil::call_validator_t<decltype((mock).trompeloeil_self_abs())>{(mock)} + ::trompeloeil::detail::conditional_t<false, decltype((mock).abs()), decltype((mock).trompeloeil_tag_abs())> {"test.cpp", static_cast<unsigned long>(20), "mock" "." "abs()"}.abs().sideeffect([=](auto& trompeloeil_x) { auto&& _1 = ::trompeloeil::mkarg<1>(trompeloeil_x); auto&& _2 = ::trompeloeil::mkarg<2>(trompeloeil_x); auto&& _3 = ::trompeloeil::mkarg<3>(trompeloeil_x); auto&& _4 = ::trompeloeil::mkarg<4>(trompeloeil_x); auto&& _5 = ::trompeloeil::mkarg<5>(trompeloeil_x); auto&& _6 = ::trompeloeil::mkarg<6>(trompeloeil_x); auto&& _7 = ::trompeloeil::mkarg<7>(trompeloeil_x); auto&& _8 = ::trompeloeil::mkarg<8>(trompeloeil_x); auto&& _9 = ::trompeloeil::mkarg<9>(trompeloeil_x); auto&&_10 = ::trompeloeil::mkarg<10>(trompeloeil_x); auto&&_11 = ::trompeloeil::mkarg<11>(trompeloeil_x); auto&&_12 = ::trompeloeil::mkarg<12>(trompeloeil_x); auto&&_13 = ::trompeloeil::mkarg<13>(trompeloeil_x); auto&&_14 = ::trompeloeil::mkarg<14>(trompeloeil_x); auto&&_15 = ::trompeloeil::mkarg<15>(trompeloeil_x); ::trompeloeil::ignore(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15); [] { do { (void)__builtin_constant_p(true); Catch::AssertionHandler catchAssertionHandler( "CHECK_FALSE"_catch_sr, ::Catch::SourceLineInfo( "test.cpp", static_cast<std::size_t>( 21 ) ), "true", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest ); try {
# 20 "test.cpp"
#pragma clang diagnostic push
# 20 "test.cpp"
#pragma clang diagnostic ignored "-Wparentheses"
# 20 "test.cpp"
                              catchAssertionHandler.handleExpr( Catch::Decomposer() <= true );
# 20 "test.cpp"
#pragma clang diagnostic pop
# 20 "test.cpp"
                              } catch(...) { catchAssertionHandler.handleUnexpectedInflightException(); } catchAssertionHandler.complete(); } while( (void)0, (false) && static_cast<const bool&>( !!(true) ) ); }(); });

}

The possibility to immediately declare and invoke lambdas inside side_effect seems very convenient to me considering the situation when someone wants to performs complex checks agains mock' input.

eduardvoronkin commented 1 year ago

This works with GCC-12, so I will close.