I previously asked about function overloading based on whether the arguments are constexpr
. I'm trying to work around the disappointing answer to that question to make a smarter assert function. This is roughly what I am trying to do:
inline void smart_assert (bool condition) {
if (is_constexpr (condition))
static_assert (condition, "Error!!!");
else
assert (condition);
}
Basically, the idea is that a compile-time check is always better than a run-time check if it's possible to check at compile time. However, due to things like inlining and constant folding, I can't always know whether a compile time check is possible. This means that there may be cases where assert (condition)
compiles down to assert(false)
and the code is just waiting for me to run it and execute that path before the I find out there is an error.
Therefore, if there were some way to check whether the condition is a constexpr (due to inlining or other optimizations), I could call static_assert
when possible, and fall back on a run-time assert otherwise. Fortunately, gcc has the intrinsic __builtin_constant_p (exp)
, which returns true if exp
is a constexpr. I don't know if other compilers have this intrinsic, but I was hoping that this would solve my problem. This is the code that I came up with:
#include <cassert>
#undef IS_CONSTEXPR
#if defined __GNUC__
#define IS_CONSTEXPR(exp) __builtin_constant_p (exp)
#else
#define IS_CONSTEXPR(exp) false
#endif
// TODO: Add other compilers
inline void smart_assert (bool const condition) {
static_assert (!IS_CONSTEXPR(condition) or condition, "Error!!!");
if (!IS_CONSTEXPR(condition))
assert (condition);
}
#undef IS_CONSTEXPR
The static_assert
relies on the short circuit behavior of or
. If IS_CONSTEXPR
is true, then static_assert
can be used, and the condition is !true or condition
, which is the same as just condition
. If IS_CONSTEXPR
is false, then static_assert
cannot be used, and the condition is !false or condition
, which is just the same as true
and the static_assert
is ignored. If the static_assert
cannot be checked because condition
is not a constexpr, then I add a run-time assert
to my code as a last-ditch effort. However, this does not work, thanks to not being able to use function arguments in a static_assert
, even if the arguments are constexpr
.
In particular, this is what happens if I try to compile with gcc:
// main.cpp
int main () {
smart_assert (false);
return 0;
}
g++ main.cpp -std=c++0x -O0
Everything is fine, compiles normally. There is no inlining with no optimization, so IS_CONSTEXPR
is false and the static_assert
is ignored, so I just get a run-time assert
statement (that fails). However,
[david@david-desktop test]$ g++ main.cpp -std=c++0x -O1
In file included from main.cpp:1:0:
smart_assert.hpp: In function ‘void smart_assert(bool)’:
smart_assert.hpp:12:3: error: non-constant condition for static assertion
smart_assert.hpp:12:3: error: ‘condition’ is not a constant expression
As soon as I turn on any optimizations and thus potentially allow static_assert
to be triggered, it fails because I cannot use function arguments in the static_assert
. Is there some way to work around this (even if it means implementing my own static_assert
)? I feel my C++ projects could theoretically benefit quite a bit from a smarter assert statement that catches errors as early as possible.
It doesn't seem like making smart_assert
a function-like macro will solve the problem in the general case. It will obviously make it work in this simple example, but condition
may have come from a function two levels up the call graph (but still becomes known to the compiler as a constexpr
due to inlining), which runs into the same problem of using a function parameter in a static_assert
.
constexpr
function. – Aquilegia