/* ======================================================================== * (C) 2000 Vienna University of Technology * ======================================================================== * PROJECT: Urban Viz * ========================================================================*/ /** several error checking/log facilities. * * for char-strings, 'String' is typedef'd for convenience * This library can be used standalone, with old style iostreams * (define \c OLDSTREAM for that) or new style iostreams * * $Header: /usr/local/cvsyare/cvsyamp/include/merror.h,v 1.3 2003/01/06 19:01:32 wimmer Exp $ * @author Michael Wimmer * @file */ /* ========================================================================*/ #ifndef NO_PRAGMA_ONCE #pragma once #endif #ifndef MERROR_H #define MERROR_H // this library can be used with new and old style iostreams #ifndef OLDSTREAM # include # define ERRSTD std #else # include # define ERRSTD #endif // --------------------------------------------------------------------------- // General macros // --------------------------------------------------------------------------- /// always use this to delete an object #define DELPTR(ptr) do { if (ptr) { delete (ptr); (ptr) = NULL; } } while (0) /// always use this to delete an array of objects #define DELAPTR(ptr) do { if (ptr) { delete [] (ptr); (ptr) = NULL; } } while (0) // --------------------------------------------------------------------------- // Log file initialization // --------------------------------------------------------------------------- // gets global logfile, used by OUT/ASSERT/etc macros, to allow // logstream to be used in global constructors ERRSTD::ostream &_GetLogStream(); // enables use of curses or other things ERRSTD::ostream &_LogStreamDone(ERRSTD::ostream &os, bool overrideconsole = false); /** an application can declare a function of this type to redirect console output. */ typedef void (*LogStreamToConsoleFunc)(const char *consoleoutput); /// Set a handler to redirect console output void SetConsoleStreamHandler(LogStreamToConsoleFunc handler); /// enables/disables logging to logfile (for performance reasons) void SetLogToFile(bool yesno); /** initializes the logfile for \c OUT/ASSERT etc macros - call once at beginning if you desire file output (for logtofile=false output goes to \c cerr initially) */ void InitLogStream(const char *filename = "error.log", bool logtofileonly = true); /// if true, logs to logfile, otherwise logs to console (\c cerr) bool SetLogStream(bool logtofileonly); // --------------------------------------------------------------------------- // Log file definitions and macros // --------------------------------------------------------------------------- /** @name Logfile output macros Any of those will write a string to the console and, if desired, to the logfile. They differ in the amount of tabs they prepend. Note: all macros (except OUT0) PREPEND a carriage return! */ //@{ /// stream a variable; example: \c OUT1(SV(refcount)) gives ' refcount = 5 ' #define SV(x) " " #x "=" << (x) << " " /// stream a string; example: \c OUT1(SS(filename)) gives ' filename = "abc" ' #define SS(x) "\"" << x << "\"" // ------- logfile output macros /** stream to logfile. (NOTE: don't insert \c endl here! use OUT1 for that - this is if you DON'T want linebreaks!) */ #define OUT0(__xx) _LogStreamDone(_GetLogStream() << __xx) /// stream to logfile (use if you want linebreaks!) - for normal use #define OUT1(__xx) OUT0("\n" << __xx) /// like \c OUT1, but with a TAB in front #define OUT2(__xx) OUT1("\t" << __xx) /// prepends 2 TABs #define OUT3(__xx) OUT1("\t\t" << __xx) /// prepends i TABs #define OUTI(__ii,__xx) { _GetLogStream() << "\n"; \ for (int __jj = 0; __jj < __ii; __jj ++) \ _GetLogStream() << "\t"; \ _LogStreamDone(_GetLogStream() << __xx); } #define HERE " ("__FILE__", " << __LINE__ << ") ("__DATE__")" // internal macro to allow forced console output #define _CONOUT(__xx) _LogStreamDone(_GetLogStream() << "\n" << __xx,true) /// error message with file, line and date information #define EOUT(__xx) _CONOUT("ERROR: " << __xx << HERE) /// warning message with file, line and date information #define WOUT(__xx) _CONOUT("\tWARNING: " << __xx << HERE) //@} /* --------------------------------------------------------------------------- Assertion macros: test the parameter result (if (!result) and take approprate action if failed: ASM_xxx 2nd parameter specifies an error message for the logfile ASS_xxx no error message xxx if failed, just print error message xxx_RETVAL if failed, the 3rd macro parameter is returned xxx_RET return is called (for void functions) xxx_ELSE if failed, output the error message, else execute following statement _ASM_TEMPLATE is used for creating the other macros. -------------------------------------------------------------------------*/ // used by ASM_* #define _BLOCK do #define _BLBEGIN _BLOCK { #define _BLEND } while (0) #define _NOP 4==4 #ifndef RELEASE_BUILD #define _ASM_TEMPLATE(result, errstr, stmt) \ _BLBEGIN if(!(result)) { EOUT(errstr); stmt; } _BLEND #define _ASS_TEMPLATE(result, stmt) \ _BLBEGIN if(!(result)) { stmt; } _BLEND #define _CHKM_TEMPLATE(result, errstr, stmt) \ _BLBEGIN if(!(result)) { EOUT(errstr); stmt; } _BLEND #define _CHKMS_TEMPLATE(result, stmt) \ _BLBEGIN if((result)==NULL) \ { DWORD errorValue = GetLastError(); EOUTMS( SV(errorValue) << \ GetErrorStringMS(errorValue)); stmt; } _BLEND #else #define _ASM_TEMPLATE(result, errstr, stmt) _BLBEGIN _NOP; _BLEND #define _ASS_TEMPLATE(result, stmt) _BLBEGIN _NOP; _BLEND #define _CHKM_TEMPLATE(result, errstr, stmt) \ _BLBEGIN if (!(result)) { stmt }; _BLEND #define _CHKMS_TEMPLATE(result, stmt) _CHKM_TEMPLATE(result, "", stmt) #endif // --------------------------------------------------------------------------- // ASSERT macros - code will NOT be executed in release builds // --------------------------------------------------------------------------- /** @name Assertion and error checking macros Use for ALL error checking in your code. They are useful to check a return code, print a message and take appropriate action. Generally, ASM_* macros are for code consistency checks, whereas CHK_* macros should be used for errors that could occur also in the final program (like file not found etc.). */ //@{ /** logs error message, returns \c value if \c result==NULL (all ASM_* map to a NOOP in Release builds - use CHKM_* if the code needs to get executed!!!) */ #define ASM_RETVAL(result, errstr, value) \ _ASM_TEMPLATE(result, errstr, return (value)) /// \c ASM_RETVAL for void-functions #define ASM_RET(result, errstr) _ASM_TEMPLATE(result, errstr, return) /// like \c ASM_RETVAL, but does an \c exit(-1) // NOTE: this should NOT have a CHKM-version, as this would result // in a bad exit behaviour! #define ASM_EXIT(result, errstr) _ASM_TEMPLATE(result, errstr, exit(-1)) // --------------------------------------------------------------------------- // CHECK macros - code WILL be executed in release builds // --------------------------------------------------------------------------- /// \c ASM_RETVAL for code that needs to be executed also in release-builds #define CHKM_RETVAL(result, errstr, value) \ _CHKM_TEMPLATE(result, errstr, return (value)) /// #define CHKM_RET(result, errstr) _CHKM_TEMPLATE(result, errstr, return) // --------------------------------------------------------------------------- // Macros not used much // --------------------------------------------------------------------------- /// like \c ASM_RETVAL but without logging anything #define ASS_RETVAL(result, value) _ASS_TEMPLATE(result, return (value)) /// like \c ASM_RET but without logging anything #define ASS_RET(result, errstr) _ASS_TEMPLATE(result, return) #ifndef RELEASE_BUILD /// log error msg if result==NULL, no further action #define ASM(result, errstr) _BLBEGIN if(!(result)) { EOUT(errstr); } _BLEND /** \c if(!result) and logs error msg. Example: \code ASM_IF(res, "failed!") { do_cleanup; } else { do_good_code; } \endcode */ #define ASM_IF(result, errstr) if((!(result)) ? (EOUT(errstr),true) : false) /** only the \c else -path of \c ASM_IF. Example: \code ASM_ELSE(res, "bad input") { do_good_code; } \endcode */ #define ASM_ELSE(result, errstr) if (!(result)) { EOUT(errstr); } else /// \c ASM_IF that also evaluates \c result in release-builds #define CHKM_IF(result, errstr) ASM_IF(result, errstr) /// see \c CHKM_IF #define CHKM_ELSE(result, errstr) ASM_ELSE(result, errstr) /** special: if result is false, output the offending statement (saves you the trouble of inventing error strings!) */ #define ASSERTSELF(result) _BLBEGIN if(!(result)) { EOUT(#result); } _BLEND /** for some Win32 API calls (those where the result contains errorcode): print error msg if statement fails */ #define CHKMS_FORMAT(result) \ _BLBEGIN int hResult = (result); if((hResult) != NULL) \ { EOUT( GetAPIErrorString(hResult) + #result ); } _BLEND /// outputs any OpenGL error accumulated so far #define CHKGL _BLBEGIN GLenum error; \ if ( (error = glGetError()) != GL_NO_ERROR) \ EOUT(gluErrorString(error)); _BLEND; #ifdef WIN32 #include /// checks the heap for consistency (useful for errors seemingly in stl!) #define CHKHEAP _BLBEGIN int hpres; if ( (hpres = _heapchk()) != _HEAPOK ) \ EOUT("Heap error: " << hpres); _BLEND; #else #define CHKHEAP #endif #ifndef DBG_LVL /** override this define in C++-settings to change behaviour of \c DBG and \c REL */ #define DBG_LVL 1 #endif #else #define ASM(result, errstr) _ASM_TEMPLATE(result, errstr, _NOP) #define ASSERTSELF(result) _BLBEGIN _NOP; _BLEND #define CHKMS_FORMAT(result) _BLBEGIN (result); _BLEND #define ASM_IF(result, errstr) if(false) #define ASM_ELSE(result, errstr) if (false) _NOP; else #define CHKM_IF(result, errstr) if(!(result)) #define CHKM_ELSE(result, errstr) if(result) #define CHKGL #define CHKHEAP #ifndef DBG_LVL #define DBG_LVL 0 #endif #endif #if DBG_LVL > 0 /** the following statement/block is only executed in debug-builds (\c DBG_LVL = 1) */ #define DDBG if(true) /** the following statement/block is only executed in release-builds (\c DBG_LVL = 0) */ #define RREL if(false) #else #define DDBG if(false) #define RREL if(true) #endif //@} // --------------------------------------------------------------------------- // Microsoft Win32 API errors // --------------------------------------------------------------------------- /** @name Win32 only These macros should be used for Win32 API-calls */ //@{ /// put a messagebox to screen - WIN32 only void Msg(const char *message, const char *title = NULL); /// gets WIN32 API error message (used by CHKMS-macros) extern char *GetErrorStringMS(unsigned long errorValue); #define EOUTMS(x) _CONOUT("\nERRMS: " << x << HERE) #define WOUTMS(x) _CONOUT("\nWARNMS: " << x << HERE) /// for Win32 API calls: prints error message if statement fails #define CHKMS(result) _CHKMS_TEMPLATE(result, _NOP) /** for Win32 API calls: prints error message and returns value if statement fails */ #define CHKMS_RETVAL(result, value) _CHKMS_TEMPLATE(result, return (value)) /// for Win32 API calls: prints error message and returns if statement fails #define CHKMS_RET(result) _CHKMS_TEMPLATE(result, return) //@} // C4127: conditional expression is constant (because of merror) // C4514 unreferenced inline function has been removed (also in stl.h) #ifdef WIN32 #pragma warning(disable: 4127 4514) #endif #undef ERRSTD #endif // MERROR_H