Boost logo

Boost :

From: John Torjo (john_at_[hidden])
Date: 2003-05-30 04:25:50


Hi all,

This is an update of smart_assert - 3rd gear allowed now ;-).
I've attached it; you can also get it from:
www.torjo.com/smart_assert.zip.

Features that were present before (and still are ;-)):
- simple and straightforward usage
- multiple levels of assertions (warn, debug, etc.)
  what used to be called 'critical', is now called 'fatal'.
- logger/ handlers (for different assert levels: warn, debug, etc.)
  settable at runtime.
- SMART_ASSERT_CONTEXT (file, line, function)
- handling simple assertions by default
- using the v_ macro
- custom printing
- SMART_VERIFY
- changing "debug" mode (BOOST_SMART_ASSERT_MODE macro)

Here's what's new:
- printing ranges.
- enforcement of setting logger/handlers.
  (not hardcoded)
- custom allocator
  * not working for gcc.
- thread-safety
- Ignore Forever/Ignore all
- persistence
- multiple configurations.
- loggers_array/ output_to_debug_wnd_win32
- SMART_VERIFY_RET
- examples.

-----------------------------
Details:
- printing ranges.
  You can print ranges, in case an assertion fails, like this:

std::list< int> l;
// ...
std::list< int>::const_iterator first = l.begin(), last = l.end();
// compare the first and last element from the list
SMART_ASSERT( *first < *l.rbegin()) (range_(first,last));

----------------------------
- custom allocator
  * not working for gcc.

Every allocation of memory is done using our own allocators.
Works with VC6. However, strange things happen on gcc 3.2 (so I turned it
off for now).

Does anyone know why this happens? (see smart_assert_alloc.hpp)
Does anyone know of a portable custom allocator?

----------------------------
- thread-safety

If needed, you can turn thread-safety on (it's off by default).
Turning it on is be #defining the 'BOOST_SMART_ASSERT_THREAD_SAFE'
directive.
(in this case, internally, uses <boost/thread/mutex.hpp>)

----------------------------
- Ignore Forever/Ignore all

For handlers.
Besides the default basic handler (basic_ask_user_on_assert), which allows:
"Ignore/Debug/Abort", an extended handler
(ask_user_on_assert_ext()) allows for two more options:

Ignore Forever (ignores a given assertion forever, even if it triggers
again)
Ignore All (ignores all the assertions, no matter what).

----------------------------
- persistence

Ignored assertions can also be persisted.
This works for "Ignore Forever" and "Ignore all".
For instance, if you ignore an assertion "Forever", if it's persisted, it
will be ignore even after closing the program and running it again.

Enabling persistence is very simple:
- just set a 'iostream&', where assertions are to be persisted
  ( persistence().set_persistence_stream( inout); )

Also, a helper class is provided (persist_to_stream).

See multiple configurations below.

----------------------------
- enforcement of setting logger/handlers.
  (not hardcoded)

The default logger and handlers are not hardcoded filenames anymore (for
instance, logging is not done to "./assert.txt" by default).

The default logger outputs to std::cerr, and default handlers are like
before
(the debug asking the user what to do (Ignore/Debug/Abort) uses std::cout
and std::cin).

Enforcing of setting to which file to do the logging is done
by forcing the user to define the 'initializer (see multiple configurations
below).

----------------------------
- multiple configurations.

This is a very cool feature.
A configuration is thought to be a combination of:
- a logger
- the handlers for warn,debug,error,fatal
- persistence - if needed

You can have multiple configurations.
Based on what you decide at runtime, you can run one of the configurations
(which, in turn sets the logger, the handlers, and persistence - if needed).

Default configurations are (you can modify them/ add your own):
"ignore" - Logger: logs assertions to file;
         - Handlers: warn/ debug assertions are ignored (no message is shown
to the user)
                     error/ fatal assertions are treated as default
           You can think of this as SILENTLY LOGGING all failed assertions.

"dbg" - Logger: logs assertions to file;
         - Handlers: warn: default
                     debug: asks user (Ignore/Ignore forever/Ignore
always/Debug/Abort)
                     error/ fatal: default

"dbg_persistent"
         - Logger: logs assertions to file;
         - Handlers: warn: default
                     debug: asks user (Ignore/Ignore forever/Ignore
always/Debug/Abort)
                            IGNORED ASSERTIONS ARE PERSISTED.
                     error/ fatal: default
Also, we have:
"ignore_ext", "dbg_win", "dbg_win_persist" (see the default_initialize
function from smart_assert_cfgarray.hpp)

To grasp the usability of this, just think of the "ignore" configuration.
This is a charm, when you send a debug version to a customer. In case an
assertion occurs, it's silently logged, not bothering the user at all.

**** The initializer.

You realized that some of the above configurations need the programmer to
set:
- the name of file to log assertions to
- the persist file name, in case it's used.

To simplify setting all of these, enforcing the user to set them is done
like this:
Somewhere, I've declared an initializer variable at namespace scope, but
never defined it.
So, if you #include <boost/smart_assert.hpp> and never define this variable,
the code will fail to link.

Once you define this initializer, you pass AT CONSTRUCTION:
- the name of the file to log assertions to
- the persist file name (in case it's used)
- (optional) the configuration to run (if not, set, the default is "dbg")

Defining this is as simple as:

// initialize the smart_assert library
namespace boost { namespace smart_assert {
smart_assert_initializer initializer( "./assert.txt", "./persist.txt",
&default_initialize);
}}

----------------------------
- logger_array/ output_to_debug_wnd_win32

In case you want a failed assertion to be logged to multiple destinations,
this is incredibly easy:
- Create a logger_array ('logger_array a;').
- call 'a.add_logger( dest);' for each destination you want to use it for.
  ('dest' must be a logger functor)
- call 'assert_settings().set_logger( a);'

A logger function that outputs using OutputDebugWindow is also present.

(see examples/ using_logger_array.cpp)
----------------------------
- SMART_VERIFY_RET

A special version of SMART_VERIFY:
- when the SMART_VERIFY fails, and you want to return an error code (instead
of an exception being thrown, as default), use SMART_VERIFY_RET, like this:

// calculates n!
int factorial( int n) {
    // if n too small, return -1
    SMART_VERIFY_RET( n >= 0)(n) .ret_on_fail(-1);
....
}

----------------------------
- examples.

Examples of using SMART_ASSERT/ SMART_VERIFY:

assert_simple.cpp
    - simple examples of using SMART_ASSERT
    - printing ranges
    - using the v_ macro
assert_different_levels.cpp
    - setting different levels of assertion.
persistent_asserts.cpp
    - to get a feel of persistence.
      Try all of the Ignore options!
using_logger_array.cpp
    - using logger_array class, to log to
      multiple places.
      Check out outputting to the Debug Window (on Win32/ Visual C)!!!
verify.cpp
    - shows using SMART_VERIFY, SMART_VERIFY_RET

----------------------------

TODO: minor improvements/ ENFORCE.

Best,
John

--
John Torjo
-- "Practical C++" column writer for builder.com.com
Freelancer, C++ consultant
mailto:john_at_[hidden]



Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk