// xassert.h            see license.txt for copyright and terms of use
// replacement for assert that throws an exception on failure
// (x_assert_fail is defined in exc.cpp)
// Scott McPeak, 1997-1998  This file is public domain.

#ifndef XASSERT_H
#define XASSERT_H

#include "macros.h"     // NORETURN

// linkdepend: exc.cpp

// this functions accepts raw 'char const *' instead of 'rostring'
// because I do not want this interface to depend on str.h, and also
// because I do not want the many call sites to have the overhead
// of constructing and destructing temporary objects
void x_assert_fail(char const *cond, char const *file, int line) NORETURN;

// Ordinary 'xassert' *can* be turned off, but the nominal intent
// is that it be left on, under the "ship what you test" theory.
// I advocate using NDEBUG_NO_ASSERTIONS only as a way to gauge the
// performance impact of the existing assertions.
#if !defined(NDEBUG_NO_ASSERTIONS)
  #define xassert(cond) \
    ((cond)? (void)0 : x_assert_fail(#cond, __FILE__, __LINE__))
#else
  #define xassert(cond) ((void)0)
#endif

// Here's a version which will turn off with ordinary NDEBUG.  It
// is for more expensive checks that need not ship.
#if !defined(NDEBUG)
  #define xassertdb(cond) xassert(cond)
#else
  #define xassertdb(cond) ((void)0)
#endif

// call when state is known to be bad; will *not* return
#define xfailure(why) x_assert_fail(why, __FILE__, __LINE__)


// Quick note: one prominent book on writing code recommends that
// assertions *not* include the failure condition, since the file
// and line number are sufficient, and the condition string uses
// memory.  The problem is that sometimes a compiled binary is
// out of date w/respect to the code, and line numbers move, so
// the condition string provides a good way to find the right
// assertion.


/*
  Why throw an exception after an assertion?
  
  The standard assert() calls abort() after printing its message.
  This is like throwing an exception all the way to the calling
  process.  This is fine if programs are small.

  But when a program is large enough, it may contain subsystems at
  several layers, such that a higher level module is capable of
  recovering from the failure of a lower level module.  Using abort(),
  one would have to resort to catching signals, which is messy.

  An exception is much nicer to catch, and has the added benefit that
  intermediate layers can catch and rethrow, appending little bits of
  context, if they want to make the message more informative.

  In most of my programs, the 'x_assert' exception is only caught in
  main() (implicitly, by catching 'xBase'), and hence 'xassert' acts
  very much like 'assert'.  But by using 'xassert' consistenty, any
  time I *do* have a large program with recovery, all the lower-level
  modules are all ready to cooperate.
  
  Speaking of recovery: Be aware that when a module fails an
  assertion, its internal state is most likely inconsistent.  Recovery
  actions need to be fairly conservative about what code gets
  re-entered and state re-used after a failure.  This is no different
  than with 'assert', as a program could have inconsistent state *on
  disk* that gets reactivated upon being restarted, but persistent
  (across process boundaries) inconsistent state is simply less
  common.

*/

#endif // XASSERT_H

