New: C++0X features!

Rich Booleans now uses two C++0X features.

The Rich Boolean rbREGEXP allows you to check whether a string matches a regular expression. rbHAS_REGEXP lets you check whether a string has a substring that matches the regular expression.

Example:

MOD_ASSERT(rbREGEXP(str,"abc[mn]*xyz"));

Initializer lists can be used to specify a sequence of values in the assertion itself:

std::vector<int> vec;
...
MOD_ASSERT(rbIN_CONTAINER_VALUES(vec, ({ 1, 2, 3 }),
           AllEqual<>()));

ModAssert and Rich Booleans

ModAssert is an advanced open source assertion package for C++. It is very flexible and easy to use. It uses Rich Booleans to create an analysis of what went wrong if an assertion fails. This allows modularization of assertions, separating the behaviour of assertions from what is being tested.

The most basic assertion packages let you write something like

ASSERT(a==foo(b));

which shows a dialog box if the condition fails. Other let you write

ASSERT_EQUAL(a, foo(b));

ModAssert on the other hand lets you write

MOD_ASSERT(rbEQUAL(a, foo(b)));

and a text like `a':<1> == `foo(b)':<2> nok will be shown (generated by the Rich Boolean macro rbEQUAL(a, foo(b)) ) where the expressions are between ` and ', and their values are between < and >. Furthermore it lets the programmer choose what happens when the condition fails: show it in a dialog box, log it to a file, log it to a custom logger or displayer, or any combination of these.

When you use

MOD_ASSERT_P(b, rbEQUAL(a, foo(b)));

the value of b is also shown (the first parameter of the assertion). Other assertions can add a level, a group, an optional action, an action to perform on failure, or a combination of these. Ordinary boolean conditions are also possible with the same assertion macros.

The true power of ModAssert is its modular approach. There are over 90 Rich Booleans, and 144 assertion macros, allowing many combinations, much more than assertion libraries that don't use a modular approach.

It also integrates nicely with UquoniTest, by reporting failed assertions in domain code as a failure (instead of showing a dialog box like other assertion libraries). UquoniTest can even check whether an assertion fails when it should fail (e.g. if you pass a null pointer to a function that should not get a null pointer).

NameLanguageTypeDescriptionLicense
Rich Booleans C++ Library A set of boolean-like expressions, like rbEQUAL(a,b), that hold extra information if they evaluate to false, to inform the user why it evaluated to false, e.g. by showing the values of a and b. wxWindows license
ModAssert C++ Library A package of 144 variations on the ASSERT macro, that can have these rich booleans as an argument, like MOD_ASSERT(rbEQUAL(a,b)), but also ordinary boolean expressions. wxWindows license

Some Rich Booleans

The 9 basic assertion macros

There are 9 basic assertion macros, that can be extended. They are MOD_ASSERT, MOD_VERIFY, MOD_CHECK, MOD_FAIL, MOD_CHECK_FAIL, MOD_VERIFY_V, MOD_VERIFY_B, MOD_CHECK_V and MOD_CHECK_B.

