f3564ed86e
--HG-- branch : develop
1012 lines
28 KiB
C++
1012 lines
28 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef NL_DEBUG_H
|
|
#define NL_DEBUG_H
|
|
|
|
#include "common.h"
|
|
#include "log.h"
|
|
#include "mutex.h"
|
|
#include "mem_displayer.h"
|
|
#include "displayer.h"
|
|
#include "app_context.h"
|
|
|
|
#include <cstdio>
|
|
#include <set>
|
|
|
|
namespace NLMISC
|
|
{
|
|
|
|
#ifdef ASSERT_THROW_EXCEPTION
|
|
#define ASSERT_THROW_EXCEPTION_CODE(exp) ASSERT_THROW_EXCEPTION_CODE_EX(exp, #exp)
|
|
#define ASSERT_THROW_EXCEPTION_CODE_EX(exp, str) if(!(exp)) throw NLMISC::Exception(str" returns false");
|
|
#else
|
|
#define ASSERT_THROW_EXCEPTION_CODE(exp)
|
|
#define ASSERT_THROW_EXCEPTION_CODE_EX(exp, str)
|
|
#endif
|
|
|
|
/** Imposter class to wrap all global access to the nel context for backward compatibility
|
|
* Yoyo note: This was a template before, hence with inline.
|
|
* We removed the inline because there was a hard compilation bug
|
|
* in plugin max under some compiler which caused operator-> to crash.... (don't understand why grrrr)
|
|
* Btw the method is optimized like this (1 call instead of 3 (and one with virtual)) because we added a local cache (_Log)
|
|
* Thus it is much better like this.
|
|
*/
|
|
class CImposterLog
|
|
{
|
|
private:
|
|
typedef CLog *(INelContext::*TAccessor)();
|
|
|
|
// Method to access the Log
|
|
TAccessor _Accessor;
|
|
public:
|
|
CImposterLog(TAccessor accessor);
|
|
CLog* operator -> ();
|
|
operator CLog*();
|
|
CLog &operator ()();
|
|
};
|
|
|
|
|
|
//
|
|
// Externals
|
|
//
|
|
|
|
// NOTE: The following are all NULL until createDebug() has been called at least once
|
|
// NOTE2: You must not use this class before the main() (not inside a static class ctor)
|
|
extern CImposterLog ErrorLog;
|
|
extern CImposterLog WarningLog;
|
|
extern CImposterLog InfoLog;
|
|
extern CImposterLog DebugLog;
|
|
extern CImposterLog AssertLog;
|
|
|
|
extern CMemDisplayer *DefaultMemDisplayer;
|
|
extern CMsgBoxDisplayer *DefaultMsgBoxDisplayer;
|
|
|
|
|
|
//
|
|
// Functions
|
|
//
|
|
|
|
// internal use only
|
|
void createDebug (const char *logPath = NULL, bool logInFile = true, bool eraseLastLog = false);
|
|
|
|
/// Do not call this, unless you know what you're trying to do (it kills debug)!
|
|
void destroyDebug();
|
|
|
|
// call this if you want to change the dir of the log.log file
|
|
void changeLogDirectory(const std::string &dir);
|
|
|
|
// call this if you want to get the dir of the log.log file
|
|
std::string getLogDirectory();
|
|
|
|
// internal breakpoint window
|
|
void enterBreakpoint (const char *message);
|
|
|
|
// if true, the assert generates an assert
|
|
// if false, the assert just displays a warning and continue
|
|
void setAssert (bool assert);
|
|
|
|
// Beep (Windows only, no effect elsewhere)
|
|
void beep( uint freq, uint duration );
|
|
|
|
|
|
typedef std::string (*TCrashCallback)();
|
|
|
|
// this function enables user application to add information in the log when a crash occurs
|
|
void setCrashCallback(TCrashCallback crashCallback);
|
|
|
|
// For Crash report window. allow to know if a crash has already raised in the application
|
|
bool isCrashAlreadyReported();
|
|
void setCrashAlreadyReported(bool state);
|
|
|
|
|
|
// This very amazing macro __FUNCTION__ doesn't exist on VC6, map it to NULL
|
|
#ifdef NL_COMP_VC6
|
|
# define __FUNCTION__ NULL
|
|
#endif
|
|
|
|
|
|
// Macros
|
|
|
|
/// Utility macro used by NL_MACRO_TO_STR to concatenate macro in text message.
|
|
#define NL_MACRO_TO_STR_SUBPART(x) #x
|
|
|
|
/** Use this macro to concatenate macro such
|
|
* You can use this macro to build '#pragma message' friendly macro
|
|
* or to make macro definition into string
|
|
* eg : #define M1 foo
|
|
* #define MESSAGE "the message is "NL_MACRO_TO_STR(M1)
|
|
* #pragma message(MESSAGE)
|
|
* printf(NL_MACRO_TO_STR(M1));
|
|
*/
|
|
#define NL_MACRO_TO_STR(x) NL_MACRO_TO_STR_SUBPART(x)
|
|
|
|
/** the two following macros help to build compiler message using #pragma message
|
|
* on visual C++.
|
|
* The macro generate a message formated like the visual C++ compiler message.
|
|
* NL_LOC_MSG generate informative message and
|
|
* NL_LOC_WRN generate warning message not differentiable to genuine Visual C++ warning.
|
|
* The two message allow automatic source access with F4 or double click in
|
|
* output window.
|
|
*
|
|
* usage : #pragma message( NL_LOC_MGS "your message" )
|
|
*
|
|
* Note : If you want to concatenate another macro to your message, you
|
|
* can append using the NL_MACRO_TO_STR macro like in
|
|
* #define CLASS_NAME TheClassName
|
|
* #pragma message( NL_LOC_MGS "The class name is " NL_MACRO_TO_STR(CLASS_NAME))
|
|
* Note 2 : To show a warning under GCC, use #warning "Your warning here",
|
|
* see nel/net/net_manager.h for an example on how to use these correctly.
|
|
*/
|
|
#define NL_LOC_MSG __FILE__ "(" NL_MACRO_TO_STR(__LINE__) ") : Message: "
|
|
#define NL_LOC_WRN __FILE__ "(" NL_MACRO_TO_STR(__LINE__) ") : Warning Msg: "
|
|
|
|
|
|
/**
|
|
* \def nldebug(exp)
|
|
* Log a debug string. You don't have to put the final new line. It will be automatically append at the end of the string.
|
|
*
|
|
* Example:
|
|
*\code
|
|
void function(sint type)
|
|
{
|
|
// display the type value.
|
|
nldebug("type is %d", type);
|
|
}
|
|
*\endcode
|
|
*/
|
|
#ifdef NL_NO_DEBUG
|
|
# if defined(NL_COMP_VC) && NL_COMP_VC_VERSION >= 71
|
|
# define nldebug __noop
|
|
# else
|
|
# define nldebug 0&&
|
|
# endif
|
|
#else // NL_NO_DEBUG
|
|
extern bool DisableNLDebug;
|
|
# define nldebug if (NLMISC::DisableNLDebug) {} else (NLMISC::createDebug(), NLMISC::INelContext::getInstance().getDebugLog()->setPosition( __LINE__, __FILE__, __FUNCTION__ ), NLMISC::INelContext::getInstance().getDebugLog())->displayNL
|
|
#endif // NL_NO_DEBUG
|
|
|
|
/**
|
|
* \def nlinfo(exp)
|
|
* Same as nldebug but it will be display in debug and in release mode.
|
|
*/
|
|
#ifdef NL_NO_DEBUG
|
|
# if defined(NL_COMP_VC) && NL_COMP_VC_VERSION >= 71
|
|
# define nlinfo __noop
|
|
# else
|
|
# define nlinfo 0&&
|
|
# endif
|
|
#else // NL_NO_DEBUG
|
|
# define nlinfo (NLMISC::createDebug(), NLMISC::INelContext::getInstance().getInfoLog()->setPosition( __LINE__, __FILE__, __FUNCTION__ ), NLMISC::INelContext::getInstance().getInfoLog())->displayNL
|
|
#endif // NL_NO_DEBUG
|
|
|
|
/**
|
|
* \def nlwarning(exp)
|
|
* Same as nlinfo but you have to call this macro when something goes wrong but it's not a fatal error, the program could continue.
|
|
*
|
|
* Example:
|
|
*\code
|
|
void function(char *str)
|
|
{
|
|
// display the type value.
|
|
if (str==NULL)
|
|
{
|
|
nlwarning("in function(), str should not be NULL, assume it's an empty string");
|
|
str="";
|
|
}
|
|
}
|
|
*\endcode
|
|
*/
|
|
|
|
#ifdef NL_NO_DEBUG
|
|
# if defined(NL_COMP_VC) && NL_COMP_VC_VERSION >= 71
|
|
# define nlwarning __noop
|
|
# else
|
|
# define nlwarning 0&&
|
|
# endif
|
|
#else // NL_NO_DEBUG
|
|
# define nlwarning (NLMISC::createDebug(), NLMISC::INelContext::getInstance().getWarningLog()->setPosition( __LINE__, __FILE__, __FUNCTION__ ), NLMISC::INelContext::getInstance().getWarningLog())->displayNL
|
|
#endif // NL_NO_DEBUG
|
|
|
|
/**
|
|
* \def nlerror(exp)
|
|
* Same as nlinfo but you have to call it when you have a fatal error, this macro display the text and \b exit the application
|
|
* automatically. nlerror must be in a try/catch because it generates an EFatalError exception to exit the application.
|
|
*
|
|
*\code
|
|
void function(char *filename)
|
|
{
|
|
FILE *fp = nlfopen (filename, "r");
|
|
if (fp==NULL)
|
|
{
|
|
nlerror("file not found");
|
|
}
|
|
}
|
|
*\endcode
|
|
*/
|
|
#define nlerror (NLMISC::createDebug (), NLMISC::INelContext::getInstance().getErrorLog()->setPosition( __LINE__, __FILE__, __FUNCTION__ ), NLMISC::nlFatalError)
|
|
|
|
|
|
/**
|
|
* \def nlerrornoex(exp)
|
|
* Same as nlerror but it doesn't generate any exceptions. It's used only in very specific case, for example, when you
|
|
* call a nlerror in a catch block (look the service.cpp)
|
|
*/
|
|
#define nlerrornoex (NLMISC::createDebug (), NLMISC::INelContext::getInstance().getErrorLog()->setPosition( __LINE__, __FILE__, __FUNCTION__ ), NLMISC::nlError)
|
|
|
|
|
|
/**
|
|
* \def nlassert(exp)
|
|
* Try the assertion of \c exp. In release mode, nlassert do \b *nothing*. In debug mode, If \c exp is true, nothing happen,
|
|
* otherwise, the program stop on the assertion line.
|
|
*
|
|
* Example:
|
|
*\code
|
|
void function(char *string)
|
|
{
|
|
// the string must not be NULL.
|
|
nlassert(string!=NULL);
|
|
}
|
|
*\endcode
|
|
*/
|
|
|
|
/**
|
|
* \def nlassertonce(exp)
|
|
* Same behaviour as nlassert but the assertion will be test on time only. It's useful when you are under an inner loop and
|
|
* you don't want to flood log file for example.
|
|
*
|
|
* Example:
|
|
*\code
|
|
for (int i = 0; i < Vect.size(); i++)
|
|
{
|
|
// all string must not be NULL, but if it's happen, log only the first time.
|
|
nlassertonce(Vect[i].string!=NULL);
|
|
}
|
|
*\endcode
|
|
*/
|
|
|
|
/**
|
|
* \def nlassertex(exp,str)
|
|
* Same behaviour as nlassert but add a user defined \c str variables args string that will be display with the assert message.
|
|
* Very useful when you can't debug directly on your computer.
|
|
*
|
|
* Example:
|
|
*\code
|
|
void function(sint type)
|
|
{
|
|
// the \c type must be between 0 and 15.
|
|
nlassertex(type>=0&&type<=16, ("type was %d", type));
|
|
// it'll display something like "assertion failed line 10 of test.cpp: type>=&&type<=16, type was 423555463"
|
|
}
|
|
*\endcode
|
|
*/
|
|
|
|
/**
|
|
* \def nlverify(exp)
|
|
* Same behaviour as nlassert but the \c exp will be executed in release mode too (but not tested).
|
|
*
|
|
* Example:
|
|
*\code
|
|
// Load a file and assert if the load failed. This example will work \b only in debug mode because in release mode,
|
|
// nlassert do nothing, the load function will not be called...
|
|
nlassert(load("test.tga"));
|
|
|
|
// If you want to do that anyway, you could call nlverify. In release mode, the assertion will not be tested but
|
|
// the \c load function will be called.
|
|
nlverify(load("test.tga"));
|
|
|
|
// You can also do this:
|
|
bool res = load ("test.tga"));
|
|
assert(res);
|
|
|
|
*\endcode
|
|
*/
|
|
|
|
/**
|
|
* \def nlverifyonce(exp)
|
|
* Same behaviour as nlassertonce but it will execute \c exp in debug and release mode.
|
|
*/
|
|
|
|
/**
|
|
* \def nlverifyex(exp,str)
|
|
* Same behaviour as nlassertex but it will execute \c exp in debug and release mode.
|
|
*/
|
|
|
|
/**
|
|
* \def nlstop
|
|
* It stop the application at this point. It's exactly the same thing as "nlassert(false)".
|
|
* Example:
|
|
*\code
|
|
switch(type)
|
|
{
|
|
case 1: ... break;
|
|
case 2: ... break;
|
|
default: nlstop; // it should never happen...
|
|
}
|
|
*\endcode
|
|
*/
|
|
|
|
/**
|
|
* \def nlstoponce
|
|
* Same as nlassertonce(false);
|
|
*/
|
|
|
|
/**
|
|
* \def nlstopex(exp)
|
|
* Same as nlassertex(false,exp);
|
|
*/
|
|
|
|
// removed because we always check assert (even in release mode) #if defined (NL_OS_WINDOWS) && defined (NL_DEBUG)
|
|
#if defined(NL_OS_WINDOWS)
|
|
#define NLMISC_BREAKPOINT __debugbreak()
|
|
#elif defined(NL_OS_UNIX) && defined(NL_COMP_GCC)
|
|
#define NLMISC_BREAKPOINT __builtin_trap()
|
|
#else
|
|
#define NLMISC_BREAKPOINT abort()
|
|
#endif
|
|
|
|
// Internal, don't use it (make smaller assert code)
|
|
extern bool _assert_stop(bool &ignoreNextTime, sint line, const char *file, const char *funcName, const char *exp);
|
|
extern void _assertex_stop_0(bool &ignoreNextTime, sint line, const char *file, const char *funcName, const char *exp);
|
|
extern bool _assertex_stop_1(bool &ignoreNextTime);
|
|
|
|
// removed because we always check assert (even in release mode) #if defined(NL_DEBUG)
|
|
|
|
#ifdef NL_NO_DEBUG
|
|
# define nlassert(exp) if(false)
|
|
# define nlassertonce(exp) if(false)
|
|
# define nlassertex(exp, str) if(false)
|
|
# define nlverify(exp) { exp; }
|
|
# define nlverifyonce(exp) { exp; }
|
|
# define nlverifyex(exp, str) { exp; }
|
|
#else // NL_NO_DEBUG
|
|
|
|
# ifdef NL_OS_UNIX
|
|
|
|
// Linux set of asserts is reduced due to that there is no message box displayer
|
|
|
|
#define nlassert(exp) \
|
|
do { \
|
|
if (!(exp)) { \
|
|
NLMISC::createDebug (); \
|
|
NLMISC::INelContext::getInstance().getAssertLog()->setPosition (__LINE__, __FILE__, __FUNCTION__); \
|
|
NLMISC::INelContext::getInstance().getAssertLog()->displayNL ("\"%s\" ", #exp); \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define nlassertonce(exp) nlassert(exp)
|
|
|
|
#define nlassertex(exp, str) \
|
|
do { \
|
|
if (!(exp)) { \
|
|
NLMISC::createDebug (); \
|
|
NLMISC::INelContext::getInstance().getAssertLog()->setPosition (__LINE__, __FILE__, __FUNCTION__); \
|
|
NLMISC::INelContext::getInstance().getAssertLog()->displayNL ("\"%s\" ", #exp); \
|
|
NLMISC::INelContext::getInstance().getAssertLog()->displayRawNL str; \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define nlverify(exp) nlassert(exp)
|
|
#define nlverifyonce(exp) nlassert(exp)
|
|
#define nlverifyex(exp, str) nlassertex(exp, str)
|
|
|
|
# else // NL_OS_UNIX
|
|
|
|
#define nlassert(exp) \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
bool _expResult_ = (exp) ? true : false; \
|
|
if (!ignoreNextTime && !_expResult_) { \
|
|
if(NLMISC::_assert_stop(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, #exp)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
ASSERT_THROW_EXCEPTION_CODE_EX(_expResult_, #exp) \
|
|
} while(0)
|
|
|
|
#define nlassertonce(exp) \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
if (!ignoreNextTime && !(exp)) { \
|
|
ignoreNextTime = true; \
|
|
if(NLMISC::_assert_stop(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, #exp)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define nlassertex(exp, str) \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
bool _expResult_ = (exp) ? true : false; \
|
|
if (!ignoreNextTime && !_expResult_) { \
|
|
NLMISC::_assertex_stop_0(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, #exp); \
|
|
NLMISC::INelContext::getInstance().getAssertLog()->displayRawNL str; \
|
|
if(NLMISC::_assertex_stop_1(ignoreNextTime)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
ASSERT_THROW_EXCEPTION_CODE_EX(_expResult_, #exp) \
|
|
} while(0)
|
|
|
|
#define nlverify(exp) \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
bool _expResult_ = (exp) ? true : false; \
|
|
if (!_expResult_ && !ignoreNextTime) { \
|
|
if(NLMISC::_assert_stop(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, #exp)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
ASSERT_THROW_EXCEPTION_CODE_EX(_expResult_, #exp) \
|
|
} while(0)
|
|
|
|
#define nlverifyonce(exp) \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
bool _expResult_ = (exp) ? true : false; \
|
|
if (!_expResult_ && !ignoreNextTime) { \
|
|
ignoreNextTime = true; \
|
|
if(NLMISC::_assert_stop(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, #exp)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define nlverifyex(exp, str) \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
bool _expResult_ = (exp) ? true : false; \
|
|
if (!_expResult_ && !ignoreNextTime) { \
|
|
NLMISC::_assertex_stop_0(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, #exp); \
|
|
NLMISC::INelContext::getInstance().getAssertLog()->displayRawNL str; \
|
|
if(NLMISC::_assertex_stop_1(ignoreNextTime)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
ASSERT_THROW_EXCEPTION_CODE_EX(_expResult_, #exp) \
|
|
} while(0)
|
|
|
|
# endif // NL_OS_UNIX
|
|
|
|
#endif // NL_NO_DEBUG
|
|
|
|
#define nlunreferenced(identifier) (void)identifier
|
|
|
|
#define nlstop \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
if (!ignoreNextTime) { \
|
|
if(NLMISC::_assert_stop(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, NULL)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
ASSERT_THROW_EXCEPTION_CODE(false) \
|
|
} while(0)
|
|
|
|
#define nlstoponce \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
if (!ignoreNextTime) { \
|
|
ignoreNextTime = true; \
|
|
if(NLMISC::_assert_stop(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, NULL)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
} while(0)
|
|
|
|
|
|
#define nlstopex(str) \
|
|
do { \
|
|
static bool ignoreNextTime = false; \
|
|
if (!ignoreNextTime) { \
|
|
NLMISC::_assertex_stop_0(ignoreNextTime, __LINE__, __FILE__, __FUNCTION__, NULL); \
|
|
NLMISC::INelContext::getInstance().getAssertLog()->displayRawNL str; \
|
|
if(NLMISC::_assertex_stop_1(ignoreNextTime)) \
|
|
NLMISC_BREAKPOINT; \
|
|
} \
|
|
} while(0)
|
|
|
|
|
|
struct EFatalError : public Exception
|
|
{
|
|
EFatalError() : Exception( "nlerror() called" ) {}
|
|
};
|
|
|
|
class ETrapDebug : public Exception
|
|
{
|
|
};
|
|
|
|
// undef default assert to force people to use nlassert() instead of assert()
|
|
// if NL_MAP_ASSERT is set we map assert to nlassert instead of removing it
|
|
// this makes it compatible with zeroc's ICE network library
|
|
|
|
#ifdef NL_MAP_ASSERT
|
|
# ifdef assert
|
|
# undef assert
|
|
# define assert nlassert
|
|
# endif
|
|
#else
|
|
# ifdef assert
|
|
# undef assert
|
|
# define assert(a) you_must_not_use_assert___use_nl_assert___read_debug_h_file
|
|
# endif
|
|
#endif
|
|
|
|
|
|
/// Get the call stack and set it with result
|
|
void getCallStack(std::string &result, sint skipNFirst = 0);
|
|
|
|
/// Get the call stack and the logs and set it with result
|
|
void getCallStackAndLog (std::string &result, sint skipNFirst = 0);
|
|
|
|
/**
|
|
* safe_cast<>: this is a function which nlassert() a dynamic_cast in Debug, and just do a static_cast in release.
|
|
* So slow check is made in debug, but only fast cast is made in release.
|
|
*/
|
|
template<class T, class U> inline T safe_cast(U o)
|
|
{
|
|
// NB: must check debug because assert may still be here in release
|
|
#ifdef NL_DEBUG
|
|
nlassert(dynamic_cast<T>(o));
|
|
#endif
|
|
return static_cast<T>(o);
|
|
}
|
|
|
|
/**
|
|
* type_cast<>: this is a function which nlassert() a dynamic_cast in Debug, and just do a static_cast in release.
|
|
* So slow check is made in debug, but only fast cast is made in release.
|
|
* Differs from safe_cast by allowinf NULL objets. (ask Stephane LE DORZE for more explanations).
|
|
*/
|
|
template<class T, class U> inline T type_cast(U o)
|
|
{
|
|
// NB: must check debug because assert may still be here in release
|
|
#ifdef NL_DEBUG
|
|
if (o)
|
|
nlassert(dynamic_cast<T>(o));
|
|
#endif
|
|
// optimization made to check pointer validity before address translation. (hope it works on linux).
|
|
if ((size_t)(static_cast<T>((U)0x0400)) == (size_t)((U)0x0400))
|
|
{
|
|
return static_cast<T>(o);
|
|
}
|
|
else
|
|
{
|
|
return (o==0)?0:static_cast<T>(o);
|
|
}
|
|
}
|
|
|
|
/** Compile time assertion
|
|
*/
|
|
#ifdef NL_ISO_CPP0X_AVAILABLE
|
|
# define nlctassert(cond) static_assert(cond, "Compile time assert in "#cond)
|
|
#else
|
|
# define nlctassert(cond) (void)sizeof(uint[(cond) ? 1 : 0])
|
|
#endif
|
|
|
|
/**
|
|
* Allow to verify an object was accessed before its destructor call.
|
|
* For instance, it could be used to check if the user take care of method call return.
|
|
* ex:
|
|
* CMustConsume<TErrorCode> foo()
|
|
* {
|
|
* ...
|
|
* return ErrorInvalidateType; // part of TErrorCode enum.
|
|
* }
|
|
* Exclusive implementation samples:
|
|
* TerrorCode code=foo().consumeValue(); // Good!
|
|
* foo().consume(); // Good!
|
|
* TerrorCode code=foo(); // Mistake!
|
|
* foo(); // Will cause an assert at next ending brace during execution time.
|
|
* TerrorCode code=foo().Value(); // Will cause an assert at next ending brace during execution time.
|
|
* (ask Stephane LE DORZE for more explanations).
|
|
*/
|
|
|
|
// Need a breakpoint in the assert / verify macro
|
|
extern bool DebugNeedAssert;
|
|
|
|
// Internal process, don't use it
|
|
extern bool NoAssert;
|
|
|
|
|
|
template<class T>
|
|
class CMustConsume
|
|
{
|
|
public:
|
|
CMustConsume(const T &val) : Value(val)
|
|
#if !FINAL_VERSION
|
|
, Consumed(false)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
~CMustConsume()
|
|
{
|
|
#if !FINAL_VERSION
|
|
nlassert(Consumed == true);
|
|
#endif
|
|
}
|
|
|
|
// Get the value without validating the access.
|
|
const T &value() const
|
|
{
|
|
return Value;
|
|
}
|
|
|
|
operator const T &() const
|
|
{
|
|
#if !FINAL_VERSION
|
|
Consumed = true;
|
|
#endif
|
|
return Value;
|
|
}
|
|
|
|
// Get the value and validate the access.
|
|
const T &consumeValue() const
|
|
{
|
|
#if !FINAL_VERSION
|
|
Consumed = true;
|
|
#endif
|
|
return Value;
|
|
}
|
|
// Only consume the access.
|
|
void consume() const
|
|
{
|
|
#if !FINAL_VERSION
|
|
Consumed = true;
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
T Value;
|
|
#if !FINAL_VERSION
|
|
mutable bool Consumed;
|
|
#endif
|
|
};
|
|
|
|
/// Data for instance counting
|
|
struct TInstanceCounterData
|
|
{
|
|
sint32 _InstanceCounter;
|
|
sint32 _DeltaCounter;
|
|
const char *_ClassName;
|
|
bool _Touched;
|
|
|
|
TInstanceCounterData(const char *className);
|
|
|
|
~TInstanceCounterData();
|
|
};
|
|
|
|
// forward declaration for members of CInstanceCounterManager
|
|
class CInstanceCounterLocalManager;
|
|
|
|
// The singleton used to display the instance counter
|
|
class CInstanceCounterManager
|
|
{
|
|
// NLMISC_SAFE_SINGLETON_DECL(CInstanceCounterManager);
|
|
private:
|
|
/* declare private constructors*/
|
|
CInstanceCounterManager () {}
|
|
CInstanceCounterManager (const CInstanceCounterManager &) {}
|
|
/* the local static pointer to the singleton instance */
|
|
static CInstanceCounterManager *_Instance;
|
|
public:
|
|
static CInstanceCounterManager &getInstance()
|
|
{
|
|
if (_Instance == NULL)
|
|
{
|
|
/* the nel context MUST be initialised */
|
|
// nlassert(NLMISC::NelContext != NULL);
|
|
void *ptr = NLMISC::INelContext::getInstance().getSingletonPointer("CInstanceCounterManager");
|
|
if (ptr == NULL)
|
|
{
|
|
/* allocate the singleton and register it */
|
|
_Instance = new CInstanceCounterManager;
|
|
NLMISC::INelContext::getInstance().setSingletonPointer("CInstanceCounterManager", _Instance);
|
|
}
|
|
else
|
|
{
|
|
_Instance = reinterpret_cast<CInstanceCounterManager*>(ptr);
|
|
}
|
|
}
|
|
return *_Instance;
|
|
}
|
|
private:
|
|
public:
|
|
|
|
std::string displayCounters() const;
|
|
|
|
void resetDeltaCounter();
|
|
|
|
uint32 getInstanceCounter(const std::string &className) const;
|
|
sint32 getInstanceCounterDelta(const std::string &className) const;
|
|
|
|
private:
|
|
|
|
friend class CInstanceCounterLocalManager;
|
|
|
|
void registerInstaceCounterLocalManager(CInstanceCounterLocalManager *localMgr);
|
|
void unregisterInstaceCounterLocalManager(CInstanceCounterLocalManager *localMgr);
|
|
|
|
// static CInstanceCounterManager *_Instance;
|
|
std::set<CInstanceCounterLocalManager*> _InstanceCounterMgrs;
|
|
};
|
|
|
|
//
|
|
// Local instance counter
|
|
//
|
|
class CInstanceCounterLocalManager
|
|
{
|
|
public:
|
|
static CInstanceCounterLocalManager &getInstance()
|
|
{
|
|
if (_Instance == NULL)
|
|
{
|
|
_Instance = new CInstanceCounterLocalManager;
|
|
}
|
|
return *_Instance;
|
|
}
|
|
|
|
static void releaseInstance()
|
|
{
|
|
if (_Instance != NULL)
|
|
{
|
|
delete _Instance;
|
|
_Instance = NULL;
|
|
}
|
|
}
|
|
|
|
void registerInstanceCounter(TInstanceCounterData *counter)
|
|
{
|
|
_InstanceCounters.insert(counter);
|
|
}
|
|
|
|
void unregisterInstanceCounter(TInstanceCounterData *counter);
|
|
|
|
~CInstanceCounterLocalManager()
|
|
{
|
|
CInstanceCounterManager::getInstance().unregisterInstaceCounterLocalManager(this);
|
|
}
|
|
|
|
private:
|
|
friend class CInstanceCounterManager;
|
|
friend class INelContext;
|
|
|
|
CInstanceCounterLocalManager()
|
|
{
|
|
}
|
|
|
|
void registerLocalManager()
|
|
{
|
|
CInstanceCounterManager::getInstance().registerInstaceCounterLocalManager(this);
|
|
}
|
|
|
|
static CInstanceCounterLocalManager *_Instance;
|
|
std::set<TInstanceCounterData*> _InstanceCounters;
|
|
};
|
|
|
|
|
|
/** Utility to count instance of class.
|
|
* This class is designed to be lightweight and to trace
|
|
* the number of instance of 'tagged' class.
|
|
* Commands are provided to display the actual number
|
|
* of object of each class and to compute delta
|
|
* between two call of the displayer.
|
|
* Usage is simple, you just have to put a macro
|
|
* inside the class definition to trace it's allocation
|
|
* and a macro in the implementation file.
|
|
* The macro only add a compiler minimum size for member
|
|
* struct of 0 octets (witch can be 0 or 1 octet, compiler
|
|
* dependent).
|
|
* usage :
|
|
*
|
|
* In the header :
|
|
* class foo // This is the class we want to count instance
|
|
* {
|
|
* NL_INSTANCE_COUNTER_DECL(foo);
|
|
* }
|
|
* In the cpp :
|
|
* NL_INSTANCE_COUNTER_IMPL(foo);
|
|
*/
|
|
#define NL_INSTANCE_COUNTER_DECL(className) \
|
|
public: \
|
|
struct className##InstanceCounter \
|
|
{ \
|
|
className##InstanceCounter() \
|
|
{ \
|
|
_InstanceCounterData._InstanceCounter++; \
|
|
_InstanceCounterData._Touched = true; \
|
|
} \
|
|
className##InstanceCounter(const className##InstanceCounter &/* other */) \
|
|
{ \
|
|
_InstanceCounterData._InstanceCounter++; \
|
|
_InstanceCounterData._Touched = true; \
|
|
} \
|
|
\
|
|
~className##InstanceCounter()\
|
|
{ \
|
|
_InstanceCounterData._InstanceCounter--; \
|
|
} \
|
|
static sint32 getInstanceCounter() \
|
|
{ \
|
|
return _InstanceCounterData._InstanceCounter; \
|
|
} \
|
|
static sint32 getInstanceCounterDelta() \
|
|
{ \
|
|
return _InstanceCounterData._InstanceCounter - _InstanceCounterData._DeltaCounter; \
|
|
} \
|
|
static NLMISC::TInstanceCounterData _InstanceCounterData; \
|
|
}; \
|
|
\
|
|
className##InstanceCounter _##className##InstanceCounter; \
|
|
private:
|
|
|
|
/// The macro to make the implementation of the counter
|
|
#define NL_INSTANCE_COUNTER_IMPL(className) NLMISC::TInstanceCounterData className::className##InstanceCounter::_InstanceCounterData(#className);
|
|
|
|
/// An utility macro to get the instance counter for a class
|
|
#define NL_GET_LOCAL_INSTANCE_COUNTER(className) className::className##InstanceCounter::getInstanceCounter()
|
|
#define NL_GET_INSTANCE_COUNTER(className) NLMISC::CInstanceCounterManager::getInstance().getInstanceCounter(#className)
|
|
|
|
/// An utility macro to get the delta since the last counter reset.
|
|
#define NL_GET_LOCAL_INSTANCE_COUNTER_DELTA(className) className::className##InstanceCounter::getInstanceCounterDelta()
|
|
#define NL_GET_INSTANCE_COUNTER_DELTA(className) NLMISC::CInstanceCounterManager::getInstance().getInstanceCounterDelta(#className)
|
|
|
|
//
|
|
// Following are internal functions, you should never use them
|
|
//
|
|
|
|
/// Never use this function (internal use only)
|
|
void nlFatalError (const char *format, ...);
|
|
|
|
/// Never use this function but call the nlerror macro (internal use only)
|
|
void nlError (const char *format, ...);
|
|
|
|
#define NL_CRASH_DUMP_FILE "nel_debug.dmp"
|
|
|
|
// Standard API to retrieve error code and test for windows or unix platform
|
|
|
|
/// Return the last error code generated by a system call
|
|
int getLastError();
|
|
|
|
/// Return a readable text according to the error code submited
|
|
std::string formatErrorMessage(int errorCode);
|
|
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
// A handy 'nldebug', 'nlinfo' & 'nlwarning' override system
|
|
//-------------------------------------------------------------------------------------------------
|
|
//
|
|
// The system includes a set of object classes
|
|
// To override one or more of the standard NeL log channels one simply instantiates the appropriate class
|
|
// with the new log channel as a parameter.
|
|
// The log channel in question will revert to its previous value on destruction of the override object
|
|
//
|
|
// Usage Example:
|
|
//
|
|
// void doSomething()
|
|
// {
|
|
// nlinfo("bla");
|
|
// nlwarning("bla");
|
|
// }
|
|
//
|
|
// NLMISC_COMMAND(bla,"bla","bla")
|
|
// {
|
|
// CNLLogOverride(&log);
|
|
// doSomething();
|
|
// return true;
|
|
// }
|
|
//
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
class CNLDebugOverride
|
|
{
|
|
public:
|
|
CNLDebugOverride(NLMISC::CLog *debugLog)
|
|
{
|
|
nlassert(debugLog!=NULL);
|
|
_OldValue=NLMISC::DebugLog;
|
|
nlassert(_OldValue!=NULL);
|
|
NLMISC::INelContext::getInstance().setDebugLog(debugLog);
|
|
}
|
|
~CNLDebugOverride()
|
|
{
|
|
NLMISC::INelContext::getInstance().setDebugLog(_OldValue);
|
|
}
|
|
private:
|
|
// prohibit copy
|
|
CNLDebugOverride(const CNLDebugOverride&);
|
|
NLMISC::CLog *_OldValue;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
class CNLInfoOverride
|
|
{
|
|
public:
|
|
CNLInfoOverride(NLMISC::CLog *infoLog)
|
|
{
|
|
nlassert(infoLog!=NULL);
|
|
_OldValue=NLMISC::InfoLog;
|
|
nlassert(_OldValue!=NULL);
|
|
NLMISC::INelContext::getInstance().setInfoLog(infoLog);
|
|
}
|
|
~CNLInfoOverride()
|
|
{
|
|
NLMISC::INelContext::getInstance().setInfoLog(_OldValue);
|
|
}
|
|
private:
|
|
// prohibit copy
|
|
CNLInfoOverride(const CNLInfoOverride&);
|
|
NLMISC::CLog *_OldValue;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
class CNLWarningOverride
|
|
{
|
|
public:
|
|
CNLWarningOverride(NLMISC::CLog *warningLog)
|
|
{
|
|
nlassert(warningLog!=NULL);
|
|
_OldValue=NLMISC::WarningLog;
|
|
nlassert(_OldValue!=NULL);
|
|
NLMISC::INelContext::getInstance().setWarningLog(warningLog);
|
|
}
|
|
~CNLWarningOverride()
|
|
{
|
|
NLMISC::INelContext::getInstance().setWarningLog(_OldValue);
|
|
}
|
|
private:
|
|
// prohibit copy
|
|
CNLWarningOverride(const CNLWarningOverride&);
|
|
NLMISC::CLog *_OldValue;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
class CNLLogOverride
|
|
{
|
|
public:
|
|
CNLLogOverride(NLMISC::CLog *commonLog): _DebugLog(commonLog), _InfoLog(commonLog), _WarningLog(commonLog) {}
|
|
|
|
private:
|
|
CNLDebugOverride _DebugLog;
|
|
CNLInfoOverride _InfoLog;
|
|
CNLWarningOverride _WarningLog;
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
class CNLSmartLogOverride
|
|
{
|
|
public:
|
|
CNLSmartLogOverride(NLMISC::CLog *commonLog):
|
|
_DebugLog(commonLog==NLMISC::InfoLog?NLMISC::DebugLog:commonLog),
|
|
_InfoLog(commonLog),
|
|
_WarningLog(commonLog==NLMISC::InfoLog?NLMISC::WarningLog:commonLog)
|
|
{}
|
|
|
|
private:
|
|
CNLDebugOverride _DebugLog;
|
|
CNLInfoOverride _InfoLog;
|
|
CNLWarningOverride _WarningLog;
|
|
};
|
|
|
|
|
|
|
|
|
|
} // NLMISC
|
|
|
|
#endif // NL_DEBUG_H
|
|
|
|
/* End of debug.h */
|