/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Assertions.
 * @remarks An exception is thrown if an asertion fails.
 *          Since a failed assertion can be caught, assertions themselves can be tested.
 *          A missing exception results if an assertion that should fail aberrantly succeeds.
 *//*
 * LEGAL:   COPYRIGHT (C) 2004 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef BASE_ASSERT_HH
#define BASE_ASSERT_HH 1

#include <string>
using std::string;

// make DEBUG=1 enables assertions, otherwise disabled.
#if ! DEBUG && ! defined(NDEBUG)
#define NDEBUG  // disable assert()
#endif
#include <assert.h>  // affected by NDEBUG

namespace base {

////////////////////////////////////////////////////////////////////////////////
/// @brief Exception thrown when ASSERT*() evaluates false.
///
class AssertException : public Exception
{
COPYABLE(AssertException)  // PREVENT_COPYING() cannot be used for exception classes
public:
                 AssertException( const string& fileName, int fileLine );
    virtual     ~AssertException() throw();
};


// __LINE__ is a decimal constant, not a string.
#if DEBUG
#define ASSERT( COND ) base::RESTRICT::Assert( (COND), __FILE__, __LINE__ )
namespace RESTRICT { void Assert( bool cond, const char* fileName, int fileLine ); }
#else
#define ASSERT( COND )
#endif

// ASSERT2() is for pedantic checks that would slow down the normal debug build.
#if DEBUG == 2  // extra debug level
#define ASSERT2( COND ) ASSERT( (COND) )
#else
#define ASSERT2( COND )
#endif

#if DEBUG
#define ASSERT_UNLESS_EXITING( COND ) base::RESTRICT::AssertUnlessExiting( (COND), __FILE__, __LINE__ )
namespace RESTRICT { void AssertUnlessExiting( bool cond, const char* fileName, int fileLine ); }
#else
#define ASSERT_UNLESS_EXITING( COND )
#endif

// Assert that a function runs only once.
#if DEBUG
#define ASSERT_RUN_ONCE {{ PERSISTENT int once = 0; ++once; ASSERT( once == 1 ); }}
#else
#define ASSERT_RUN_ONCE
#endif

// Assert that one instance of a class exists.  Pass Class::msInstanceCount.
#if DEBUG
#define ASSERT_ONE_INSTANCE_CTOR( INSTANCE_COUNT ) {{ ASSERT( (INSTANCE_COUNT) == 0 ); ++(INSTANCE_COUNT); }}
#define ASSERT_ONE_INSTANCE_DTOR( INSTANCE_COUNT ) {{ ASSERT( (INSTANCE_COUNT) == 1 ); --(INSTANCE_COUNT); }}
#else
#define ASSERT_ONE_INSTANCE_CTOR( INSTANCE_COUNT )
#define ASSERT_ONE_INSTANCE_DTOR( INSTANCE_COUNT )
#endif

// AssertOr*() is for error conditions where the program might be able to continue.
// The OR case is in case assert() is disabled by NDEBUG.
#define ASSERT_OR_RETURN( COND )          {{ ASSERT( (COND) ); if ( !(COND) ) return; }}
#define ASSERT_OR_RETURN_VAL( COND, VAL ) {{ ASSERT( (COND) ); if ( !(COND) ) return (VAL); }}
#define ASSERT_OR_GOTO( COND, LABEL )     {{ ASSERT( (COND) ); if ( !(COND) ) goto (LABEL); }}

// See base_asert.cc
#if DEBUG
void ASSERT_STACK_OBJECT( const void* obj );
void ASSERT_PERSISTENT_OBJECT( const void* obj );
#else
#define ASSERT_STACK_OBJECT( OBJ_PTR )
#define ASSERT_PERSISTENT_OBJECT( OBJ_PTR )
#endif

} // namespace base

#endif // BASE_ASSERT_HH
