/*
* File: dbg.h
* Author: Pete Goodliffe
* Version: 1.20
* Created: 20 July 2003
*
* Purpose: C++ debugging support library
*
* Copyright (c) Pete Goodliffe 2001-2003 (pete@cthree.org)
*
* This file is modifiable/redistributable under the terms of the GNU
* Lesser General Public License.
*
* You should have recieved a copy of the GNU General Public License along
* with this program; see the file COPYING. If not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 0211-1307, USA.
*/
#ifndef DBG_DBG_H
#define DBG_DBG_H
#include
#include
#ifndef _MSC_VER
#include
#else
// The start of a MSVC compatibility disaster area.
// See the documentation for the dbgclock_t type.
#include
#endif
#if defined(DBG_ENABLED) && defined(NDEBUG)
//#warning DBG_ENABLED defined with NDEBUG which do you want?
#endif
/**
* @libdoc dbg library
*
* The dbg library is a set of C++ utilities to facilitate modern debugging
* idioms.
*
* It has been designed to support defensive programming techniques in modern
* C++ code. It integrates well with standard library usage and has been
* carefully designed to be easy to write, easy to read and very easy to use.
*
* It provides various constraint checking utilities together with an
* integrated error logging facility. These utilities are flexible and
* customisable. They can be enabled and disabled at runtime, and in release
* builds, dbg library use can be compiled away to nothing.
*
* Rich debugging can only be implemented in large code bases from the outset,
* it is hard to retrofit full defensive programming techniques onto existent
* code. For this reason it is good practice to use a library like dbg when
* you start a new project. By using dbg extensively you will find bugs
* quicker, and prevent more insidious problems rearing their head later in
* the project's life.
*
* For instructions on the dbg library's use see the @ref dbg namespace
* documentation.
*/
/**
* The dbg namespace holds a number of C++ debugging utilities.
*
* They allow you to include constraint checking in your code, and provide
* an integrated advanced stream-based logging facility.
*
* The characteristics of this library are:
* @li Easy to use (not overly complex)
* (easy to write, easy to read, easy to use)
* @li Powerful
* @li Configurable
* @li No run time overhead when "compiled out"
* @li Minimises use of the preprocessor
* @li Can throw exceptions if required
* @li Can separate different "sources" of diagnostic output (these
* sources are differentiated by name)
* @li Designed to be a "standard" library
* (integrates with the style of the C++ standard library and works
* well with it)
*
* @sect Enabling debugging
*
* To use dbg in your program you must #include <dbg.h>
* and compile with the DBG_ENABLED
flag set.
*
* If you build without DBG_ENABLED you will have no debugging support (neither
* constraints nor logging). There is no overhead building a program using
* these utilities when DBG_ENABLED is not set. Well, actually there might be
* minimal overhead: there is no overhead when using gcc with a little
* optimisation (-O3
). There is a few bytes overhead with
* optimisation disabled. (The -O1
level leaves almost no
* overhead.)
*
* Either way, the rich debugging support is probably worth a few bytes.
*
* Once your program is running, you will want to enable diagnostic
* levels with @ref dbg::enable, and probably attach an ostream (perhaps
* cerr
) to the diagnostic outputs. See the default states section
* below for information on the initial state of dbg.
*
* Aside:
* The standard assert
macro is an insidious little devil, a lower
* case macro. This library replaces it and builds much richer constraints
* in its place.
* However, because of it, we have to use an API name dbg::assertion,
* not dbg::assert - this makes me really cross, but I can't assume that the
* user does not #include <assert.h>
when using
* <dbg.h>
.
*
* @sect Using constraints
*
* The dbg library constraints are very easy to use. Each debugging utility is
* documented fully to help you understand how they work. Here are some simple
* examples of library use for run-time constraint checking:
*
* void test_dbg()
* {
* dbg::trace trace(DBG_HERE);
*
* int i = 5;
* int *ptr = &i;
*
* dbg::assertion(DBG_ASSERTION(i != 6));
* dbg::check_ptr(ptr, DBG_HERE);
*
* if (i == 5)
* {
* return;
* }
*
* // Shouldn't get here
* dbg::sentinel(DBG_HERE);
* }
*
*
* The constraints provided by dbg are:
* @li @ref dbg::assertion - General purpose assertion
* (a better assert
)
* @li @ref dbg::sentinel - Marker for "shouldn't get here" points
* @li @ref dbg::unimplemented - Marks unimplemented code
* @li @ref dbg::check_ptr - Zero pointer check
* @li @ref dbg::check_bounds - Array bounds checking
* @li @ref dbg::post_mem_fun - Member function post condition
* @li @ref dbg::post - General function post condition
* @li @ref dbg::compile_assertion - Compile time assertion
*
* You can modify constraint behaviour with:
* @li @ref dbg::set_assertion_behaviour - Set how constraints behave
* @li @ref dbg::set_assertion_period - Set up trigger periods
*
* See their individual documentation for further details on usage.
*
* You can specify whether constraints merely report a warning, cause
* an exception to be thrown, or immediately abort the program (see
* @ref dbg::assertion_behaviour).
*
* For assertions that may fire many times in a tight loop, there is the
* facility to time-restrict output (see @ref dbg::set_assertion_period)
*
* @sect Using logging
*
* All the constraint checking shown above integrates with the dbg library
* stream logging mechanisms. These logging facilities are open for your use as
* well.
*
* Here is a simple example of this:
*
* dbg::attach_ostream(dbg::info, cout);
* // now all 'info' messages go to cout
*
* dbg::out(dbg::info) << "This is some info I want to print out\n";
*
* dbg::out(dbg::tracing) << dbg::indent()
* << "This is output at 'tracing' level, indented "
* << "to the same level as the current tracing "
* << "indent.\n";
*
*
* When you build without the DBG_ENABLED flag specified, these logging
* messages will compile out to nothing.
*
* The logging is a very flexible system. You can attach multiple ostreams
* to any dbg output, so you can easily log to a file and log to the console,
* for example. The output can be formatted in a number of different ways to
* suit your needs.
*
* The logging mechanisms provide you with the ability to prepend to all
* diagnostic output a standard prefix (see @ref dbg::set_prefix), and
* also to add the diagnostic level and current time to the prefix (see
* @ref dbg::enable_level_prefix and @ref dbg::enable_time_prefix).
*
* The logging facilities provide by dbg include:
* @li @ref dbg::enable - Enable/disable activity
* @li @ref dbg::out - Returns a diagnostic ostream
* @li @ref dbg::attach_ostream - Attach an ostream to diagnostic output
* @li @ref dbg::detach_ostream - Detach an ostream to diagnostic output
* @li @ref dbg::trace - Trace entry/exit points
*
* The output formatting utilities include:
* @li @ref dbg::set_prefix - Sets the diagnostic output "margin"
* @li @ref dbg::enable_level_prefix - More information in messages
* @li @ref dbg::enable_time_prefix - Prints time in messages
*
* @sect Diagnostic sources
*
* The dbg library allows you to differentiate different "sources" of logging.
*
* Each of the debug utilities has a second form in which you can supply
* a string describing the source of the diagnostic output (see
* @ref dbg::dbg_source). This source may be a different software component, a
* separate file - whatever granularity you like!
*
* If you don't specify a @ref dbg::dbg_source then you are working with the
* ordinary "unnamed" source.
*
* Using these forms you can filter out diagnostics from the different
* parts of your code. Each source can also be attached to a different set of
* streams (logging each component to a separate file, for example). The
* filtering is rich - you can selectively filter each different diagnostic
* @ref dbg::level for each @ref dbg::dbg_source. For example,
*
*
* dbg::enable(dbg::all, "foo-driver", true);
* dbg::enable(dbg::all, "bar-driver", false);
*
* int i = 5;
* dbg::assertion("foo-driver", DBG_ASSERTION(i != 6));
* dbg::assertion("bar-driver", DBG_ASSERTION(i != 6));
*
*
* This will trigger an assertion for the "foo-driver" but not the
* "bar-driver".
*
* There is no requirement to "register" a @ref dbg::dbg_source. The first
* time you use it in any of the dbg APIs, it will be registered with the dbg
* library. It comes into an existence as a copy of the "default"
* debugging source, @ref dbg::default_source.
*
* The default source initially has all debug levels disabled.
* You can change that with this call. Note that this function
* only affects sources created after the call is made.
* Existing sources are unaffected.
*
* If you don't know all of the @ref dbg::dbg_source sources currently
* available, you can blanket enable/disable them with @ref dbg::enable_all.
*
* It can be tedious to specify the @ref dbg_source in every dbg call in
* a source file. For this reason, you can specify the DBG_SOURCE compile
* time macro (wherever you specify DBG_ENABLED). When set, the calls
* automatically receive the source name via the DBG_HERE macro (see
* @ref dbg::source_pos for details). If DBG_SOURCE is supplied but you call
* a dbg API with a specific named @ref dbg_source, this name will override
* the underlying DBG_SOURCE name.
*
* @sect Overloads
*
* Each constraint utility has a number of overloaded forms. This is to make
* using them more convenient. The most rich overload allows you to specify
* a diagnostic @ref dbg::level and a @ref dbg::dbg_source. There are other
* versions that omit one of these parameters, assuming a relevant default.
*
* @sect Default states
*
* When your program first starts up the dbg library has all debugging levels
* switched off. You can enable debugging with @ref dbg::enable. All of the
* possible @ref dbg::dbg_source enables are also all off for all
* levels. You can enable these with @ref dbg::enable, or @ref dbg::enable_all.
*
* Initially, the std::cerr
stream is attached to the
* @ref dbg::error and @ref dbg::fatal diagnostic levels. You can
* attach ostreams to the other diagnostic levels with @ref
* dbg::attach_ostream.
*
* You can modify the "default state" of newly created debug sources. To do
* this use the special @ref dbg::default_source source name in calls to
* @ref dbg::enable, @ref dbg::attach_ostream, and and @ref detach_ostream.
* New sources take the setup from this template source.
*
* All assertion levels are set to @ref dbg::assertions_abort at first, like
* the standard library's assert macro. You can change this behaviour with
* @ref dbg::set_assertion_behaviour. There are no timeout periods set - you
* can change this with @ref dbg::set_assertion_period.
*
* @short Debugging utilities
* @author Pete Goodliffe
* @version 1.20
*/
namespace dbg
{
/**
* This is the version number of the dbg library.
*
* The value is encoded as version * 100. This means that 100 represents
* version 1.00, for example.
*/
const int version = 120;
/**************************************************************************
* Debugging declarations
*************************************************************************/
/**
* The various predefined debugging levels. The dbg API calls use these
* levels as parameters, and allow the user to sift the less interesting
* debugging levels out through @ref dbg::enable.
*
* These levels (and their intended uses) are:
* @li info - Informational, just for interest
* @li warning - For warnings, bad things but recoverable
* @li error - For errors that can't be recovered from
* @li fatal - Errors at this level will cause the dbg library to abort
* program execution, no matter what the
* @ref assertion_behaviour is set to
* @li tracing - Program execution tracing messages
* @li debug - Messages about the state of dbg library, you cannot
* generate messages at this level
* @li none - For APIs that use 'no level specified'
* @li all - Used in @ref enable and @ref attach_ostream to
* specify all levels
*/
enum level
{
info,
warning,
error,
fatal,
tracing,
debug,
none,
all
};
/**
* This enum type describes what happens when a debugging assertion
* fails. The behaviour can be:
* @li assertions_abort - Assertions cause a program abort
* @li assertions_throw - Assertions cause a @ref dbg_exception to
* be thrown
* @li assertions_continue - Assertions cause the standard diagnostic
* printout to occur (the same as the above
* behaviours) but execution continues
* regardless
*
* The dbg library defaults to assertions_abort behaviour, like the
* standard C assert
.
*
* @see dbg::set_assertion_behaviour
*/
enum assertion_behaviour
{
assertions_abort,
assertions_throw,
assertions_continue
};
/**
* typedef for a string that describes the "source" of a diagnostic. If
* you are working on a large project with many small code modules you may
* only want to enable debugging from particular source modules. This
* typedef facilitates this.
*
* Depending on the desired granularity of your dbg sources you will use
* different naming conventions. For example, your dbg_sources might
* be filenames, that way you can switch off all debugging output from
* a particular file quite easily. It might be device driver names,
* component names, library names, or even function names. It's up to you.
*
* If you provide the DBG_SOURCE macro definition at compile time, then
* the DBG_HERE macro includes this source name, differentiating the
* sources for you automatically.
*
* @see dbg::enable(level,dbg_source,bool)
* @see dbg::enable_all
*/
typedef const char * dbg_source;
/**************************************************************************
* source_pos
*************************************************************************/
/**
* Typedef used in the @ref source_pos data structure.
*
* Describes a line number in a source file.
*
* @see dbg::source_pos
*/
typedef const unsigned int line_no_t;
/**
* Typedef used in the @ref source_pos data structure.
*
* Describes a function name in a source file. (Can be zero to
* indicate the function name cannot be ascertained on this compiler).
*
* @see dbg::source_pos
*/
typedef const char * func_name_t;
/**
* Typedef used in the @ref source_pos data structure.
*
* Describes a filename.
*
* @see dbg::source_pos
*/
typedef const char * file_name_t;
/**
* Data structure describing a position in the source file. That is,
* @li The line number
* @li The function name (if the compiler supports this)
* @li The filename
* @li The @ref dbg_soruce specified by DBG_SOURCE compilation
* parameter, if any (otherwise zero)
*
* To create a source_pos for the current position, you can use
* the DBG_HERE convenience macro.
*
* There is an empty constructor that allows you to create a source_pos
* that represents 'no position specified'.
*
* This structure should only be used in dbg library API calls.
*
* You can print a source_pos using the usual stream manipulator syntax.
*/
struct source_pos
{
line_no_t line;
func_name_t func;
file_name_t file;
dbg_source src;
/**
* Creates a source_pos struct. Use the DBG_HERE macro to
* call this constructor conveniently.
*/
source_pos(line_no_t ln, func_name_t fn, file_name_t fl, dbg_source s)
: line(ln), func(fn), file(fl), src(s) {}
/**
* A 'null' source_pos for 'no position specified'
*/
source_pos()
: line(0), func(0), file(0), src(0) {}
};
#ifndef _MSC_VER
/**
* The dbgclock_t typedef is an unfortunate workaround for compatibility
* purposes. One (unnamed) popular compiler platform supplies a
* header file, but this header does NOT place the contents
* into the std namespace.
*
* This typedef is the most elegant work around for that problem. It is
* conditionally set to the appropriate clock_t definition.
*
* In an ideal world this would not exist.
*
* This is the version for sane, standards-compliant platforms.
*/
typedef std::clock_t dbgclock_t;
#else
/**
* See dbgclock_t documentation above. This is the version for broken
* compiler platforms.
*/
typedef clock_t dbgclock_t;
#endif
/**************************************************************************
* Exceptions
*************************************************************************/
/**
* The base type of exception thrown by dbg assertions (and other dbg
* library constraint checks) if the @ref assertion_behaviour is set to
* assertions_throw.
*
* The exception keeps a record of the source position of the trigger
* for this exception.
*/
struct dbg_exception : public std::exception
{
dbg_exception(const source_pos &p) : pos(p) {}
const source_pos pos;
};
/**
* The type of exception thrown by @ref assertion.
*
* @see assertion
*/
struct assertion_exception : public dbg_exception
{
assertion_exception(const source_pos &p) : dbg_exception(p) {}
};
/**
* The type of exception thrown by @ref sentinel.
*
* @see sentinel
*/
struct sentinel_exception : public dbg_exception
{
sentinel_exception(const source_pos &p) : dbg_exception(p) {}
};
/**
* The type of exception thrown by @ref unimplemented.
*
* @see unimplemented
*/
struct unimplemented_exception : public dbg_exception
{
unimplemented_exception(const source_pos &p) : dbg_exception(p) {}
};
/**
* The type of exception thrown by @ref check_ptr.
*
* @see check_ptr
*/
struct check_ptr_exception : public dbg_exception
{
check_ptr_exception(const source_pos &p) : dbg_exception(p) {}
};
#ifdef DBG_ENABLED
/**************************************************************************
* default_source
*************************************************************************/
/**
* The name of a "template" debugging source that provides the default
* state for newly created sources. You can attach and detach logging
* streams here, and enable/disable logging levels.
*
* All source state is copied from the default_source to a new dbg_source.
*
* Whilst you can also use this source for diagnostic purposes this isn't
* it's intention, and it would be confusing to do so.
*
* See @ref dbg_source for discussion on the use of debugging sources in
* dbg.
*
* @see dbg_source
*/
extern dbg_source default_source;
/**************************************************************************
* Debug version of the DBG_HERE macro
*************************************************************************/
/*
* DBG_FUNCTION is defined to be a macro that expands to the name of
* the current function, or zero if the compiler is unable to supply that
* information. It's sad that this wasn't included in the C++ standard
* from the very beginning.
*/
#if defined(__GNUC__)
#define DBG_FUNCTION __FUNCTION__
#else
#define DBG_FUNCTION 0
#endif
#if !defined(DBG_SOURCE)
#define DBG_SOURCE 0
#endif
/*
* Handy macro to generate a @ref source_pos object containing the
* information of the current source line.
*
* @see dbg::source_pos
*/
#define DBG_HERE \
(::dbg::source_pos(__LINE__, DBG_FUNCTION, __FILE__, DBG_SOURCE))
/**************************************************************************
* Enable/disable dbg facilities
*************************************************************************/
/**
* Enables or disables a particular debugging level. The affects dbg
* library calls which don't specify a @ref dbg_source, i.e. from the
* unnamed source.
*
* Enabling affects both constraint checking and diagnostic log output.
*
* If you enable a debugging level twice you only need to disable it once.
*
* All diagnostic output is initially disabled. You can easily enable
* output in your main() thus:
*
* dbg::enable(dbg::all, true);
*
*
* Note that if dbg library calls do specify a @ref dbg_source, or you
* provide a definition for the DBG_SOURCE macro on compilation, then you
* will instead need to enable output for that particular source. Use the
* overloaded version of enable. This version of enable doesn't affect
* these other @ref dbg_source calls.
*
* @param lvl Diagnostic level to enable/disable
* @param enabled true to enable this diagnostic level, false to disable it
* @see dbg::enable_all
* @see dbg::out
* @see dbg::attach_ostream
*/
void enable(level lvl, bool enabled);
/**
* In addition to the above enable function, this overloaded version is
* used when you use dbg APIs with a @ref dbg_source specified. For these
* versions of the APIs no debugging will be performed unless you
* enable it with this API.
*
* To enable debugging for the "foobar" diagnostic source at the info
* level you need to do the following:
*
* dbg::enable(dbg::info, "foobar", true);
*
*
* If you enable a level for a particular @ref dbg_source twice you only
* need to disable it once.
*
* @param lvl Diagnostic level to enable/disable for the @ref dbg_source
* @param src String describing the diagnostic source
* @param enabled true to enable this diagnostic level, false to disable it
* @see dbg::out
*/
void enable(level lvl, dbg_source src, bool enabled);
/**
* You may not know every single @ref dbg_source that is generating
* debugging in a particular code base. However, using this function
* you can enable a diagnostic level for all currently registered sources
* in one fell swoop.
*
* For example,
*
* dbg::enable_all(dbg::all, true);
*
*/
void enable_all(level lvl, bool enabled);
/**************************************************************************
* Logging
*************************************************************************/
/**
* Returns an ostream suitable for sending diagnostic messages to.
* Each diagnostic level has a different logging ostream which can be
* enabled/disabled independently. In addition, each @ref dbg_source
* has separate enables/disables for each diagnostic level.
*
* This overloaded version of out is used when you are creating diagnostics
* that are tied to a particular @ref dbg_source.
*
* It allows you to write code like this:
*
* dbg::out(dbg::info, "foobar") << "The foobar is flaky\n";
*
*
* If you want to prefix your diagnostics with the standard dbg library
* prefix (see @ref set_prefix) then use the @ref prefix or @ref indent
* stream manipulators.
*
* @param lvl Diagnostic level get get ostream for
* @param src String describing the diagnostic source
*/
std::ostream &out(level lvl, dbg_source src);
/**
* Returns an ostream suitable for sending diagnostic messages to.
* Each diagnostic level has a different logging ostream which can be
* enabled/disabled independently.
*
* You use this version of out when you are creating diagnostics
* that aren't tidied to a particular @ref dbg_source.
*
* Each diagnostic @ref dbg_source has a separate set of streams.
* This function returns the stream for the "unnamed" source. Use the
* overload below to obtain the stream for a named source.
*
* It allows you to write code like this:
*
* dbg::out(dbg::info) << "The code is flaky\n";
*
*
* If you want to prefix your diagnostics with the standard dbg library
* prefix (see @ref set_prefix) then use the @ref prefix or @ref indent
* stream manipulators.
*
* @param lvl Diagnostic level get get ostream for
*/
inline std::ostream &out(level lvl)
{
return out(lvl, 0);
}
/**
* Attaches the specified ostream to the given diagnostic level
* for the "unnamed" debug source. Now when diagnostics are produced
* at that level, this ostream will receive a copy.
*
* You can attach multiple ostreams to a diagnostic level. Be careful
* that they don't go to the same place (e.g. cout and cerr both going
* to your console) - this might confuse you!
*
* If you attach a ostream multiple times it will only receive one
* copy of the diagnostics, and you will only need to call
* @ref detach_ostream once.
*
* Remember, don't destroy the ostream without first removing it from
* dbg library, or Bad Things will happen.
*
* @param lvl Diagnostic level
* @param o ostream to attach
* @see dbg::detach_ostream
* @see dbg::detach_all_ostreams
*/
void attach_ostream(level lvl, std::ostream &o);
/**
* Attaches the specified ostream to the given diagnostic level
* for the specified debug source. Otherwise, similar to
* @ref dbg::attach_ostream above.
*
* @param lvl Diagnostic level
* @param src Debug source
* @param o ostream to attach
* @see dbg::detach_ostream
* @see dbg::detach_all_ostreams
*/
void attach_ostream(level lvl, dbg_source src, std::ostream &o);
/**
* Detaches the specified ostream from the given diagnostic level.
*
* If the ostream was not attached then no error is generated.
*
* If you attached the ostream twice, one call to detach_ostream will
* remove it completely.
*
* @param lvl Diagnostic level
* @param o ostream to detach
* @see dbg::attach_ostream
* @see dbg::detach_all_ostreams
*/
void detach_ostream(level lvl, std::ostream &o);
/**
* Detaches the specified ostream from the given diagnostic level
* for the specified debug source. Otherwise, similar to
* @ref dbg::detach_ostream above.
*
* @param lvl Diagnostic level
* @param src Debug source
* @param o ostream to detach
* @see dbg::attach_ostream
* @see dbg::detach_all_ostreams
*/
void detach_ostream(level lvl, dbg_source src, std::ostream &o);
/**
* Detaches all attached ostreams from the specified diagnostic level
* for the "unnamed" diagnostic source.
*
* @param lvl Diagnostic level
* @see dbg::attach_ostream
* @see dbg::detach_ostream
*/
void detach_all_ostreams(level lvl);
/**
* Detaches all attached ostreams from the specified diagnostic level
* for the specified debug source. Otherwise, similar to
* @ref dbg::detach_all_ostreams above.
*
* @param lvl Diagnostic level
* @see dbg::attach_ostream
* @see dbg::detach_ostream
*/
void detach_all_ostreams(level lvl, dbg_source src);
/**
* Convenience function that returns the ostream for the info
* @ref dbg::level for the "unnamed" source.
*
* @see dbg::out
*/
inline std::ostream &info_out()
{
return out(dbg::info);
}
/**
* Convenience function that returns the ostream for the warning
* @ref dbg::level for the "unnamed" source.
*
* @see dbg::out
*/
inline std::ostream &warning_out()
{
return out(dbg::warning);
}
/**
* Convenience function that returns the ostream for the error
* @ref dbg::level for the "unnamed" source.
*
* @see dbg::out
*/
inline std::ostream &error_out()
{
return out(dbg::error);
}
/**
* Convenience function that returns the ostream for the fatal
* @ref dbg::level for the "unnamed" source.
*
* @see dbg::out
*/
inline std::ostream &fatal_out()
{
return out(dbg::fatal);
}
/**
* Convenience function that returns the ostream for the tracing
* @ref dbg::level for the "unnamed" source.
*
* @see dbg::out
*/
inline std::ostream &trace_out()
{
return out(dbg::tracing);
}
/**************************************************************************
* Output formatting
*************************************************************************/
/**
* Sets the debugging prefix - the characters printed before any
* diagnostic output. Defaults to "*** ".
*
* @param prefix New prefix string
* @see dbg::prefix
* @see dbg::enable_level_prefix
* @see dbg::enable_time_prefix
*/
void set_prefix(const char *prefix);
/**
* The dbg library can add to the @ref prefix the name of the used
* diagnostic level (e.g. info, fatal, etc).
*
* By default, this facility is disabled. This function allows you to
* enable the facility.
*
* @param enabled true to enable level prefixing, false to disable
* @see dbg::set_prefix
* @see dbg::enable_time_prefix
*/
void enable_level_prefix(bool enabled);
/**
* The dbg library can add to the @ref prefix the current time. This
* can be useful when debugging systems which remain active for long
* periods of time.
*
* By default, this facility is disabled. This function allows you to
* enable the facility.
*
* The time is produced in the format of the standard library ctime
* function.
*
* @param enabled true to enable time prefixing, false to disable
* @see dbg::set_prefix
* @see dbg::enable_level_prefix
*/
void enable_time_prefix(bool enabled);
/**
* Used so that you can produce a prefix in your diagnostic output in the
* same way that the debugging library does.
*
* You can use it in one of two ways: with or without a diagnostic
* @ref level. For the latter, if level prefixing is enabled (see
* @ref enable_level_prefix) then produces a prefix including the
* specified diagnostic level text.
*
* Examples of use:
*
*
* dbg::out(dbg::info) << dbg::prefix()
* << "A Bad Thing happened\n";
*
* dbg::out(dbg::info) << dbg::prefix(dbg::info)
* << "A Bad Thing happened\n";
*
*
* @see dbg::indent
* @see dbg::set_prefix
* @see dbg::enable_level_prefix
* @see dbg::enable_time_prefix
*/
struct prefix
{
/**
* Creates a prefix with no specified diagnostic @ref level.
* No diagnostic level text will be included in the prefix.
*/
prefix() : l(none) {}
/**
* @param lvl Diagnostic @ref level to include in prefix
*/
prefix(level lvl) : l(lvl) {}
level l;
};
/**
* This is called when you use the @ref prefix stream manipulator.
*
* @internal
* @see dbg::prefix
*/
std::ostream &operator<<(std::ostream &s, const prefix &p);
/**
* Used so that you can indent your diagnostic output to the same level
* as the debugging library. This also produces the @ref prefix output.
*
* Examples of use:
*
*
* dbg::out(dbg::info) << dbg::indent()
* << "A Bad Thing happened\n";
*
* dbg::out(dbg::info) << dbg::indent(dbg::info)
* << "A Bad Thing happened\n";
*
*
* @see dbg::prefix
* @see dbg::set_prefix
* @see dbg::enable_level_prefix
* @see dbg::enable_time_prefix
*/
struct indent
{
/**
* Creates a indent with no specified diagnostic @ref level.
* No diagnostic level text will be included in the @ref prefix part.
*/
indent() : l(none) {}
/**
* @param lvl Diagnostic level to include in prefix
*/
indent(level lvl) : l(lvl) {}
level l;
};
/**
* This is called when you use the @ref indent stream manipulator.
*
* @internal
* @see dbg::indent
*/
std::ostream &operator<<(std::ostream &s, const indent &i);
/**
* This is called when you send a @ref source_pos to a diagnostic output.
* You can use this to easily check the flow of execution in your
* program.
*
* For example,
*
* dbg::out(dbg::tracing) << DBG_HERE << std::endl;
*
*
* Take care that you only send DBG_HERE to the diagnostic outputs
* (obtained with @ref dbg::out) and not "ordinary" streams like
* std::cout
.
*
* In non debug builds, DBG_HERE is a "no-op" doing nothing, and so no
* useful output will be produced on cout.
*
* @internal
* @see dbg::indent
*/
std::ostream &operator<<(std::ostream &s, const source_pos &pos);
/**************************************************************************
* Behaviour
*************************************************************************/
/**
* Sets what happens when assertions (or other constraints) trigger. There
* will always be diagnostic output. Assertions have 'abort' behaviour by
* default - like the ISO C standard, they cause an abort.
*
* If an assertion is encountered at the fatal level, the debugging library
* will abort the program regardless of this behaviour setting.
*
* If a diagnostic level is not enabled (see @ref enable) then the
* @ref assertion_behaviour is not enacted, and no output is produced.
*
* @param lvl Diagnostic level to set behaviour for
* @param behaviour Assertion behaviour
* @see dbg::set_assertion_period
* @see dbg::enable
* @see dbg::assertion
* @see dbg::sentinel
* @see dbg::unimplemented
* @see dbg::check_ptr
*/
void set_assertion_behaviour(level lvl, assertion_behaviour behaviour);
/**
* You may want an assertion to trigger once only and then for subsequent
* calls to remain inactive. For example, if there is an @ref assertion in
* a loop you may not want diagnostics produced for each loop iteration.
*
* To do this, you do the following:
*
* // Prevent several thousand diagnostic print outs
* dbg::set_assertion_period(CLOCKS_PER_SEC);
*
* // Example loop
* int array[LARGE_VALUE];
* put_stuff_in_array(array);
* for(unsigned int n = 0; n < LARGE_VALUE; n++)
* {
* dbg::assertion(DBG_ASSERT(array[n] != 0));
* do_something(array[n]);
* }
*
*
* set_assertion_period forces a certain time period between triggers of a
* particular constraint. The @ref assertion in the example above will only
* be triggered once a second (despite the fact that the constraint
* condition will be broken thousands of times a second). This will not
* affect any other @ref assertion - they will each have their own timeout
* periods.
*
* Setting a period of zero disables any constraint period.
*
* The default behaviour is to have no period.
*
* If a period is set then diagnostic printouts will include the number
* of times each constraint has been triggered (since the period was set).
* Using this, even if diagnostics don't always appear on the attached
* ostreams you have some indication of how often each constraint is
* triggered.
*
* This call only really makes sense if the @ref assertion_behaviour is
* set to @ref assertions_continue.
*
* @param period Time between triggerings of each assertion, or zero to
* disable
* @see dbg::set_assertion_behaviour
* @see dbg::assertion
* @see dbg::sentinel
* @see dbg::unimplemented
* @see dbg::check_ptr
*/
void set_assertion_period(dbgclock_t period);
/**************************************************************************
* Assertion
*************************************************************************/
/**
* Describes an @ref assertion.
*
* This is an internal data structure, you do not need to create it
* directly. Use the DBG_ASSERTION macro to create it.
*
* @internal
* @see dbg::assertion
*/
struct assert_info : public source_pos
{
bool asserted;
const char *text;
/**
* Do not call this directly. Use the DBG_ASSERTION macro.
*
* @internal
*/
assert_info(bool a, const char *t,
line_no_t line, func_name_t func,
file_name_t file, dbg_source spos)
: source_pos(line, func, file, spos), asserted(a), text(t) {}
/**
* Do not call this directly. Use the DBG_ASSERTION macro.
*
* @internal
*/
assert_info(bool a, const char *b, const source_pos &sp)
: source_pos(sp), asserted(a), text(b) {}
};
/*
* Utility macro used by the DBG_ASSERTION macro - it converts a
* macro parameter into a character string.
*/
#define DBG_STRING(a) #a
/*
* Handy macro used by clients of the @ref dbg::assertion function.
* It use is described in the @ref assertion documentation.
*
* @see dbg::assertion
*/
#define DBG_ASSERTION(a) \
::dbg::assert_info(a, DBG_STRING(a), DBG_HERE)
/**
* Used to assert a constraint in your code. Use the DBG_ASSERTION macro
* to generate the third parameter.
*
* This version creates an assertion bound to a particular @ref dbg_source.
*
* The assertion is the most general constraint utility - there are others
* which have more specific purposes (like @ref check_ptr to ensure a
* pointer is non-null). assertion allows you to test any boolean
* expression.
*
* To use assertion for a @ref dbg_source "foobar" you write code like:
*
* int i = 0;
* dbg::assertion(info, "foobar", DBG_ASSERTION(i != 0));
*
*
* If you build with debugging enabled (see @ref dbg) the program will
* produce diagnostic output to the relevant output stream if the
* constraint fails, and the appropriate @ref assertion_behaviour
* is enacted.
*
* Since in non-debug builds the expression in the DBG_ASSERTION macro
* will not be evaluated, it is important that the expression has no
* side effects.
*
* @param lvl Diagnostic level to assert at
* @param src String describing the diagnostic source
* @param ai assert_info structure created with DBG_ASSERTION
*/
void assertion(level lvl, dbg_source src, const assert_info &ai);
/**
* Overloaded version of @ref assertion that is not bound to a particular
* @ref dbg_source.
*
* @param lvl Diagnostic level to assert at
* @param ai assert_info structure created with DBG_ASSERTION
*/
inline void assertion(level lvl, const assert_info &ai)
{
assertion(lvl, 0, ai);
}
/**
* Overloaded version of @ref assertion that defaults to the
* warning @ref level.
*
* @param src String describing the diagnostic source
* @param ai assert_info structure created with DBG_ASSERTION
*/
inline void assertion(dbg_source src, const assert_info &ai)
{
assertion(warning, src, ai);
}
/**
* Overloaded version of @ref assertion that defaults to the
* warning @ref level and is not bound to a particular @ref dbg_source.
*
* @param ai assert_info structure created with DBG_ASSERTION
*/
inline void assertion(const assert_info &ai)
{
assertion(warning, 0, ai);
}
/**************************************************************************
* Sentinel
*************************************************************************/
/**
* You should put this directly after a "should never get here" comment.
*
*
* int i = 5;
* if (i == 5)
* {
* std::cout << "Correct program behaviour\n";
* }
* else
* {
* dbg::sentinel(dbg::error, "foobar", DBG_HERE);
* }
*
*
* @param lvl Diagnostic level to assert at
* @param src String describing the diagnostic source
* @param here Supply DBG_HERE
*/
void sentinel(level lvl, dbg_source src, const source_pos &here);
/**
* Overloaded version of @ref sentinel that is not bound to a particular
* @ref dbg_source.
*
* @param lvl Diagnostic level to assert at
* @param here Supply DBG_HERE
*/
inline void sentinel(level lvl, const source_pos &here)
{
sentinel(lvl, 0, here);
}
/**
* Overloaded version of @ref sentinel that defaults to the warning
* @ref level and is not bound to a particular @ref dbg_source.
*
* @param src String describing the diagnostic source
* @param here Supply DBG_HERE
*/
inline void sentinel(dbg_source src, const source_pos &here)
{
sentinel(warning, src, here);
}
/**
* Overloaded version of @ref sentinel that defaults to the warning
* @ref level and is not bound to a particular @ref dbg_source.
*
* @param here Supply DBG_HERE
*/
inline void sentinel(const source_pos &here)
{
sentinel(warning, 0, here);
}
/**************************************************************************
* Unimplemented
*************************************************************************/
/**
* You should put this directly after a "this has not been implemented
* (yet)" comment.
*
*
* switch (variable)
* {
* ...
* case SOMETHING:
* {
* dbg::unimplemented(dbg::warning, "foobar", DBG_HERE);
* break;
* }
* ...
* }
*
*
* Note the "break;" above - if the @ref assertion_behaviour is non-fatal
* then execution will continue. You wouldn't want unintentional
* fall-through.
*
* @param lvl Diagnostic level to assert at
* @param src String describing the diagnostic source
* @param here Supply DBG_HERE
*
*/
void unimplemented(level lvl, dbg_source src, const source_pos &here);
/**
* Overloaded version of @ref unimplemented that is not bound to a
* particular @ref dbg_source.
*
* @param lvl Diagnostic level to assert at
* @param here Supply DBG_HERE
*/
inline void unimplemented(level lvl, const source_pos &here)
{
unimplemented(lvl, 0, here);
}
/**
* Overloaded version of @ref unimplemented that defaults to the
* warning @ref level.
*
* @param src String describing the diagnostic source
* @param here Supply DBG_HERE
*/
inline void unimplemented(dbg_source src, const source_pos &here)
{
unimplemented(warning, src, here);
}
/**
* Overloaded version of @ref unimplemented that defaults to the
* warning @ref level and is not bound to a particular @ref dbg_source.
*
* @param here Supply DBG_HERE
*/
inline void unimplemented(const source_pos &here)
{
unimplemented(warning, 0, here);
}
/**************************************************************************
* Pointer checking
*************************************************************************/
/**
* A diagnostic function to assert that a pointer is not zero.
*
* To use it you write code like:
*
* void *p = 0;
* dbg::check_ptr(dbg::info, "foobar", p, DBG_HERE);
*
*
* It's better to use this than a general purpose @ref assertion. It
* reads far more intuitively in your code.
*
* @param lvl Diagnostic level to assert at
* @param src String describing the diagnostic source
* @param p Pointer to check
* @param here Supply DBG_HERE
*/
void check_ptr(level lvl, dbg_source src, const void *p,
const source_pos &here);
/**
* Overloaded version of @ref check_ptr that is not bound to a particular
* @ref dbg_source.
*
* @param lvl Diagnostic level to assert at
* @param p Pointer to check
* @param here Supply DBG_HERE
*/
inline void check_ptr(level lvl, const void *p, const source_pos &here)
{
check_ptr(lvl, 0, p, here);
}
/**
* Overloaded version of @ref check_ptr that defaults to the
* warning @ref level.
*
* @param src String describing the diagnostic source
* @param p Pointer to check
* @param here Supply DBG_HERE
*/
inline void check_ptr(dbg_source src, const void *p, const source_pos &here)
{
check_ptr(warning, src, p, here);
}
/**
* Overloaded version of @ref check_ptr that defaults to the
* warning @ref level and is not bound to a particular @ref dbg_source.
*
* @param p Pointer to check
* @param here Supply DBG_HERE
*/
inline void check_ptr(const void *p, const source_pos &here)
{
check_ptr(warning, 0, p, here);
}
/**************************************************************************
* Bounds checking
*************************************************************************/
/**
* Utility that determines the number of elements in an array. Used
* by the @ref check_bounds constraint utility function.
*
* This is not available in non-debug versions, so do not use it
* directly.
*
* @param array Array to determine size of
* @return The number of elements in the array
* @internal
*/
template
inline unsigned int array_size(T &array)
{
return sizeof(array)/sizeof(array[0]);
}
/**
* A diagnostic function to assert that an array access is not out
* of bounds.
*
* You probably want to use the more convenient check_bounds versions
* below if you are accessing an array whose definition is in scope -
* the compiler will then safely determine the size of the array for you.
*
* @param lvl Diagnostic level to assert at
* @param src String describing the diagnostic source
* @param index Test index
* @param bound Boundary value (index must be < bound, and >= 0)
* @param here Supply DBG_HERE
*/
void check_bounds(level lvl, dbg_source src,
int index, int bound, const source_pos &here);
/**
* A diagnostic function to assert that an array access is not out
* of bounds. With this version you can specify the minimum and maximum
* bound value.
*
* You probably want to use the more convenient check_bounds version
* below if you are accessing an array whose definition is in scope -
* the compiler will then safely determine the size of the array for you.
*
* @param lvl Diagnostic level to assert at
* @param src String describing the diagnostic source
* @param index Test index
* @param minbound Minimum bound (index must be >= minbound
* @param maxbound Minimum bound (index must be < maxbound)
* @param here Supply DBG_HERE
*/
inline void check_bounds(level lvl, dbg_source src,
int index, int minbound, int maxbound,
const source_pos &here)
{
check_bounds(lvl, src, index-minbound, maxbound, here);
}
/**
* Overloaded version of check_bounds that can automatically determine the
* size of an array if it within the current scope.
*
* You use it like this:
*
* int a[10];
* int index = 10;
* dbg::check_bounds(dbg::error, index, a, DBG_HERE);
* a[index] = 5;
*
*
* @param lvl Diagnostic level to assert at
* @param src String describing the diagnostic source
* @param index Test index
* @param array Array index is applied to
* @param here Supply DBG_HERE
*/
template
void check_bounds(level lvl, dbg_source src,
int index, T &array, const source_pos &here)
{
check_bounds(lvl, src, index, array_size(array), here);
}
/**
* Overloaded version of @ref check_bounds that is not bound to a
* particular @ref dbg_source.
*
* @param lvl Diagnostic level to assert at
* @param index Test index
* @param array Array index is applied to
* @param here Supply DBG_HERE
*/
template
void check_bounds(level lvl, int index, T &array, const source_pos &here)
{
check_bounds(lvl, 0, index, array_size(array), here);
}
/**
* Overloaded version of @ref check_bounds that defaults to the
* warning @ref level.
*
* @param src String describing the diagnostic source
* @param index Test index
* @param array Array index is applied to
* @param here Supply DBG_HERE
*/
template
void check_bounds(dbg_source src, int index, T &array,
const source_pos &here)
{
check_bounds(warning, src, index, array_size(array), here);
}
/**
* Overloaded version of @ref check_bounds that defaults to the
* warning @ref level and is not bound to a particular @ref dbg_source.
*
* @param index Test index
* @param array Array index is applied to
* @param here Supply DBG_HERE
*/
template
void check_bounds(int index, T &array, const source_pos &here)
{
check_bounds(warning, 0, index, array_size(array), here);
}
/**************************************************************************
* Tracing
*************************************************************************/
/**
* The trace class allows you to easily produce tracing diagnostics.
*
* When the ctor is called, it prints "->" and the name of the
* function, increasing the indent level. When the object is deleted
* it prints "<-" followed again by the name of the function.
*
* You can use the name of the current function gathered via the
* DBG_HERE macro, or some other tracing string you supply.
*
* Diagnostics are produced at the tracing @ref level.
*
* For example, if you write the following code:
*
*
* void foo()
* {
* dbg::trace t1(DBG_HERE);
* // do some stuff
* {
* dbg::trace t2("sub block");
* // do some stuff
* dbg::out(tracing) << dbg::prefix() << "Hello!\n";
* }
* dbg::out(tracing) << dbg::prefix() << "Hello again!\n";
* // more stuff
* }
*
*
* You will get the following tracing information:
*
*
* *** ->foo (0 in foo.cpp)
* *** ->sub block
* *** Hello!
* *** <-sub block
* *** Hello again!
* *** <-foo (0 in foo.cpp)
*
*
* Don't forget to create named dbg::trace objects. If you create
* anonymous objects (i.e. you just wrote "dbg::trace(DBG_HERE);")
* then the destructor will be called immediately, rather than at the
* end of the block scope, causing invalid trace output.
*
* Tracing does not cause assertions to trigger, therefore you will
* never generate an abort or exception using this object.
*
* If you disable the tracing diagnostic @ref level before the trace
* object's destructor is called you will still get the closing trace
* output. This is important, otherwise the indentation level of the
* library would get out of sync. In this case, the closing diagnostic
* output will have a "note" attached to indicate what has happened.
*
* Similarly, if tracing diagnostics are off when the trace object is
* created, yet subsequently enabled before the destructor there will
* be no closing tracing output.
*/
class trace
{
public:
/**
* Provide the function name, or some other tracing string.
*
* This will not tie the trace object to a particular
* @ref dbg_source.
*
* @param name Tracing block name
*/
trace(func_name_t name);
/**
* @param src String describing the diagnostic source
* @param name Tracing block name
*/
trace(dbg_source src, func_name_t name);
/**
* This will not tie the trace object to a particular
* @ref dbg_source.
*
* @param here Supply DBG_HERE
*/
trace(const source_pos &here);
/**
* @param src String describing the diagnostic source
* @param here Supply DBG_HERE
*/
trace(dbg_source src, const source_pos &here);
~trace();
private:
trace(const trace &);
trace &operator=(const trace &);
void trace_begin();
void trace_end();
dbg_source m_src;
const char *m_name;
const source_pos m_pos;
bool m_triggered;
};
/**************************************************************************
* Post conditions
*************************************************************************/
/**
* A post condition class. This utility automates the checking of
* post conditions using @ref assertion. It requires a member function
* with the signature:
*
* bool some_class::invariant() const;
*
*
* When you create a post_mem_fun object you specify a post condition
* member function. When the post_mem_fun object is destroyed the
* postcondition is asserted.
*
* This is useful for methods where there are a number of exit points
* which would make it tedious to put the same @ref dbg::assertion
* in multiple places.
*
* It is also handy when an exception might be thrown and propagated by a
* function, ensuring that a postcondition is first checked. Bear in mind
* that Bad Things can happen if the @ref assertion_behaviour is
* assertions_throw and this is triggered via a propagating exception.
*
* An example of usage, the do_test method below uses the post_mem_fun
* object:
*
* class test
* {
* public:
* test() : a(10) {}
* do_test()
* {
* dbg::post_mem_fun
* post(dbg::info, this, &test::invariant, DBG_HERE);
* a = 9;
* if (SOME_CONDITION)
* {
* return; // (*)
* }
* else if (SOME_OTHER_CONDITION)
* {
* throw std::exception(); // (*)
* }
* // (*)
* }
* private:
* bool invariant()
* {
* return a == 10;
* }
* int a;
* };
*
* The post condition will be asserted at each point marked (*).
*
* @see dbg::post
*/
template
class post_mem_fun
{
public:
/**
* The type of the constraint function. It returns a bool and
* takes no parameters.
*/
typedef bool (obj_t::*fn_t)();
/**
* @param lvl Diagnostic level
* @param obj Object to invoke @p fn on (usually "this")
* @param fn Post condition member function
* @param here Supply DBG_HERE
*/
post_mem_fun(level lvl, obj_t *obj, fn_t fn, const source_pos &pos)
: m_lvl(lvl), m_src(0), m_obj(obj), m_fn(fn), m_pos(pos) {}
/**
* @param lvl Diagnostic level
* @param src String describing the diagnostic source
* @param obj Object to invoke @p fn on (usually "this")
* @param fn Post condition member function
* @param here Supply DBG_HERE
*/
post_mem_fun(level lvl, dbg_source src,
obj_t *obj, fn_t fn, const source_pos &pos)
: m_lvl(lvl), m_src(src), m_obj(obj), m_fn(fn), m_pos(pos) {}
/**
* Overloaded version of constructor which defaults to the
* @ref warning diagnostic level.
*
* @param obj Object to invoke @p fn on (usually "this")
* @param fn Post condition member function
* @param here Supply DBG_HERE
*/
post_mem_fun(obj_t *obj, fn_t fn, const source_pos &pos)
: m_lvl(dbg::warning), m_src(0),
m_obj(obj), m_fn(fn), m_pos(pos) {}
/**
* Overloaded version of constructor which defaults to the
* @ref warning diagnostic level.
*
* @param src String describing the diagnostic source
* @param obj Object to invoke @p fn on (usually "this")
* @param fn Post condition member function
* @param here Supply DBG_HERE
*/
post_mem_fun(dbg_source src, obj_t *obj, fn_t fn,
const source_pos &pos)
: m_lvl(dbg::warning), m_src(src),
m_obj(obj), m_fn(fn), m_pos(pos) {}
/**
* The destructor asserts the post condition.
*/
~post_mem_fun()
{
assertion(m_lvl, m_src,
assert_info((m_obj->*m_fn)(), "post condition",
m_pos.line, m_pos.func, m_pos.file, m_pos.src));
}
private:
const level m_lvl;
const dbg_source m_src;
obj_t *m_obj;
fn_t m_fn;
const source_pos m_pos;
};
/**
* A post condition class. Unlike @ref post_mem_fun, this class
* calls a non-member function with signature:
*
* bool some_function();
*
*
* Otherwise, use it identically to the @ref post_mem_fun.
*
* @see dbg::post_mem_fun
*/
class post
{
public:
/**
* The type of the constraint function. It returns a bool and
* takes no parameters.
*/
typedef bool (*fn_t)();
/**
* @param lvl Diagnostic level
* @param fn Post condition function
* @param here Supply DBG_HERE
*/
post(level lvl, fn_t fn, const source_pos &pos)
: m_lvl(lvl), m_src(0), m_fn(fn), m_pos(pos) {}
/**
* @param lvl Diagnostic level
* @param src String describing the diagnostic source
* @param fn Post condition function
* @param here Supply DBG_HERE
*/
post(level lvl, dbg_source src, fn_t fn, const source_pos &pos)
: m_lvl(lvl), m_src(src), m_fn(fn), m_pos(pos) {}
/**
* Overloaded version of constructor which defaults to the
* @ref warning diagnostic level.
*
* @param fn Post condition function
* @param here Supply DBG_HERE
*/
post(fn_t fn, const source_pos &pos)
: m_lvl(dbg::warning), m_src(0), m_fn(fn), m_pos(pos) {}
/**
* Overloaded version of constructor which defaults to the
* @ref warning diagnostic level.
*
* @param src String describing the diagnostic source
* @param fn Post condition function
* @param here Supply DBG_HERE
*/
post(dbg_source src, fn_t fn, const source_pos &pos)
: m_lvl(dbg::warning), m_src(src), m_fn(fn), m_pos(pos) {}
/**
* The destructor asserts the post condition.
*/
~post()
{
assertion(m_lvl, m_src,
assert_info(m_fn(), "post condition",
m_pos.line, m_pos.func, m_pos.file, m_pos.src));
}
private:
level m_lvl;
const dbg_source m_src;
fn_t m_fn;
const source_pos m_pos;
};
/**************************************************************************
* Compile time assertions
*************************************************************************/
/**
* If we need to assert a constraint that can be calculated at compile
* time, then it would be advantageous to do so - moving error detection
* to an earlier phase in development is always a Good Thing.
*
* This utility allows you to do this. You use it like this:
*
*
* enum { foo = 4, bar = 6 };
* compile_assertion<(foo > bar)>();
*
*
* There is a particular point to observe here. Although the
* expression is now a template parameter, it is important to contain it
* in parentheses. This is simply because the expression contains a ">"
* which otherwise would be taken by the compiler to be the closing of
* the template parameter. Although not all expressions require this,
* it is good practice to do it at all times.
*/
template
class compile_assertion;
template <>
class compile_assertion {};
#else
/**************************************************************************
* Non-debug stub versions
*************************************************************************/
/*
* With debugging switched off we generate null versions of the above
* definitions.
*
* Given a good compiler and a strong prevailing headwind, these will
* optimise away to nothing.
*/
#define DBG_HERE ((void*)0)
#define DBG_ASSERTION(a) ((void*)0)
//enum { default_source = 0xdead };
const dbg_source default_source = 0;
/**
* In non-debug versions, this class is used to replace an ostream
* so that code will compile away. Do not use it directly.
*
* @internal
*/
class null_stream
{
public:
#ifdef _MSC_VER
null_stream &operator<<(void *) { return *this; }
null_stream &operator<<(const void *) { return *this; }
null_stream &operator<<(long) { return *this; }
#else
template
null_stream &operator<<(const otype &) { return *this; }
#endif
template
null_stream &operator<<(otype &) { return *this; }
};
struct prefix { prefix() {} prefix(level) {} };
struct indent { indent() {} indent(level) {} };
inline void enable(level, bool) {}
inline void enable(level, dbg_source, bool) {}
inline void enable_all(level, bool) {}
inline null_stream out(level, dbg_source) {return null_stream();}
inline null_stream out(level) {return null_stream();}
inline void attach_ostream(level, std::ostream &) {}
inline void attach_ostream(level, dbg_source, std::ostream &) {}
inline void detach_ostream(level, std::ostream &) {}
inline void detach_ostream(level, dbg_source, std::ostream &) {}
inline void detach_all_ostreams(level) {}
inline void detach_all_ostreams(level, dbg_source) {}
inline null_stream info_out() {return null_stream();}
inline null_stream warning_out() {return null_stream();}
inline null_stream error_out() {return null_stream();}
inline null_stream fatal_out() {return null_stream();}
inline null_stream trace_out() {return null_stream();}
inline void set_prefix(const char *) {}
inline void enable_level_prefix(bool) {}
inline void enable_time_prefix(bool) {}
inline void set_assertion_behaviour(level, assertion_behaviour) {}
inline void set_assertion_period(dbgclock_t) {}
inline void assertion(level, dbg_source, void *) {}
inline void assertion(level, void *) {}
inline void assertion(dbg_source, void *) {}
inline void assertion(void *) {}
inline void sentinel(level, dbg_source, void *) {}
inline void sentinel(level, void *) {}
inline void sentinel(dbg_source, void *) {}
inline void sentinel(void *) {}
inline void unimplemented(level, dbg_source, void *) {}
inline void unimplemented(level, void *) {}
inline void unimplemented(dbg_source, void *) {}
inline void unimplemented(void *) {}
inline void check_ptr(level, dbg_source, const void *, void *) {}
inline void check_ptr(level, const void *, void *) {}
inline void check_ptr(dbg_source, const void *, void *) {}
inline void check_ptr(const void *, void *) {}
inline void check_bounds(level, void *, int, int, void *) {}
inline void check_bounds(level, dbg_source, int, void*, void*) {}
inline void check_bounds(level, dbg_source, int, int,
void *, void *) {}
inline void check_bounds(level, int, void *, void*) {}
inline void check_bounds(void *, int, void *, void *) {}
inline void check_bounds(int, void *, void *) {}
class trace
{
public:
trace(const char *fn_name) {}
trace(dbg_source, const char *fn_name) {}
trace(void *here) {}
trace(dbg_source, void *here) {}
~trace() {}
};
template
class post_mem_fun
{
public:
typedef bool (obj_t::*fn_t)();
post_mem_fun(level, void *, fn_t, void *) {}
post_mem_fun(level, dbg_source, void *, fn_t, void *) {}
post_mem_fun(void *, fn_t, void *) {}
post_mem_fun(dbg_source, void *, fn_t, void *) {}
~post_mem_fun() {}
};
class post
{
public:
typedef bool(*fn_t)();
post(level, fn_t, void *) {}
post(level, dbg_source, fn_t, void *) {}
post(fn_t, void *) {}
post(dbg_source, fn_t, void *) {}
~post() {}
};
template
class compile_assertion {};
#endif
}
#endif
Generated by: pg1 on imgpc030 on Tue Sep 16 13:46:16 2003, using kdoc 2.0a54. |