I'm implementing MY_VERIFY macro to verify values at runtime and terminate if value is bad. Currently I use the following version of MY_VERIFY:
template <typename T>
using DecayLvalueRef =
typename std::conditional_t<
std::is_lvalue_reference_v<T>,
T, typename std::decay_t<T>>;
#define MY_VERIFY(expr, ...) \
[&](auto&& _expr) constexpr noexcept -> DecayLvalueRef<decltype(_expr)> { \
if (!static_cast<bool>(_expr)) { \
printf("" __VA_ARGS__); \
printf("\n"); \
exit(-1); \
} \
return std::forward<decltype(_expr)>(_expr); \
}(expr)
The point is not only to check expr, but also return it if check is ok. And it also works for constexpr. For example:
auto* res = MY_VERIFY(impl.get_ptr(), "nullptr returned");
MY_VERIFY(sizeof(char) == 1, "Bad char size");
It works perfectly well for everything but structured bindings. :(
std::map<std::string, int> mymap;
for (const auto& [key, value] : mymap) {
MY_VERIFY(value != 0, "Bad value for key %s", key.c_str());
doesn't compile because binding key is not captured to lambda closure.
Lambda implicit capture fails with variable declared from structured binding - here is more information.
I tried to solve problem, implementing it as a template function instead of lambda:
template <typename ResultType>
constexpr auto MY_VERIFY(ResultType&& _expr, const char* format, ...) noexcept -> DecayLvalueRef<decltype(_expr)> {
if (!static_cast<bool>(_expr)) {
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("\n");
exit(-1);
}
return std::forward<decltype(_expr)>(_expr);
}
but in this case values for format string are evaluated before expr check, what is bad for performance reasons:
MY_VERIFY(sizeof(char) == 1, "Error value %d", SideEffect().GetValue());
In example above SideEffect instance would be created and GetValue() invoked even when expr is true.
I couldn't find solution, which worked in C++17. Any idea?