/// \ingroup rbd_common
///@{

/// \file myexcept.h
/// Exception handler.
/// The low level classes for
/// - my exception class hierarchy
/// - the functions needed for my simulated exceptions
/// - the Tracer mechanism
/// - routines for checking whether new and delete calls are balanced
///


// A set of classes to simulate exceptions in C++
//
//   Partially copied from Carlos Vidal s article in the C users  journal
//   September 1992, pp 19-28
//
//   Operations defined
//      Try {     }
//      Throw ( exception object )
//      ReThrow
//      Catch ( exception class ) {      }
//      CatchAll {      }
//      CatchAndThrow
//
//   All catch lists must end with a CatchAll or CatchAndThrow statement
//   but not both.
//
//   When exceptions are finally implemented replace Try, Throw(E), Rethrow,
//   Catch, CatchAll, CatchAndThrow by try, throw E, throw, catch,
//   catch(...), and {}.
//
//   All exception classes must be derived from BaseException, have no
//   non-static variables and must include the statement
//
//      static unsigned long Select;
//
//   Any constructor in one of these exception classes must include
//
//      Select = BaseException::Select;
//
//   For each exceptions class, EX_1, some .cpp file must include
//
//      unsigned long EX_1::Select;
//


#ifndef EXCEPTION_LIB
#define EXCEPTION_LIB

#include "include.h"

