Library for compile time checks, header-only, requires at least C++14.
This C++ library was conceived by the need of having a compile-time assert
function (not static_assert
).
If you need something like
constexpr int foo(int i) { chk::cassert(i > 2); return i - 2; }
then this library is for you.
Notice that in the given example it is not possible to use assert
or static_assert
, as the former is not constexpr
, and the latter requires a constexpr
-expression.
chk::cassert
work like a constexpr
function and can take an optional string in two forms:
-
takes a string-like (
\0
terminatedconst char*
,std::string
) object as optional parameter:chk::cassert(i > 2, "fail");
-
the string can be computed lazily only in case of failure:
chk::cassert(i > 2, [](){/* compute string at runtime*/});
The second form is useful in order not to waste time creating an error message if the test does not fail.
Also the expression to verify can be computed lazily through a function: chk::cassert([&i](){return i > 2});
This is useful is the verification is expensive, and because chk::cassert
is not a macro.
If NDEBUG
is not defined, then chk::cassert
compiles to a no-op, but the expression is still computed at the caller site.
If it is expensive, this is undesirable, and maybe it won’t get optimized away, especially if it has side effects like memory allocations.
By using a lambda or normal function, the compiler is able to remove the entire verification as the lambda/function is never called.
Since chk::cassert
is a normal (templated) function, it can be also used in those places where assert
, since it is a macro, is unsuitable, for example as parameter for an algorithm:
std::for_each(begin, end, chk::cassert)
If the expression evaluates to false at compile-time, compilation stops, while at runtime it calls abort just like assert
.
assert
is a debugging tool controlled by NDEBUG
, which can be used to control chk::cassert
too.
But if the check takes place at compile-time, it will still get executed (the motivation behind this decision is that compile-time checks take 0 time at runtime)
Nevertheless there might be situations where it makes sense (reduce compilation times) also the checks at compile-time.
This is controlled by the macro CNDEBUG
.
GCC (and clang) have a very useful function when
-
one wants to take advantage of diagnostics for unreachable code
-
trying to remove useless boilerplate
-
document better some invariant through code
The function is __builtin_unreachable
.
But like assert
, it’s not constexpr
.
There are other utilities, like chk::unreachable
chk::unreachable
-
adds the
constexpr
modifier, in order to use it insideconstexpr
functions. -
has an optional message parameter, to help the developer in case
NDEBUG
is not defined (similarly toassert
) if the code is reached.
constexpr void foo(int i) { if(i > 5){ // } else if(i < 3){ // } else { chk::unreachable("foo should never reach this point"); //message is optional } }
Since it is header-only, there are multiple ways of consuming this library.
Option 1)
Just copy the include
folder in you project somewhere, possibly no adjustment to the build system required
Option 2)
This is a CMake-Project, thus "build" and install the library:
cmake -S <dir of this project> -B <build directory> -DCMAKE_INSTALL_PREFIX:PATH=<optional, where to install the headers and cmake target> cmake --build <build directory> --target install
And in your CMakeLists.txt
add find_package(chk REQUIRED)
for importing chk
as target, and adapt CMAKE_PREFIX_PATH
, for example:
cmake -S <dir of your project> -B <build directory of your project> -DCMAKE_PREFIX_PATH=<installation directory of chk>
Option 3)
Use conan, the project provides a conanfile
conan create <dir of this project> <name of package>
And add to the conanfile of you project
[requires] chk/1.0@<choosen name>
For example usages, just look in the test
directory.