The uniassert library is a small collection of useful macros. Most of them are designed for assumption checks (and this is a reason behind the library name).
This library was tested on gcc 4.8+, clang 3.5+ and MSVS 2013+, but will probably work on any c++ compiler with c++11 support.
This simple macro does nothing but requires trailing semicolon if is used as single statement.
If you are familiar with assert
from the standard library, BOOST_ASSERT
from boost or Q_ASSERT
from Qt, you already know how to use UNI_ASSERT
.
For all others we recommend to read Wikipedia article
Assertion (software development).
By default UNI_ASSERT
defined as UNI_EMPTY_EXPRESSION
if NDEBUG
is
defined (almost always true for release builds) or equal to assert
otherwise (_ASSERTE
for MSVS in most cases), but this behavior can be changed
(see "Configuration" section).
If UNI_ASSERT
is not defined as UNI_EMPTY_EXPRESSION
then
UNI_ASSERTS_ENABLED
macro is defined. You can use it for generating
additional code for assumption checks (see example below).
#if defined(UNI_ASSERTS_ENABLED)
bool my_complex_check()
{
...
}
#endif
...
UNI_ASSERT(my_complex_check());
...
You can use UNI_ASSERT
in other macros but make sure that the expression is
evaluated only once.
UNI_VERIFY
is similar to BOOST_VERIFY
from boost. It has exactly the same
behavior as UNI_ASSERT
, except that the expression passed to UNI_VERIFY
is
always evaluated.
UNI_VERIFY
uses UNI_ASSERT
but the expression is evaluated only once.
UNI_ENSURE_THROW(condition[, exception_type], message)
UNI_ENSURE_THROW
throws exception if condition
is not equal to true
.
If exception_type
is not specified, std::runtime_error
is thrown. In that
case message
must be a C string, std::string
or any other type that can be
implicitly converted to these types. If exception_type
is specified, it must
be constructable from the message
type.
UNI_ENSURE_THROW
uses UNI_ASSERT
but the expression is evaluated only once.
message
is evaluated only once if condition is false, and not at all if true.
UNI_ENSURE_RETURN(condition[, return_code])
UNI_ENSURE_RETURN
returns return_code
if condition
is not equal to true
.
If UNI_ENSURE_RETURN
is used in a function with void
result type,
return_code
must be omitted.
UNI_ENSURE_RETURN
uses UNI_ASSERT
but the expression is evaluated only once.
return_code
is evaluated only once if condition is false, and not at all if true.
UNI_ENSURE_CONTINUE(condition)
UNI_ENSURE_CONTINUE
skips current iteration of outer loop if condition
is not equal to true
.
UNI_ENSURE_CONTINUE
uses UNI_ASSERT
but the expression is evaluated only once.
UNI_ENSURE_BREAK(condition)
UNI_ENSURE_BREAK
breaks outer loop if condition
is not equal to true
.
UNI_ENSURE_BREAK
uses UNI_ASSERT
but the expression is evaluated only once.
To configure uniassert library add additional defines before including
uniassert.h
. These defines can be added either by passing additional
parameters to a compiler, or by adding them inside your own proxy header (see
example below).
myassert.h
:
#define PARAM VALUE
#include <uniassert.h>
code.cpp
:
#include <myassert.h>
In later examples we will provide code for the second case, but uniassert.h
include will be skipped.
It is possible to call an external function when an assumption check is failed.
If you don't want to change assertion handler in runtime, define
UNI_ENABLE_STATIC_ASSERT_HANDLER
(see below). Otherwise you should define
UNI_ENABLE_DYNAMIC_ASSERT_HANDLER
.
If both UNI_ENABLE_DYNAMIC_ASSERT_HANDLER
and UNI_ASSERTS_ENABLED
are
defined then UNI_DYNAMIC_ASSERT_HANDLER_DEFINED
is defined and you can use
uniassert::set_assert_handler
function to set new assertion handler and get
the previous one.
If no assertion handler is set (by default), UNI_SYSTEM_ASSERT
macro will
be used.
There are two helper classes aimed to be used in unit tests to temporary change
or disable current assertion handler: assert_handler_guard
and
disable_asserts_guard
. These classes are always there if
UNI_ENABLE_DYNAMIC_ASSERT_HANDLER
is defined even when UNI_ASSERTS_ENABLED
is not defined.
It is possible to call an external function when an assumption check is failed.
All you need is to define UNI_ENABLE_STATIC_ASSERT_HANDLER
and implement
uniassert::assertion_failed
function somewhere else:
#if defined(UNI_STATIC_ASSERT_HANDLER_DEFINED)
namespace uniassert
{
void assertion_failed(char const * assertion, char const * file, char const * function, int line)
{
// Something useful
}
} // namespace uniassert
#endif
If UNI_ENABLE_STATIC_ASSERT_HANDLER
and UNI_ASSERTS_ENABLED
are defined and
UNI_ENABLE_DYNAMIC_ASSERT_HANDLER
is not defined then
UNI_STATIC_ASSERT_HANDLER_DEFINED
is defined. You can check it when defining
function uniassert::assertion_failed
(see example above).
The function description passed to uniassert::assertion_failed
is compiler
specific. We use __PRETTY_FUNCTION__
for gcc and clang, __FUNCSIG__
for MSVS
and __func__
in other cases. If you think that the generated function
description is too long (or your compiler has a better macro for that), you can
define your own UNI_FUNCTION
:
#define UNI_FUNCTION COMPILER_SPECIFIC_FUNCTION_NAME
UNI_FUNCTION
is always defined and you can use it in your own code.
By default UNI_ASSERT
is mapped to assert
or _ASSERTE
for MSVS, but one
can change this mapping by defining UNI_SYSTEM_ASSERT
:
#define UNI_SYSTEM_ASSERT YOUR_OWN_ASSERT
If both UNI_SYSTEM_ASSERT
and UNI_ENABLE_STATIC_ASSERT_HANDLER
are defined,
UNI_SYSTEM_ASSERT
will be ignored.
If your UNI_SYSTEM_ASSERT
requires any additional header to be included, you
can also define UNI_SYSTEM_ASSERT_HEADER
:
#define UNI_SYSTEM_ASSERT_HEADER <QtCore/QtGlobal>
#define UNI_SYSTEM_ASSERT Q_ASSERT
You can disable UNI_ASSERT
even for debug builds by defining
UNI_DISABLE_ASSERT
.
If both UNI_DISABLE_ASSERTS
and UNI_ENABLE_STATIC_ASSERT_HANDLER
are
defined, UNI_ENABLE_STATIC_ASSERT_HANDLER
will be ignored.
If both UNI_DISABLE_ASSERTS
and UNI_ENABLE_DYNAMIC_ASSERT_HANDLER
are
defined, UNI_ENABLE_DYNAMIC_ASSERT_HANDLER
will be ignored.
By default UNI_ASSERT
does nothing if NDEBUG
is defined (almost always
true for release builds). This check can be disabled by defining
UNI_FORCE_ASSERTS
. You should probably define either UNI_SYSTEM_ASSERT
,
UNI_ENABLE_DYNAMIC_ASSERT_HANDLER
or UNI_ENABLE_STATIC_ASSERT_HANDLER
,
otherwise internal macro can still do nothing.
If both UNI_FORCE_ASSERT
and UNI_DISABLE_ASSERTS
are defined,
UNI_FORCE_ASSERTS
will be ignored.
Single-line checks are useful even in general code. These macros don't use
UNI_ASSERT
.
UNI_CHECK_THROW(condition[, exception_type], message)
See UNI_ENSURE_THROW
.
UNI_CHECK_RETURN(expr[, return_code])
See UNI_ENSURE_RETURN
.
UNI_CHECK_CONTINUE(expr)
See UNI_ENSURE_CONTINUE
.
UNI_CHECK_BREAK(expr)
See UNI_ENSURE_BREAK
.
See UNI_EMPTY_EXPRESSION
in "Assumption checks" section.
You can suppress warnings for unused variables with the help of UNI_UNUSED
macro:
void func(int variable)
{
UNI_UNUSED(variable);
}
Of course, int /*variable*/
works on any known compiler, but variable can be
used (or not used) based on defined macros and UNI_UNUSED
is a common case
solution.
See UNI_FUNCTION
in "Assumption checks" section.