#ifdef use_namespace
namespace RBD_COMMON {
#endif


void Terminate();


//********** classes for setting up exceptions and reporting ************//

class BaseException;
class GeneralMatrix;

class Tracer                             // linked list showing how
{                                        // we got here
public:
  Tracer(const char*) {};
  ~Tracer() {}
  void ReName(const char*) {}
  static void PrintTrace() {};             // for printing trace
  static void AddTrace() {};               // insert trace in exception record
  static Tracer* last;                  // points to Tracer list
};


class BaseException {                     // The base exception class
 protected:
  int    MAXSIZE = 512;
  char*  _what;                            // error message
  bool   _deleteMe;
 public:
  static unsigned long Select;
  BaseException(const char* a_what = 0);
  BaseException(const BaseException& oth);
  BaseException& operator=(const BaseException& oth);
  ~BaseException();
  void AddMessage(const char* a_what);
  void AddInt(int value);
  void MatrixDetails(const GeneralMatrix& A);
  const char* what() { return _what; }
};

 
#ifdef TypeDefException
typedef BaseException Exception;        // for compatibility with my older libraries
#endif

#ifdef SimulateExceptions                // SimulateExceptions

#include <setjmp.h>


//************* the definitions of Try, Throw and Catch *****************//


class JumpItem;
class Janitor;

class JumpBase         // pointer to a linked list of jmp_buf s
{
public:
   static JumpItem *jl;
   static jmp_buf env;
};

class JumpItem         // an item in a linked list of jmp_buf s
{
public:
   JumpItem *ji;
   jmp_buf env;
   Tracer* trace;                     // to keep check on Tracer items
   Janitor* janitor;                  // list of items for cleanup
   JumpItem() : ji(JumpBase::jl), trace(0), janitor(0)
      { JumpBase::jl = this; }
   ~JumpItem() { JumpBase::jl = ji; }
};

void Throw();

inline void Throw(const BaseException&) { Throw(); }

#define Try                                             \
   if (!setjmp( JumpBase::jl->env )) {                  \
   JumpBase::jl->trace = Tracer::last;               \
   JumpItem JI387256156;

#define ReThrow Throw()

#define Catch(EXCEPTION)                                \
   } else if (BaseException::Select == EXCEPTION::Select) {

#define CatchAll } else

#define CatchAndThrow  } else Throw();


//****************** cleanup heap following Throw ***********************//

class Janitor
{
protected:
   static bool do_not_link;                  // set when new is called
   bool OnStack;                             // false if created by new
public:
   Janitor* NextJanitor;
   virtual void CleanUp() {}
   Janitor();
   virtual ~Janitor();
};


// The tiresome old trick for initializing the Janitor class
// this is needed for classes derived from Janitor which have objects
// declared globally

class JanitorInitializer
{
public:
   JanitorInitializer();
private:
   static int ref_count;
};

static JanitorInitializer JanInit;

#endif                                // end of SimulateExceptions

#ifdef UseExceptions

#define Try try
#define Throw(E) throw E
#define ReThrow throw
#define Catch catch
#define CatchAll catch(...)
#define CatchAndThrow {}

#endif                                // end of UseExceptions


#ifdef DisableExceptions              // Disable exceptions

#define Try {
#define ReThrow Throw()
#define Catch(EXCEPTION) } if (false) {
#define CatchAll } if (false)
#define CatchAndThrow }

inline void Throw() { Terminate(); }
inline void Throw(const BaseException&) { Terminate(); }


#endif                                // end of DisableExceptions

#ifndef SimulateExceptions            // ! SimulateExceptions

class Janitor                         // a dummy version
{
public:
   virtual void CleanUp() {}
   Janitor() {}
   virtual ~Janitor() {}
};

#endif                                // end of ! SimulateExceptions


//******************** FREE_CHECK and NEW_DELETE ***********************//

#ifdef DO_FREE_CHECK                          // DO_FREE_CHECK
// Routines for tracing whether new and delete calls are balanced

class FreeCheck;

class FreeCheckLink
{
protected:
   FreeCheckLink* next;
   void* ClassStore;
   FreeCheckLink();
   virtual void Report()=0;                   // print details of link
   friend class FreeCheck;
};

class FCLClass : public FreeCheckLink         // for registering objects
{
   char* ClassName;
   FCLClass(void* t, char* name);
   void Report();
   friend class FreeCheck;
};

class FCLRealArray : public FreeCheckLink     // for registering real arrays
{
   char* Operation;
   int size;
   FCLRealArray(void* t, char* o, int s);
   void Report();
   friend class FreeCheck;
};

class FCLIntArray : public FreeCheckLink     // for registering int arrays
{
   char* Operation;
   int size;
   FCLIntArray(void* t, char* o, int s);
   void Report();
   friend class FreeCheck;
};


class FreeCheck
{
   static FreeCheckLink* next;
   static int BadDelete;
public:
   static void Register(void*, char*);
   static void DeRegister(void*, char*);
   static void RegisterR(void*, char*, int);
   static void DeRegisterR(void*, char*, int);
   static void RegisterI(void*, char*, int);
   static void DeRegisterI(void*, char*, int);
   static void Status();
   friend class FreeCheckLink;
   friend class FCLClass;
   friend class FCLRealArray;
   friend class FCLIntArray;
};

#define FREE_CHECK(Class)                                                  \
public:                                                                    \
   void* operator new(size_t size)                                         \
   {                                                                       \
      void* t = ::operator new(size); FreeCheck::Register(t,#Class);       \
      return t;                                                            \
   }                                                                       \
   void operator delete(void* t)                                           \
   { FreeCheck::DeRegister(t,#Class); ::operator delete(t); }


#ifdef SimulateExceptions         // SimulateExceptions

#define NEW_DELETE(Class)                                                  \
public:                                                                    \
   void* operator new(size_t size)                                         \
   {                                                                       \
      do_not_link=true;                                                    \
      void* t = ::operator new(size); FreeCheck::Register(t,#Class);       \
      return t;                                                            \
   }                                                                       \
   void operator delete(void* t)                                           \
   { FreeCheck::DeRegister(t,#Class); ::operator delete(t); }


#endif                           // end of SimulateExceptions


#define MONITOR_REAL_NEW(Operation, Size, Pointer)                         \
	FreeCheck::RegisterR(Pointer, Operation, Size);
#define MONITOR_INT_NEW(Operation, Size, Pointer)                          \
	FreeCheck::RegisterI(Pointer, Operation, Size);
#define MONITOR_REAL_DELETE(Operation, Size, Pointer)                      \
	FreeCheck::DeRegisterR(Pointer, Operation, Size);
#define MONITOR_INT_DELETE(Operation, Size, Pointer)                       \
	FreeCheck::DeRegisterI(Pointer, Operation, Size);

#else                            // DO_FREE_CHECK not defined

#define FREE_CHECK(Class) public:
#define MONITOR_REAL_NEW(Operation, Size, Pointer) {}
#define MONITOR_INT_NEW(Operation, Size, Pointer) {}
#define MONITOR_REAL_DELETE(Operation, Size, Pointer) {}
#define MONITOR_INT_DELETE(Operation, Size, Pointer) {}


#ifdef SimulateExceptions         // SimulateExceptions


#define NEW_DELETE(Class)                                                  \
public:                                                                    \
	void* operator new(size_t size)                                    \
	{ do_not_link=true; void* t = ::operator new(size); return t; }    \
	void operator delete(void* t) { ::operator delete(t); }

#endif                            // end of SimulateExceptions

#endif                            // end of ! DO_FREE_CHECK

#ifndef SimulateExceptions        // ! SimulateExceptions

#define NEW_DELETE(Class) FREE_CHECK(Class)

#endif                            // end of ! SimulateExceptions


//********************* derived exceptions ******************************//

class Logic_error : public BaseException
{
public:
   static unsigned long Select;
   Logic_error(const char* a_what = 0);
};

class Runtime_error : public BaseException
{
public:
   static unsigned long Select;
   Runtime_error(const char* a_what = 0);
};

class Domain_error : public Logic_error
{
public:
   static unsigned long Select;
   Domain_error(const char* a_what = 0);
};

class Invalid_argument : public Logic_error
{
public:
   static unsigned long Select;
   Invalid_argument(const char* a_what = 0);
};

class Length_error : public Logic_error
{
public:
   static unsigned long Select;
   Length_error(const char* a_what = 0);
};

class Out_of_range : public Logic_error
{
public:
   static unsigned long Select;
   Out_of_range(const char* a_what = 0);
};

//class Bad_cast : public Logic_error
//{
//public:
//   static unsigned long Select;
//   Bad_cast(const char* a_what = 0);
//};

//class Bad_typeid : public Logic_error
//{
//public:
//   static unsigned long Select;
//   Bad_typeid(const char* a_what = 0);
//};

class Range_error : public Runtime_error
{
public:
   static unsigned long Select;
   Range_error(const char* a_what = 0);
};

class Overflow_error : public Runtime_error
{
public:
   static unsigned long Select;
   Overflow_error(const char* a_what = 0);
};

class Bad_alloc : public BaseException
{
public:
   static unsigned long Select;
   Bad_alloc(const char* a_what = 0);
};

#ifdef use_namespace
}
#endif


#endif                            // end of EXCEPTION_LIB


// body file: myexcept.cpp


///@}

