the NULL->nullptr changes for lib/awpmd. Then, hand-applied a minor cleanup to comments, ala Axel's changes to the rest of the codebase.
314 lines
10 KiB
C++
314 lines
10 KiB
C++
# ifndef LOGEXC_H
|
|
# define LOGEXC_H
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstdarg>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
#if !defined(_WIN32) || defined(__MINGW32__)
|
|
# include <typeinfo>
|
|
#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<class exc_t>
|
|
struct log_exception_traits{
|
|
/// exception 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<int>{
|
|
/// exception 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<enum vbLEVELS>{
|
|
static int level(const enum vbLEVELS &signal){ return log_exception_traits<int>::level(signal); }
|
|
static string name(const enum vbLEVELS &signal){ return log_exception_traits<int>::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<class exc_t>
|
|
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(nullptr),next(nullptr){
|
|
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=nullptr;
|
|
prev=nullptr;
|
|
}
|
|
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<class exc_t>
|
|
int message(const exc_t &signal, int errcode, const char *what, ...){
|
|
int level=log_exception_traits<exc_t>::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<exc_t>::name(signal).c_str(),buff);
|
|
}
|
|
if(level&vblERR){
|
|
if(throw_ex==1) // throws exc_t exception
|
|
throw log_exception_traits<exc_t>::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<class exc_t>
|
|
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(nullptr), ferr(nullptr){
|
|
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 *logfmt(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 {
|
|
/// exception 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<log_exception>{
|
|
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
|