MOD_ASSERT and MOD_VERIFY have one argument, the condition. This can be a boolean expression or a Rich Boolean. A Rich Boolean is preferred, because it gives more information. The difference between them is that MOD_ASSERT is removed entirely by the preprocessor if NDEBUG is defined (e.g. in Release mode in Visual Studio), while MOD_VERIFY still evaluates its argument (but doesn't report if the condition fails). These two are meant for unexpected errors, i.e. errors that are the consequence of a bug in your code.

Example:

#include "modassert/assert.hpp"

...
MOD_ASSERT(rbEQUAL(a,b));

MOD_CHECK has a second argument, an action that should be executed if the condition fails. This is meant for so called expected errors, because they are not the consequence of a bug, but of an incorrect action of the user or some other source, e.g. a user enters an invalid value or a file is read-only. The condition is still evaluated if NDEBUG is defined, and if the condition fails, the action is still executed. Anytime you want error handling, you should use a MOD_CHECK macro, so that all the information about it is displayed and/or logged in the same way.

Example:

MOD_CHECK(rbLESS(n, 100), throw MyException());

MOD_FAIL has no arguments, and is equivalent to MOD_ASSERT(false). It should be used in places where you expect your application can't come.

Example:

for (int i=0; i<n; ++i)
{
    if (a[i]>10)
        return a[i];
}
MOD_FAIL; // at least one should be bigger than 10

MOD_CHECK_FAIL has one argument, the failure action, and is equivalent to MOD_CHECK(false, action). It should be used in places where your application shouldn't come, unless some expected error occurs (as with MOD_CHECK).

Example:

for (int i=0; i<n; ++i)
{
    if (a[i]>10)
        return a[i];
}
// at least one should be bigger than 10
MOD_CHECK_FAIL(throw MyException());

MOD_VERIFY_V is like MOD_VERIFY, but returns a value. If you don't use a Rich Boolean, the returned value is the condition. This is handy for functions that return a pointer that shouldn't be NULL. If you use a Rich Boolean, it returns one of the arguments of the Rich Boolean, usually the first one.

Example:

Widget *widget = MOD_VERIFY_V(CreateWidget());

MOD_VERIFY_B is like MOD_VERIFY_V, but returns a ModAssert::UseBool instead of a value. This is a class of which the objects can be converted to a boolean, so you can use it e.g. as the condition in an if-statement. If you don't convert it to a boolean, an assertion will fail in its destructor. It has transfer semantics, so you can use it as the return value of a function, and leave the checking of the value to the caller of the function. This is handy as a condition in e.g. an if-statement.

This macro is only useful in applications that have to be very safe, that have to try to recover from a bug that is detected.

MOD_CHECK_V is like MOD_CHECK, but returns a value. If you don't use a Rich Boolean, the returned value is the condition. This is handy for functions that return a pointer that shouldn't be NULL. If you use a Rich Boolean, it returns one of the arguments of the Rich Boolean, usually the first one. Unlike MOD_CHECK, the failure action should be an expression on which operator()() can be called.

MOD_CHECK_B is like MOD_CHECK, but doesn't have a failure action and returns a ModAssert::UseBool object. See MOD_VERIFY_B above for info on the ModAssert::UseBool class.

Example:

if (!MOD_CHECK_B(a==10))
{
    ...
}

Note: if you use a Rich Boolean in MOD_VERIFY_V, MOD_VERIFY_B, MOD_CHECK_V or MOD_CHECK_B, you should use one of a different kind, namely one that starts with rbv instead of rb.

Extending the basic macros

The basic macros can be extended by adding suffixes that specify which extra arguments you can give. If you add one or more suffixes, they should be preceded by an underscore. The extra arguments are always before the condition (unlike the failure action of MOD_CHECK).

The suffix P lets you add expressions and messages. You can give more than one by separating them with <<. If you use MOD_VERIFY_V, MOD_CHECK_V or MOD_CHECK_B the parameters should not be separated with << but with commas, and embraced by parentheses.

Example:

MOD_ASSERT_P(a << b << c << d, rbEQUAL(a+b, c+d));
int sum = MOD_VERIFY_VP((a, b, c, d),
    rbEQUAL(a+b, c+d));

In this example, the value of a+b and c+d will be shown because they are the arguments of a Rich Boolean, but a, b, c, and d will also be shown.

You can mix messages and parameters. A message is recognized by ModAssert when it is a literal string, i.e. it's between ".

Example:

MOD_ASSERT_P(
  "nr of spaces + tabs should be less than string size"
  << nrSpaces << nrTabs,
  rbLESS(nrSpaces+nrTabs, str.size()));

The suffix G lets you add a group or a level. A group is an object of type ModAssert::Group<ModAssert::ReportFailure>, ModAssert::Group<ModAssert::ReportAlways>, or ModAssert::Group<ModAssert::ReportNone>, used to respectively let the assertion be displayed and logged when it fails, always, or never. If you use such a group in a number of assertions, you can turn them on or off by just changing the type of the group. If you want more information during debugging, you can even change it to ModAssert::Group<ModAssert::ReportAlways>, but don't forget to change it back if you don't need it anymore. If you want to do this with assertions that don't have a group, you can use the predefined object ModAssert::IfSuccess instead. Groups can be combined with || and &&.

A level can be of type ModAssert::Info, ModAssert::Warning, ModAssert::Error, or ModAssert::Fatal. A level can be added to a group or a combination of groups with %, but only one level can be added. Adding a level is useful because you can control the displaying and logging of assertions per level. By default assertions have the level ModAssert::Error (as you can see in the dialog boxes above).

Example:

ModAssert::Group<ModAssert::ReportAlways> group;
MOD_ASSERT_G(group % ModAssert::Warning,
    rbLESS(foo(a), foo(b)));

The suffix O (capital o, not the number 0) lets you add an optional action. This requires two extra arguments. The first is the action, the second is a description of the optional action that is shown to the user. This is useful to let the user escape from a long loop where a lot of assertions fail (another option is to select 'This assert' in the box labeled 'Stop displaying assertions' in the dialog box, with the difference that the execution of the problematic code will continue). The action is the code itself, unless you use MOD_VERIFY_V, MOD_CHECK_V or MOD_CHECK_B, then it should be an expression on which you can call operator()(), e.g. function that takes no arguments or an object of a class that has operator()().

Example:

for (int i=0; i<10000; ++i)
{
    ...
    MOD_ASSERT_O(return false, "Stop processing",
        rbLESS(a, b));
}

You can also combine these suffixes, in the order P, G, O. Their arguments are in the same order.

Example:

MOD_ASSERT_PGO(a<<b, ModAssert::Fatal, return false, 
         "Stop processing", rbLESS(foo(a), foo(b)));

This gives you a total of 72 assertion macros. Actually there are 72 more that have to do with default parameters (see the documentation), so the total is actually 144. 112 of them can have a Rich Boolean as the condition, of which there are over 90. This gives you over 10000 combinations. No other assertion package offers this. Most assertion packages don't even have 144 macros, even including the ones that perform an analysis, like ASSERT_EQUAL(a,b).