Description
To facilitate testing at compile-time, Catch::ScopedMessage should have a constexpr constructor that does nothing. Optionally make some other macros constexpr-friendly.
Context
I'm using LLVM's idiom of testing code both at compile time and runtime which looks something like this:
constexpr auto do_test = [] {
INFO("foo");
CHECK(foo(1) == 4);
REQUIRE(foo(2) == 5);
return true;
};
do_test();
STATIC_CHECK(do_test());
Obviously, CHECK and REQUIRE don't support compile-time assertions, but it's trivial to create a wrapper in C++23:
#define FRT_CHECK(...) do { \
if consteval { \
FR_PANIC_CHECK(__VA_ARGS__); \
} \
else { \
CHECK(__VA_ARGS__); \
} \
} while (false)
REQUIRE wrapper looks the same. FR_PANIC_CHECK is just a custom assertion macro that is always enabled and supports constexpr contexts.
The problem is that there is no way to wrap INFO macro. Simply branching by if consteval doesn't work because each branch creates a new scope, so ScopedMessage objects get destroyed before CHECK has a chance to run. I tried to do the following after looking into the INTERNAL_CATCH_INFO (and confirming Hyrum's Law in the process):
#define FRT_INFO(...) \
const auto INTERNAL_CATCH_UNIQUE_NAME(scoped_message) = [&] { \
if consteval { \
return ::Catch::ScopedMessage{}; \
} \
else { \
return ::Catch::ScopedMessage{ \
::Catch::MessageBuilder( \
"INFO"##_catch_sr, \
CATCH_INTERNAL_LINEINFO, \
::Catch::ResultWas::Info \
) << (__VA_ARGS__) \
}; \
} \
}
It doesn't compile because there is no empty constexpr ScopedMessage constructor. Returning just an int instead of ScopedMessage also doesn't work since you can't have different return types in each if consteval branch.
Adding a constexpr constructor to ScopedMessage and classes of its members (MessageInfo, SourceLineInfo, ResultWas::OfType) would allow me to write FRT_INFO. Despite having a std::string member, creating an empty ScopedMessage object should compile as long as no memory allocation actually takes place.
Additional context
If I were to implement this, I would add constructors that accept some tag type:
struct ConstexprInit {
explicit
ConstexprInit() = default;
};
class ScopedMessage {
public:
constexpr ScopedMessage( ConstexprInit ) { }
explicit ScopedMessage( MessageBuilder&& builder );
ScopedMessage( ScopedMessage& duplicate ) = delete;
ScopedMessage( ScopedMessage&& old ) noexcept;
CATCH_INTERNAL_CONSTEXPR_DTOR ~ScopedMessage() {
#if CATCH_INTERNAL_HAS_IF_CONSTEVAL
if !consteval {
#elif CATCH_INTERNAL_HAS_IS_CONSTANT_EVALUATED
if (!std::is_constant_evaluated()) {
#endif
destroyImpl();
#if CATCH_INTERNAL_HAS_IF_CONSTEVAL || CATCH_INTERNAL_HAS_IS_CONSTANT_EVALUATED
}
#endif
}
MessageInfo m_info;
bool m_moved = false;
};
This way, classes don't suddenly become default-constructible. Here is a proof-of-concept at Compiler Explorer. Works in C++11, C++20, and C++23 modes.
I would also appreciate to be able to use other macros at compile time. SECTION is not implementable, but CHECK, REQUIRE, and INFO would cover 90%+ of use cases.
Description
To facilitate testing at compile-time,
Catch::ScopedMessageshould have aconstexprconstructor that does nothing. Optionally make some other macrosconstexpr-friendly.Context
I'm using LLVM's idiom of testing code both at compile time and runtime which looks something like this:
Obviously,
CHECKandREQUIREdon't support compile-time assertions, but it's trivial to create a wrapper in C++23:REQUIREwrapper looks the same.FR_PANIC_CHECKis just a custom assertion macro that is always enabled and supports constexpr contexts.The problem is that there is no way to wrap
INFOmacro. Simply branching byif constevaldoesn't work because each branch creates a new scope, soScopedMessageobjects get destroyed beforeCHECKhas a chance to run. I tried to do the following after looking into theINTERNAL_CATCH_INFO(and confirming Hyrum's Law in the process):It doesn't compile because there is no empty
constexprScopedMessageconstructor. Returning just anintinstead ofScopedMessagealso doesn't work since you can't have different return types in eachif constevalbranch.Adding a
constexprconstructor toScopedMessageand classes of its members (MessageInfo,SourceLineInfo,ResultWas::OfType) would allow me to writeFRT_INFO. Despite having astd::stringmember, creating an emptyScopedMessageobject should compile as long as no memory allocation actually takes place.Additional context
If I were to implement this, I would add constructors that accept some tag type:
This way, classes don't suddenly become default-constructible. Here is a proof-of-concept at Compiler Explorer. Works in C++11, C++20, and C++23 modes.
I would also appreciate to be able to use other macros at compile time.
SECTIONis not implementable, butCHECK,REQUIRE, andINFOwould cover 90%+ of use cases.