khanat-opennel-code/code/ryzom/common/src/game_share/utils.h

804 lines
23 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// 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 UTILS_H
#define UTILS_H
//-------------------------------------------------------------------------------------------------
// includes
//-------------------------------------------------------------------------------------------------
#include "nel/misc/types_nl.h"
#include "nel/misc/common.h"
#include "nel/misc/debug.h"
#include "nel/misc/sstring.h"
#include <list>
//-------------------------------------------------------------------------------------------------
// UTILITY FUNCTIONS
//-------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline std::string capitalize(const std::string & s)
{
if ( s.empty() )
return s;
return NLMISC::toUpper( s.substr(0,1) ) + NLMISC::toLower( s.substr(1,std::string::npos) );
}
inline ucstring capitalize(const ucstring & s)
{
if ( s.empty() )
return s;
return NLMISC::toUpper( s.substr(0,1) ) + NLMISC::toLower( s.substr(1,std::string::npos) );
}
//-------------------------------------------------------------------------------------------------
// HANDY MACROS - For forcing the pre-preprocessor to evaluate concatenation operations nicely
//-------------------------------------------------------------------------------------------------
// a Few examples:
// ---------------
//
// #define TOTO TATA
// MACRO_CONCAT(a,TOTO) => aTOTO
// MACRO_CONCAT2(a,TOTO) => aTATA
// MACRO_TOTXT(TOTO) => "TOTO"
// MACRO_TOTXT2(TOTO) => "TATA"
//
/// MACRO_CONCAT(a,__LINE__) => a__LINE__
// MACRO_CONCAT2(a,__LINE__) => a123
// MACRO_TOTXT(__LINE__) => "__LINE__"
// MACRO_TOTXT2(__LINE__) => "123"
//
// __FILE_LINE__ => "utils.h:123:"
#define MACRO_CONCAT(a,b) a##b
#define MACRO_CONCAT2(a,b) CONCAT(a,b)
#define MACRO_TOTXT(a) #a
#define MACRO_TOTXT2(a) TOTXT(a)
#define __FILE_LINE__ __FILE__ ":"TOTXT2(__LINE__)":"
//-------------------------------------------------------------------------------------------------
// LOGGING / DEBUGGING MACROS
//-------------------------------------------------------------------------------------------------
#ifdef NL_DEBUG
#define DEBUG_STOP nlstop
#define nlassertd(a) nlassert(a)
#else
#define DEBUG_STOP \
std::string stack; \
NLMISC::getCallStack(stack); \
std::vector<std::string> contexts;\
NLMISC::explode(stack, std::string("\n"), contexts);\
nldebug("Dumping callstack :"); \
for (uint i=0; i<contexts.size(); ++i) \
nldebug(" %3u : %s", i, contexts[i].c_str());
#define nlassertd(a) if (0) ; else
#endif
// the following set of definess can be undefined and re-defined to add user code to execute
// systematically when GIVEUP, DROP or BOMB macros are triggered
// eg #define ON_BOMB logError(__FILE__,__LINE__,msg);
#define ON_GIVEUP
#define ON_DROP
#define ON_BOMB
// info and warning macros
#define INFO(msg) nlinfo((NLMISC::CSString()<<msg).c_str());
#define WARN(msg) nlwarning((NLMISC::CSString()<<msg).c_str());
// unconditional abort macros
#define STOP(msg) { WARN(msg) DEBUG_STOP }
#define GIVEUP(msg,action) { INFO(msg) { ON_GIVEUP; } { action; } }
#define DROP(msg,action) { WARN(msg) { ON_DROP; } { action; } }
#define BOMB(msg,action) { STOP(msg) { ON_BOMB; } { action; } }
// conditional warn and abort macros
#define GIVEUP_IF(condition,msg,action) if (!(condition));else GIVEUP(msg,action)
#define WARN_IF(condition,msg) if (!(condition));else WARN(msg)
#define DROP_IF(condition,msg,action) if (!(condition));else DROP(msg,action)
#define BOMB_IF(condition,msg,action) if (!(condition));else BOMB(msg,action)
#define STOP_IF(condition,msg) if (!(condition));else STOP(msg)
// testing for variable value changes
#define ON_CHANGE(type,var,code)\
class __COnChange##var\
{\
public:\
const type& _Var;\
const type _OldVal;\
__COnChange##var(const type& var): _Var(var), _OldVal(var) {}\
~__COnChange##var() { if(_OldVal!=_Var) { code; } }\
}\
__onChange##__LINE__(var);
#define ON_CHANGE_ASSERT(type,var) ON_CHANGE(type,var,nlerror("Variable "#var" changed from %s to %s",NLMISC::toString(_OldVal).c_str(),NLMISC::toString(_Var).c_str()))
//-------------------------------------------------------------------------------------------------
// 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;
//};
//-------------------------------------------------------------------------------------------------
// A Little CallStack system
//-------------------------------------------------------------------------------------------------
// example:
// void traceTest2(int i)
// {
// CSTRACE;
// if (i<2)
// traceTest2(++i);
// else
// SHOW_CALLSTACK;
// }
//
// void traceTest()
// {
// CSTRACE_MSG("foo");
// traceTest2(0);
// {
// int i=0;
// CSTRACE_VAR(int,i);
// CSTRACE_VAL(int,i);
// i=1;
// SHOW_CALLSTACK;
// }
// WARN_CALLSTACK;
// }
//
// output:
// INF 3980: >>test.cpp:21: Call Stack:
// INF 3980: >>test.cpp:17
// INF 3980: >>test.cpp:17
// INF 3980: >>test.cpp:17
// INF 3980: >>test.cpp:26: foo
// INF 3980:
// INF 3980: >>test.cpp:33: Call Stack:
// INF 3980: >>test.cpp:31: i=[0]
// INF 3980: >>test.cpp:30: i=>[1]
// INF 3980: >>test.cpp:26: foo
// INF 3980:
// WRN 3980: >>test.cpp:35: Call Stack:
// WRN 3980: >>test.cpp:26: foo
// WRN 3980:
//-------------------------------------------------------------------------------------------------
// A Little CallStack system - MACROS
//-------------------------------------------------------------------------------------------------
// CSTRACE - displays source file and line number
// CSTRACE_MSG(msg) - as with trace but with additional simple text message eg TRACE_MSG("hello world")
// CSTRACE_VAL(type,name) - displays a value (calculated at the moment that the trace is created)
// CSTRACE_VAR(type,name) - displays the value of the given variable at the moment that callstack is displayed
// SHOW_CALLSTACK - displays the callstack using NLMISC::InfoLog
// WARN_CALLSTACK - displays the callstack using NLMISC::WarningLog
#define CSTRACE\
class __CCallStackEntry##__LINE__: public ICallStackEntry\
{\
public:\
virtual void displayEntry(NLMISC::CLog& log) const\
{\
log.displayNL(">>"__FILE__":%d",__LINE__);\
}\
}\
__callStackEntry##__LINE__;
#define CSTRACE_MSG(msg)\
class __CCallStackEntry##__LINE__: public ICallStackEntry\
{\
public:\
virtual void displayEntry(NLMISC::CLog& log) const\
{\
log.displayNL(">>"__FILE__":%d: %s",__LINE__,msg);\
}\
}\
__callStackEntry##__LINE__;
#define CSTRACE_VAL(type,var)\
class __TraceVal_##var: public ICallStackEntry\
{\
public:\
__TraceVal_##var(const type& var): _Val(var) \
{\
}\
virtual void displayEntry(NLMISC::CLog& log) const\
{\
log.displayNL(">>"__FILE__":%d: %s=[%s]",__LINE__,#var,NLMISC::toString(_Val).c_str());\
}\
const type _Val;\
}\
__traceVal_##var(var);
#define CSTRACE_VAR(type,var)\
class __TraceVar_##var: public ICallStackEntry\
{\
public:\
__TraceVar_##var(const type& var): _Var(var) \
{\
}\
virtual void displayEntry(NLMISC::CLog& log) const\
{\
log.displayNL(">>"__FILE__":%d: %s=>[%s]",__LINE__,#var,NLMISC::toString(_Var).c_str());\
}\
const type& _Var;\
}\
__traceVar_##var(var);
#define SHOW_CALLSTACK { CSTRACE_MSG("Call Stack:"); CCallStackSingleton::display(NLMISC::InfoLog); }
#define WARN_CALLSTACK { CSTRACE_MSG("Call Stack:"); CCallStackSingleton::display(NLMISC::WarningLog); }
//-------------------------------------------------------------------------------------------------
// A Little CallStack system - Private stack entry base class
//-------------------------------------------------------------------------------------------------
class ICallStackEntry
{
public:
ICallStackEntry();
~ICallStackEntry();
void displayStack(NLMISC::CLog& log) const;
virtual void displayEntry(NLMISC::CLog& log) const=0;
private:
ICallStackEntry* _Next;
};
//-------------------------------------------------------------------------------------------------
// A Little CallStack system - Public Singleton Class
//-------------------------------------------------------------------------------------------------
class CCallStackSingleton
{
public:
static ICallStackEntry* getTopStackEntry();
static void setTopStackEntry(ICallStackEntry* newEntry);
static void display(NLMISC::CLog *log=NLMISC::InfoLog);
private:
// this is a singleton so prohibit public construction
CCallStackSingleton() {}
// encapsulation of a variable to make it a singleton
static ICallStackEntry*& topStackEntry();
};
//-------------------------------------------------------------------------------------------------
// A Little CallStack system - Public Singleton inlines
//-------------------------------------------------------------------------------------------------
inline ICallStackEntry* CCallStackSingleton::getTopStackEntry()
{
return topStackEntry();
}
inline void CCallStackSingleton::setTopStackEntry(ICallStackEntry* newEntry)
{
topStackEntry()= newEntry;
}
inline void CCallStackSingleton::display(NLMISC::CLog *log)
{
nlassert(log!=NULL);
getTopStackEntry()->displayStack(*log);
log->displayNL("");
}
inline ICallStackEntry*& CCallStackSingleton::topStackEntry()
{
static ICallStackEntry* stackEntry=NULL;
return stackEntry;
}
//-------------------------------------------------------------------------------------------------
// A Little CallStack system - Private stack entry base inlines
//-------------------------------------------------------------------------------------------------
inline ICallStackEntry::ICallStackEntry()
{
// add self to the call stack
_Next=CCallStackSingleton::getTopStackEntry();
CCallStackSingleton::setTopStackEntry(this);
}
inline ICallStackEntry::~ICallStackEntry()
{
// if this object is in the call stack then pop items off the top of the stack
// until this object is no longer in the stack
while (_Next!=this)
{
// get a pointer to the top object on the call stack
ICallStackEntry* entry= CCallStackSingleton::getTopStackEntry();
nlassertd(entry!=NULL);
// pop the object off the callstack
CCallStackSingleton::setTopStackEntry(entry->_Next);
// mark object as no longer in callstack
entry->_Next=entry;
}
}
inline void ICallStackEntry::displayStack(NLMISC::CLog& log) const
{
// stop recursing when we reach a NULL object
// (this is implemented in this way in order to ximplify call code)
if (this==NULL)
return;
// display this entry
displayEntry(log);
// recurse through call stack
_Next->displayStack(log);
}
//-------------------------------------------------------------------------------------------------
// HANDY Utility methods...
//-------------------------------------------------------------------------------------------------
inline NLMISC::CVectorSString& operator<<(NLMISC::CVectorSString& vect,const NLMISC::CSString s)
{
vect.push_back(s);
return vect;
}
template<class T> inline T& vectAppend(std::vector<T>& vect)
{
vect.resize(vect.size()+1);
return vect.back();
}
template<class T0,class T1> inline void vectInsert(std::vector<T0>& vect,const T1& value)
{
for (uint32 i=0;i<vect.size();++i)
if (vect[i]== value)
return;
vect.push_back(value);
}
template<class T> inline T& listAppend(std::list<T>& list)
{
list.resize(list.size()+1);
return list.back();
}
inline NLMISC::CSString popString(NLMISC::IStream& stream)
{
std::string s;
stream.serial(s);
return s;
}
inline sint32 popSint(NLMISC::IStream& stream)
{
sint32 val;
stream.serial(val);
return val;
}
inline uint32 popUint(NLMISC::IStream& stream)
{
uint32 val;
stream.serial(val);
return val;
}
inline bool popBool(NLMISC::IStream& stream)
{
bool val;
stream.serial(val);
return val;
}
template<class T> inline void pushToStream(NLMISC::IStream& stream,const T& value)
{
stream.serial(const_cast<T&>(value));
}
inline void pushToStream(NLMISC::IStream& stream,const char* txt)
{
std::string s(txt);
stream.serial(s);
}
//-------------------------------------------------------------------------------------------------
// HANDY IPtr and IConstPtr CLASSES
//-------------------------------------------------------------------------------------------------
// This class gives a base that can be specialised in order to make pointer encapsulation classes
// it offers the basic standard methods that you have to define every time in the normal way...
template <class C>
class IPtr
{
public:
IPtr() { _Ptr= NULL; }
IPtr(C* p) { operator=(p); }
IPtr& operator=(C* p) { _Ptr=p; return *this; }
IPtr& operator=(IPtr& other) { _Ptr=other._Ptr; return *this; }
IPtr& operator++() { ++_Ptr; return *this; }
IPtr& operator--() { --_Ptr; return *this; }
const C* operator->() const { return _Ptr; }
const C& operator*() const { return *_Ptr; }
operator C const *() const { return _Ptr; }
C* operator->() { return _Ptr; }
C& operator*() { return *_Ptr; }
operator C*() { return _Ptr; }
bool operator==(const IPtr& other) const { return _Ptr==other._Ptr; }
bool operator!=(const IPtr& other) const { return _Ptr!=other._Ptr; }
bool operator==(const C* p) const { return _Ptr==p; }
bool operator!=(const C* p) const { return _Ptr!=p; }
private:
C * _Ptr;
};
template <class C>
class IConstPtr
{
public:
IConstPtr() { _Ptr= NULL; }
IConstPtr(const C* p) { operator=(p); }
IConstPtr& operator=(const C* p) { _Ptr=p; return *this; }
IConstPtr& operator=(const IConstPtr& other) { _Ptr=other._Ptr; return *this; }
IConstPtr& operator++() { ++_Ptr; return *this; }
IConstPtr& operator--() { --_Ptr; return *this; }
const C* operator->() const { return _Ptr; }
const C& operator*() const { return *_Ptr; }
operator C const *() const { return _Ptr; }
bool operator==(const IConstPtr& other) const { return _Ptr==other._Ptr; }
bool operator!=(const IConstPtr& other) const { return _Ptr!=other._Ptr; }
bool operator==(const C* p) const { return _Ptr==p; }
bool operator!=(const C* p) const { return _Ptr!=p; }
private:
C const * _Ptr;
};
//-------------------------------------------------------------------------------------------------
// HANDY cleanPath() method for cleaning file system paths
//-------------------------------------------------------------------------------------------------
// Clean a path performing the following operations:
// - convert '\\' characters to '/'
// - replace '//' strings in the middle of the path with '/'
// - remove '.' directory entries
// - colapse '..' directory entries (removing parent entries)
// - append a final '/' (optionally)
//
// examples:
// - a:/bcd/efg/ => a:/bcd/efg/ (no change)
// - a:\bcd\efg => a:/bcd/efg/
// - \bcd\\efg => /bcd/efg/
// - \\bcd\efg => //bcd/efg/
// - \bcd\.\efg => /bcd/efg/
// - \bcd\..\efg => /efg/
// - bcd\..\efg => efg/
// - bcd\..\..\efg => ../efg/
// - \bcd\..\..\efg => /efg/ (NOTE: the redundant '..' entry is lost due to leading '\')
//
NLMISC::CSString cleanPath(const NLMISC::CSString& path,bool addTrailingSlash);
template <class T>
struct TTypeLimits
{
static T max();
static T min();
static T floor(T value);
};
template <>
struct TTypeLimits<uint8>
{
static uint8 max() { return (uint8)0xff; }
static uint8 min() { return 0; }
enum
{
IsSigned = 0,
IsInteger = 1,
};
static uint8 floor(uint8 value) { return value; }
};
template <>
struct TTypeLimits<uint16>
{
static uint16 max() { return (uint16)0xffff; }
static uint16 min() { return 0; }
enum
{
IsSigned = 0,
IsInteger = 1,
};
static uint16 floor(uint16 value) { return value; }
};
template <>
struct TTypeLimits<uint32>
{
static uint32 max() { return 0xffffffffu; }
static uint32 min() { return 0; }
enum
{
IsSigned = 0,
IsInteger = 1,
};
static uint32 floor(uint32 value) { return value; }
};
/*
#ifdef NL_OS_WINDOWS
template <>
struct TTypeLimits<unsigned int> : public TTypeLimits<uint32>
{
};
#endif
*/
template <>
struct TTypeLimits<uint64>
{
static uint64 max() { return UINT64_CONSTANT(0xffffffffffffffff); }
static uint64 min() { return 0; }
enum
{
IsSigned = 0,
IsInteger = 1,
};
static uint64 floor(uint64 value) { return value; }
};
template <>
struct TTypeLimits<sint8>
{
static sint8 max() { return (sint8)0x7f; }
static sint8 min() { return (sint8)0x80; }
enum
{
IsSigned = 1,
IsInteger = 1,
};
static sint8 floor(sint8 value) { return value; }
};
template <>
struct TTypeLimits<sint16>
{
static sint16 max() { return (sint16)0x7fff; }
static sint16 min() { return (sint16)0x8000; }
enum
{
IsSigned = 1,
IsInteger = 1,
};
static sint16 floor(sint16 value) { return value; }
};
template <>
struct TTypeLimits<sint32>
{
static sint32 max() { return (sint32)0x7fffffff; }
static sint32 min() { return (sint32)0x80000000; }
enum
{
IsSigned = 1,
IsInteger = 1,
};
static sint32 floor(sint32 value) { return value; }
};
/*#ifdef NL_OS_WINDOWS
template <>
struct TTypeLimits<int> : public TTypeLimits<sint32>
{
};
#endif*/
template <>
struct TTypeLimits<sint64>
{
static sint64 max() { return SINT64_CONSTANT(0x7fffffffffffffff); }
static sint64 min() { return SINT64_CONSTANT(0x8000000000000000); }
enum
{
IsSigned = 1,
IsInteger = 1,
};
static sint64 floor(sint64 value) { return value; }
};
template <>
struct TTypeLimits<float>
{
static float max() { return FLT_MAX; }
static float min() { return FLT_MIN; }
enum
{
IsSigned = 1,
IsInteger = 0,
};
static float floor(float f) { return f < 0 ? (float)::ceil(f) : (float)::floor(f); }
};
template <>
struct TTypeLimits<double>
{
static double max() { return DBL_MAX; }
static double min() { return DBL_MIN; }
enum
{
IsSigned = 1,
IsInteger = 0,
};
static double floor(double d) { return d < 0 ? ::ceil(d) : ::floor(d); }
};
template <class T, class U>
inline T checkedCast(U val)
{
typedef TTypeLimits<U> TLimitIn;
typedef TTypeLimits<T> TLimitOut;
// Only allow checked cast to integer type !
nlctassert(TLimitOut::IsInteger);
T dest = (T) val;
U check = (U) dest;
if (val < 0)
{
BOMB_IF(check != TLimitIn::floor(val), "checkedCast : Value "<<val<<" exceed the negative capacity of "<<typeid(T).name()<<" clamping at min value", return TLimitOut::min());
}
else
{
BOMB_IF(check != TLimitIn::floor(val), "checkedCast : Value "<<val<<" exceed the positive capacity of "<<typeid(T).name()<<" clamping at max value", return TLimitOut::max());
}
return T(dest);
}
//-------------------------------------------------------------------------------------------------
#endif