You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ryzom-core/code/nel/include/nel/misc/variable.h

512 lines
12 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_VARIABLE_H
#define NL_VARIABLE_H
#include "types_nl.h"
#include "command.h"
#include "value_smoother.h"
#include "sstring.h"
namespace NLMISC {
/** WARNING:
* This is POSIX C/C++ linker behavior: object files
* that are not referenced from outside are discarded. The
* file in which you run your constructor is thus simply
* thrown away by the linker, which explains why the constructor
* is not run.
*/
/**
* Add a variable that can be modified in realtime. The variable must be global. If you must access the variable with
* function, use NLMISC_DYNVARIABLE
*
* Example:
* \code
// I want to look and change the variable 'foobar' in realtime, so, first i create it:
uint8 foobar;
// and then, I add it
NLMISC_VARIABLE(uint8, FooBar, "this is a dummy variable");
* \endcode
*
* Please use the same casing than for the variable (first letter of each word in upper case)
* ie: MyVariable, NetSpeedLoop, Time
*
* \author Vianney Lecroart
* \author Nevrax France
* \date 2001
*/
#define NLMISC_VARIABLE(__type,__var,__help) NLMISC_CATEGORISED_VARIABLE(variables,__type,__var,__help)
#define NLMISC_CATEGORISED_VARIABLE(__category,__type,__var,__help) \
NLMISC::CVariablePtr<__type> __var##Instance(#__category,#__var, __help " (" #__type ")", &__var)
/**
* Add a variable that can be modified in realtime. The code profide the way to access to the variable in the read
* and write access (depending on the \c get boolean value)
*
* Example:
* \code
// a function to read the variable
uint8 getVar() { return ...; }
// a function to write the variable
void setVar(uint8 val) { ...=val; }
// I want to look and change the variable in realtime:
NLMISC_DYNVARIABLE(uint8, FooBar, "this is a dummy variable")
{
// read or write the variable
if (get)
*pointer = getVar();
else
setVar(*pointer);
}
* \endcode
*
* Please use the same casing than for the variable (first letter of each word in upper case)
* ie: MyVariable, NetSpeedLoop, Time
*
* \author Vianney Lecroart
* \author Nevrax France
* \date 2001
*/
#define NLMISC_DYNVARIABLE(__type,__name,__help) NLMISC_CATEGORISED_DYNVARIABLE(variables,__type,__name,__help)
#define NLMISC_CATEGORISED_DYNVARIABLE(__category,__type,__name,__help) \
class __name##Class : public NLMISC::IVariable \
{ \
public: \
__name##Class () : IVariable(#__category, #__name, __help) { } \
\
virtual bool fromString(const std::string &val, bool human=false) \
{ \
/*std::stringstream ss (val);*/ \
__type p; \
/*ss >> p;*/ \
bool ret = NLMISC::fromString(val, p) ; \
ptr (&p, false, human); \
return ret; \
} \
\
virtual std::string toString(bool human) const \
{ \
__type p; \
ptr (&p, true, human); \
/*std::stringstream ss;*/ \
/*ss << p;*/ \
/*return ss.str();*/ \
return NLMISC::toString(p); \
} \
\
void ptr(__type *pointer, bool get, bool human) const; \
}; \
__name##Class __name##Instance; \
void __name##Class::ptr(__type *pointer, bool get, bool human) const
/** Helper to declare a variable as friend of a class.
* Useful when you want to declare variable that need to access private data to act on or display internal state of the class
*/
#define NLMISC_DYNVARIABLE_FRIEND(__name) NLMISC_CATEGORISED_DYNVARIABLE_FRIEND(variables, __name)
#define NLMISC_CATEGORISED_DYNVARIABLE_FRIEND(__category, __name) friend class __name##Class
//
//
//
//
class IVariable : public ICommand
{
friend class CCommandRegistry;
public:
IVariable(const char *categoryName, const char *commandName, const char *commandHelp, const char *commandArgs = "[<value>]", bool useConfigFile = false, void (*cc)(IVariable &var)=NULL) :
ICommand(categoryName,commandName, commandHelp, commandArgs), _UseConfigFile(useConfigFile), ChangeCallback(cc)
{
Type = Variable;
}
virtual bool fromString(const std::string &val, bool human=false) = 0;
virtual std::string toString(bool human=false) const = 0;
virtual bool execute(const std::string &/* rawCommandString */, const std::vector<std::string> &args, NLMISC::CLog &log, bool quiet, bool human)
{
if (args.size() > 1)
return false;
if (args.size() == 1)
{
// set the value
fromString (args[0], human);
}
// display the value
if (quiet)
{
log.displayNL(toString(human).c_str());
}
else
{
log.displayNL("Variable %s = %s", _CommandName.c_str(), toString(human).c_str());
}
return true;
}
static void init (CConfigFile &configFile);
private:
bool _UseConfigFile;
protected:
// TODO: replace by interface (see IVariableChangedCallback)
void (*ChangeCallback)(IVariable &var);
};
template <class T>
class CVariablePtr : public IVariable
{
public:
CVariablePtr (const char *categoryName, const char *commandName, const char *commandHelp, T *valueptr, bool useConfigFile = false, void (*cc)(IVariable &var)=NULL) :
IVariable (categoryName, commandName, commandHelp, "[<value>]", useConfigFile, cc), _ValuePtr(valueptr)
{
}
virtual bool fromString (const std::string &val, bool /* human */=false)
{
//std::stringstream ss (val);
//ss >> *_ValuePtr;
bool ret = NLMISC::fromString(val, *_ValuePtr);
if (ChangeCallback) ChangeCallback (*this);
return ret;
}
virtual std::string toString (bool /* human */) const
{
//std::stringstream ss;
//ss << *_ValuePtr;
//return ss.str();
return NLMISC::toString(*_ValuePtr);
}
private:
T *_ValuePtr;
};
template <class T>
class CVariable : public IVariable
{
public:
CVariable ( const char *categoryName,
const char *commandName,
const char *commandHelp,
const T &defaultValue,
uint nbMeanValue = 0,
bool useConfigFile = false,
void (*cc)(IVariable &var)=NULL,
bool executeCallbackForDefaultValue=false ) :
IVariable (categoryName, commandName, commandHelp, "[<value>|stat|mean|min|max]", useConfigFile, cc), _Mean(nbMeanValue), _First(true)
{
set (defaultValue, executeCallbackForDefaultValue);
}
virtual bool fromString (const std::string &val, bool /* human */=false)
{
T v;
bool ret = NLMISC::fromString(val, v);
// std::stringstream ss (val);
// ss >> v;
set (v);
return ret;
}
virtual std::string toString (bool /* human */) const
{
return NLMISC::toString(_Value);
// std::stringstream ss;
// ss << _Value;
// return ss.str();
}
CVariable<T> &operator= (const T &val)
{
set (val);
return *this;
}
operator T () const
{
return get ();
}
void set (const T &val, bool executeCallback = true)
{
_Value = val;
_Mean.addValue (_Value);
if (_First)
{
_First = false;
_Min = _Value;
_Max = _Value;
}
else
{
if (_Value > _Max) _Max = _Value;
if (_Value < _Min) _Min = _Value;
}
if (ChangeCallback && executeCallback) ChangeCallback (*this);
}
const T &get () const
{
return _Value;
}
std::string getStat (bool light = false) const
{
CSString str;
str << _CommandName << "=" << _Value << " Min=" << _Min;
if (_Mean.getNumFrame()>0)
{
const std::vector<T>& v = _Mean.getLastFrames();
T theMin = *std::min_element(v.begin(), v.end());
str << " RecentMin=" << theMin;
}
str << " Max=" << _Max;
if (_Mean.getNumFrame()>0)
{
const std::vector<T>& v = _Mean.getLastFrames();
T theMax = *std::max_element(v.begin(), v.end());
str << " RecentMax=" << theMax;
}
if (_Mean.getNumFrame()>0)
{
str << " RecentMean=" << _Mean.getSmoothValue();
if(!light)
{
str << " RecentValues=";
// output the oldest part of the buffer first
for (uint i=_Mean.getCurrentFrame(); i<_Mean.getNumFrame(); ++i)
{
str << _Mean.getLastFrames()[i];
if (i < _Mean.getNumFrame()-1 || _Mean.getCurrentFrame() != 0)
str << ",";
}
// then output the newest part
for (uint i = 0; i < _Mean.getCurrentFrame(); i++)
{
str << _Mean.getLastFrames()[i];
if (i < _Mean.getCurrentFrame()-1)
str << ",";
}
}
}
return str;
}
virtual bool execute (const std::string &/* rawCommandString */, const std::vector<std::string> &args, NLMISC::CLog &log, bool quiet, bool human)
{
if (args.size() > 1)
return false;
bool haveVal=false;
std::string val;
if (args.size() == 1)
{
if (args[0] == "stat")
{
// display the stat value
std::string stat = getStat();
// cut the stat line in lines of 80 chars
std::string::size_type pos = 0;
while (pos < stat.size())
{
log.displayNL(getStat().substr(pos, 80).c_str());
pos += 80;
}
return true;
}
else if (args[0] == "mean")
{
haveVal = true;
val = NLMISC::toString(_Mean.getSmoothValue());
}
else if (args[0] == "min")
{
haveVal = true;
val = NLMISC::toString(_Min);
}
else if (args[0] == "max")
{
haveVal = true;
val = NLMISC::toString(_Max);
}
else
{
// set the value
fromString (args[0], human);
}
}
// display the value
if (!haveVal)
{
val = toString(human);
}
if (quiet)
{
log.displayNL(val.c_str());
}
else
{
log.displayNL("Variable %s = %s", _CommandName.c_str(), val.c_str());
}
return true;
}
private:
T _Value;
CValueSmootherTemplate<T> _Mean;
T _Min, _Max;
bool _First;
};
template<> class CVariable<std::string> : public IVariable
{
public:
CVariable (const char *categoryName, const char *commandName, const char *commandHelp, const std::string &defaultValue, uint /* nbMeanValue */ = 0, bool useConfigFile = false, void (*cc)(IVariable &/* var */)=NULL, bool executeCallbackForDefaultValue=false) :
IVariable (categoryName, commandName, commandHelp, "[<value>]", useConfigFile, cc)
{
set (defaultValue, executeCallbackForDefaultValue);
}
virtual bool fromString (const std::string &val, bool /* human */=false)
{
set (val);
return true;
}
virtual std::string toString (bool /* human */=false) const
{
return _Value;
}
CVariable<std::string> &operator= (const std::string &val)
{
set (val);
return *this;
}
operator std::string () const
{
return get();
}
operator const char * () const
{
return get().c_str();
}
const char *c_str () const
{
return get().c_str();
}
void set (const std::string &val, bool executeCallback = true)
{
_Value = val;
static bool RecurseSet = false;
if (ChangeCallback && !RecurseSet && executeCallback)
{
RecurseSet = true;
ChangeCallback(*this);
RecurseSet = false;
}
}
const std::string &get () const
{
return _Value;
}
virtual bool execute (const std::string &/* rawCommandString */, const std::vector<std::string> &args, NLMISC::CLog &log, bool quiet, bool human)
{
if (args.size () > 1)
return false;
if (args.size () == 1)
{
// set the value
fromString (args[0], human);
}
// convert the string from utf-8 to ascii (thrue unicode)
ucstring temp;
temp.fromUtf8(toString(human));
std::string disp = temp.toString();
// display the value
if (quiet)
{
log.displayNL (disp.c_str());
}
else
{
log.displayNL ("Variable %s = %s", _CommandName.c_str(), disp.c_str());
}
return true;
}
private:
std::string _Value;
};
/// This class can provide a callback called when the value of a variable has been changed
class IVariableChangedCallback
{
public:
virtual ~IVariableChangedCallback() {}
virtual void onVariableChanged(NLMISC::IVariable& var) = 0;
};
} // NLMISC
#endif // NL_VARIABLE_H
/* End of variable.h */