# ifndef LOGEXC_H # define LOGEXC_H #include #include #include #include #include #if !defined(_WIN32) || defined(__MINGW32__) # include #endif /// this specifies whether to put file/line info in error messages # ifndef LINFO # define LINFO 1 # endif using namespace std; /// verbosity levels enum vbLEVELS{ vblNONE = 0, ///< completely silent vblFATAL = 0x1, ///< report fatal errors vblOERR = 0x2, ///< report other errors vblERR = vblFATAL|vblOERR, ///< report all errors vblWARN = 0x4, ///< report warnings vblALLBAD = vblWARN|vblERR, ///< report all errors and warnings vblMESS1 = 0x8, ///< report messages of level 1 (important) vblMESS2 = 0x10, ///< report messages of level 2 (less important) vblMESS3 = 0x20, ///< report messages of level 3 (not important) vblMESS4 = 0x40, ///< report messages of level 4 (anything possible) vblALLMESS = vblMESS1|vblMESS2|vblMESS3|vblMESS4, ///< report all messages vblPROGR = 0x80, ///< report progress vblALL = 0xffff }; /// traits structure to deduce exception level and name from exception class /// by default all exceptions have vblFATAL level template struct log_exception_traits{ /// exeption level according to the vbLEVELS static int level(const exc_t &signal){ return vblFATAL; } /// the string name of exception category static string name(const exc_t &signal){ return typeid(exc_t).name();} /// adds some more explanations to the description /// default behaviour: nothing done static exc_t add_words(const exc_t &orig, const char *words){ return orig; } }; /// integer exceptions have the level equal to their value template<> struct log_exception_traits{ /// exeption level according to the vbLEVELS static int level(const int &signal){ return signal; } /// the string name of exception category static string name(const int &signal){ if(signal&vblFATAL) return "fatal error"; else if(signal&vblOERR) return "error"; else if(signal&vblWARN) return "warning"; else return ""; /* else if(signal&vblALLMESS) return "message"; else if(signal&vblPROGR) return "progress report"; else return "integer exception";*/ } /// default behaviour: nothing done static int add_words(const int &orig, const char *words){ return orig; } }; /// vbLEVELS exceptions act as integers template<> struct log_exception_traits{ static int level(const enum vbLEVELS &signal){ return log_exception_traits::level(signal); } static string name(const enum vbLEVELS &signal){ return log_exception_traits::name(signal); } static enum vbLEVELS add_words(const enum vbLEVELS &orig, const char *words){ return orig; } }; /// Logger class to control (computational) function behaviour when something requiring user attention has happened. /// message(signal,errcode, text) is used to either throw an exception or return errorcode /// At first, the the level of error is determined via log_exception_traits<>::level(signal) /// For integer (enum) signals the level is the signal itself. /// Then text is printed, if signal level is listed in output levels or (or in extra outlevels, if they are set) /// via log_text() function. /// If level has vblERR bit, the behaviour is controlled by the flag specified in set_throw(flag): /// flag=0: nothing done; /// flag=1: calls add_words() for signal and throws signal; /// flag=2: throws pair<>(errcode, text); /// flag=3: throws errcode. /// Then, if the level is listed in stop_levels (or in extra stop levels, if they are set), the program is aborted, /// otherwise errcode is returned; /// The function set_levels(out_levels,stop_levels) is used to specify bitflags for the levels which /// require message output or/and program termination. Stop level has effect only when exceptions are not thrown. /// The function extra_levels(eout_levels,estop_levels) is used to temporarily set the corresponding levels, /// they are unset (the original levels are restored) by calling extra_levels(0,0). class message_logger { // global message is a friend template friend int message(const exc_t &signal, int errcode, const char *what, ...); protected: string descriptor; int throw_ex; int outlevel, eoutlevel; int stoplevel, estoplevel; static message_logger *glogp; /// used to restore the previous global logger message_logger *prev, *next; public: message_logger(const string &descriptor_="", int out_level=vblALLBAD|vblMESS1, int stop_level=vblFATAL, int throw_exceptions=0, int use_globally=0) :descriptor(descriptor_),prev(NULL),next(NULL){ set_throw(throw_exceptions); set_levels(out_level,stop_level); extra_levels(0,0); if(use_globally){ set_global(true); } } /// returns a reference to global logger /// if not set, links with default message_logger static message_logger &global(); /// sets/unsets this logger as the global logger int set_global(bool set){ if(set){ if(prev) // already set return -1; if(glogp) glogp->next=this; prev=glogp; glogp=this; } else{ if(glogp!=this) // was not set as the global return -1; glogp=prev; if(glogp) glogp->next=NULL; prev=NULL; } return 1; } virtual void set_throw(int throw_exceptions){ throw_ex=throw_exceptions; } virtual void set_levels(int out_level=vblALLBAD|vblMESS1, int stop_level=vblFATAL){ outlevel=out_level; stoplevel=stop_level; } /// nonzero extra levels are applied instead of set ones virtual void extra_levels(int out_level=vblALLBAD|vblMESS1, int stop_level=vblFATAL){ eoutlevel=out_level; estoplevel=stop_level; } template int message(const exc_t &signal, int errcode, const char *what, ...){ int level=log_exception_traits::level(signal); char buff[1024]; if(level&(eoutlevel ? eoutlevel : outlevel)){ //needs to print a message va_list args; va_start(args,what); vsnprintf(buff,1024,what,args); log_text(level,log_exception_traits::name(signal).c_str(),buff); } if(level&vblERR){ if(throw_ex==1) // throws exc_t exception throw log_exception_traits::add_words(signal,buff); else if(throw_ex==2) // throws pair<>(int,const char*) exception throw make_pair(errcode,what); else if(throw_ex==3) // throws int exception throw errcode; } if(level&(estoplevel ? estoplevel: stoplevel) ){ // needs to stop exit(errcode); } return errcode; } virtual void log_text(int level, const char *messtype, const char *messtext){ if(descriptor!="") // descriptor is used as header printf("%s:\n",descriptor.c_str()); if(string(messtype)!=string("")) printf("%s: ",messtype); printf("%s\n",messtext); } /// checks that the deleted one is not in global logger chain ~message_logger(){ if(prev){ prev->next=next; if(next) next->prev=prev; } set_global(false); } }; /// global message function template int message(const exc_t &signal, int errcode, const char *what, ...){ if(message_logger::glogp){ va_list args; va_start(args,what); char buff[1024]; vsnprintf(buff,1024,what,args); return message_logger::glogp->message(signal,errcode,buff); } else return -1; } /// message logger for which std and error streams may be specified class stdfile_logger: public message_logger { protected: FILE *fout, *ferr; public: stdfile_logger(const string &descriptor_="", int throw_exceptions=0, FILE *out=stdout, FILE *err=stderr, int out_level=vblALLBAD|vblMESS1,int stop_level=vblFATAL, int use_globally=0) : message_logger(descriptor_,out_level,stop_level,throw_exceptions,use_globally),fout(NULL), ferr(NULL){ set_out(out); set_err(err); } virtual void set_out(FILE *out, int close_prev=0){ if(close_prev && fout && fout!=stderr && fout !=stdout) fclose(fout); fout=out; } virtual void set_err(FILE *err, int close_prev=0){ if(close_prev && ferr && ferr!=stderr && ferr !=stdout) fclose(ferr); ferr=err; } virtual void log_text(int level, const char *messtype, const char *messtext){ FILE *f= (level&vblALLBAD ? ferr : fout); if(!f) return; if(descriptor!="") // descriptor is used as header fprintf(f,"%s:\n",descriptor.c_str()); if(string(messtype)!="") fprintf(f,"%s: ",messtype); fprintf(f,"%s\n",messtext); } }; /// format a string const char *fmt(const char *format,...); /// macros with common usage #define LOGFATAL(code,text,lineinfo) ((lineinfo) ? ::message(vblFATAL,(code)," %s at %s:%d",(text),__FILE__,__LINE__) : \ (::message(vblFATAL,(code)," %s",(text))) ) #define LOGERR(code,text,lineinfo) ((lineinfo) ? ::message(vblOERR,(code)," %s at %s:%d",(text),__FILE__,__LINE__) : \ (::message(vblOERR,(code)," %s",(text))) ) #define LOGMSG(cat,text,lineinfo) ((lineinfo) ? ::message((cat),0," %s at %s:%d",(text),__FILE__,__LINE__) : \ (::message((cat),0," %s",(text))) ) # if 0 /// did not work /// this may be used to inherit exceptions /// where level and name are defined whithin a class struct log_exception { /// exeption level according to the vbLEVELS static int level(const log_exception &signal){ return vblFATAL; } /// the string name of exception category static string name(const log_exception &signal){ return "undefined exception";} }; /// log_exceptions act as themselves template<> struct log_exception_traits{ static int level(const log_exception &signal){ return log_exception::level(signal); } static string name(const log_exception &signal){ return log_exception::name(signal); } }; # endif # endif