Exceptions

Files CCore/inc/Exception.h CCore/src/Exception.cpp

CCore uses a specially designed pattern to throw exceptions.


#include <CCore/inc/Exception.h>

....

void func()
 {
  ....

  Printf(Exception,"message format",....);
 }

class SomeClass
 {
  public:

   ~SomeClass()
    {
     Printf(NoException,"message format",....);
    }
 };

To throw an exception, just print a message to one of two targets: Exception or NoException. If you print to the Exception, the print statement will throw an exception of the type CatchType. Printing to the NoException does not throw an exception. But it prints a message and setup a error flag in the exception report object if there is one.

In CCore we keep these statements in .cpp files. The pattern for the exception message is the following:


void GuardIndexOutOfRange(ulen index,ulen len)
 {
  Printf(Exception,"CCore::GuardIndex(index=#;,len=#;) : out of range",index,len);
 }

PrintException

The class PrintException serves as a print adapter for the enum ExceptionType (the type of constants Exception and NoException).


class PrintException : public PrintBase
 {
   ExceptionType ex;
   ReportException *report;
   
   char buf[TextBufLen];
   bool do_throw;
 
  private:
  
   virtual PtrLen<char> do_provide(ulen hint_len);
   
   virtual void do_flush(char *ptr,ulen len);
   
  protected: 
   
   PrintException();
   
  public:
  
   explicit PrintException(ExceptionType ex); 
   
   ~PrintException();
 };
 
template <>
struct PrintOutAdapter<ExceptionType>
 {
  using PrintOutType = PrintException ;
 };

You don't need this class for a direct usage. It does its job inside a Printf() statement: finds a ReportException object, prints the message to it, setup a error flag and finally in destructor throws an exception, if required.

ReportException

Each thread may have an associated ReportException object. The purpose of this object is to serve as the printing target for exception messages. It also has an internal flag, which signals if there were exception messages.


class ReportException : NoCopy
 {
   bool nok;

   ....

  protected:
  
   static void Print(StrLen str);
  
   virtual void print(StrLen str);
  
   virtual void start(ExceptionType ex);
  
   virtual void add(const char *ptr,ulen len);
   
   virtual void end();
  
  public:
  
   ReportException();
   
   ~ReportException();
   
   bool operator + () const { return !nok; }
   
   bool operator ! () const { return nok; }
   
   void clear() { nok=false; }
  
   void guard() { if( nok ) throw CatchType(); }

   static void Clear();
 };

By default, ReportException prints a text to the default console. But you may alter this by deriving a class from the ReportException and overriding virtual methods print(), start(), add(), and end(). To redirect the text output, you need to override only the method print(), but you may alter the formating or processing the elements of the message by overriding other methods. Print() prints to the console. Be caution in implementation of these methods: they must not throw exceptions, it will lead to abort.

The static method Clear() clears the exception flag for the current ReportException object, if any.

SilentReportException is a such derived class, it prints nowhere.

ReportException objects must be created as local variables to setup a report exception target for the current thread for the scope duration. Usually, you should couple such object with a try/catch block. Here is an example:


try
  {
   ReportException report; // or some derived from ReportException class

   {

    // do some job here

   } // you need the inner scope to complete all destructors before report.guard();

   report.guard(); // throw an exception if there was Printf(NoException,...); above
  }
catch(CatchType)
  {
   // handle errors here
  }

Or the following way:


ReportException report; // or some derived from ReportException class

try
  {
   {

    // do some job here

   } // you need the inner scope to complete all destructors before report.guard();

   report.guard(); // throw an exception if there was Printf(NoException,...); above
  }
catch(CatchType)
  {
   // handle errors here
  }

If you have multiple ReportException objects, then the last is the current.


ReportException report1;

 // report1 is the current

{
 ReportException report2;

  // report2 is the current here

}

 // report1 is the current again

ReportExceptionTo

ReportExceptionTo class is a derived class from the ReportException, this class redirects exception message output to the given output object.


template <class P>
class ReportExceptionTo : public ReportException
 {
   P &out;
   
  public: 
   
   explicit ReportExceptionTo(P &out_) : out(out_) {}
   
   ~ReportExceptionTo() {}
   
   virtual void print(StrLen str)
    {
     SilentReportException report;
     
     try
       {
        out.put(str.ptr,str.len);
       }
     catch(...) {}
    }
 };
 

Constructor takes and stores the reference to the target object. The class of this object must provide the method put(const char *str,ulen len). For example, any printing object classes are acceptable.

The virtual method print() is public for this class.

Custom exceptions

Sometimes you may want to throw not the object of the empty CatchType, but something more. To do this use the PrintCustomException class.


class SomeException : public CatchType
 {
  ....

  public:

   SomeException(A a,B b,C c);
 };

....

{
 A a;
 B b;
 C c;

 PrintCustomException<SomeException> out(a,b,c);

 Printf(out,"message format",...);
}

For example, that is the way we throw the "no-memory" exception:


class NoMemCatchType : public CatchType , public std::bad_alloc {};

void GuardNoMem(ulen len)
 {
  PrintCustomException<NoMemCatchType> out;
  
  Printf(out,"CCore::GuardNoMem(len=#;) : no memory",len);
 }

We have to do it so to satisfy the C++ standard requirements.