-
Ranged numbers
Hey ! I was bored and I had this idea so here it is. It's a simple class containing a Data field and a Min and Max field where Data will be a number between Min and Max. Why is this in game programming ? Well, you can use this to contain the health, mana or whatever you wish. I've overloaded most operators though I can't see to make the pre and post-increment operators work. A problem of syntax, perhaps someone can help.
PS: I know it may be a little bit of overhead but I think it cleans up a little of the mess in your code since you don't have to check boundaries and such.
Code:
#ifndef RANGED_NUM_H_INCLUDED
#define RANGED_NUM_H_INCLUDED
#define READJUST_VALUE(a, min, max) if(a < min) a = min; if(a > max) a = max
template <typename T>
class RangedNum {
public:
explicit RangedNum(T dat = 0, T min = 0, T max = 0) : Data(dat), Min(min), Max(max)
{
}
explicit RangedNum(const RangedNum<T>& cpy)
{
Data = cpy.Data;
Min = cpy.Min;
Max = cpy.Max;
}
void SetMinimum(T min = 0)
{
Min = min;
if(Min > Max) Min = Max;
READJUST_VALUE(Data, Min, Max);
}
void SetMaximum(T max = 0)
{
Max = max;
if(Max < Min) Max = Min;
READJUST_VALUE(Data, Min, Max);
}
void SetRanges(T min, T max)
{
Min = min;
Max = max;
if(Min > Max) Min = Max;
READJUST_VALUE(Data, Min, Max);
}
operator T ()
{
return Data;
}
template <typename OpT>
T& operator + (const OpT& arg)
{
Tmp = Data + Arg;
READJUST_VALUE(Tmp, Min, Max);
return Tmp;
}
template <typename OpT>
T& operator += (const OpT& arg)
{
Data += arg;
READJUST_VALUE(Data, Min, Max);
return Data;
}
template <typename OpT>
T& operator - (const OpT& arg)
{
Tmp = Data - Arg;
READJUST_VALUE(Tmp, Min, Max);
return Tmp;
}
template <typename OpT>
T& operator -= (const OpT& arg)
{
Data -= arg;
READJUST_VALUE(Data, Min, Max);
return Data;
}
template <typename OpT>
T& operator * (const OpT& arg)
{
Tmp = Data * Arg;
READJUST_VALUE(Tmp, Min, Max);
return Tmp;
}
template <typename OpT>
T& operator *= (const OpT& arg)
{
Data *= arg;
READJUST_VALUE(Data, Min, Max);
return Data;
}
template <typename OpT>
T& operator / (const OpT& arg)
{
if(arg = 0) arg = 1;
Tmp = Data / Arg;
READJUST_VALUE(Tmp, Min, Max);
return Tmp;
}
template <typename OpT>
T& operator /= (const OpT& arg)
{
if(arg = 0) arg = 1;
Data /= arg;
READJUST_VALUE(Data, Min, Max);
return Data;
}
T& operator ++(RangedNum<T>& arg)
{
arg.Data++;
return arg.Data;
}
T& operator ++ ()
{
Data++;
return Data;
}
template <typename OpT>
bool operator == (const OpT& arg) const
{
return (Data == arg);
}
template <typename OpT>
bool operator != (const OpT& arg) const
{
return (Data != arg);
}
protected:
T Data, Min, Max, Tmp;
};
#endif // RANGED_NUM_H_INCLUDED
-
You are using nice features like templates and operator overloading, but at the same time you're using macros.
To make the ++ operator work, use an unnamed int argument to distinguish pre- and post-increment. I'm sure you can google somthing.
-
As Sang-drax was saying, the prefix takes no operators, the postfix takes a dummy int operator. It's used for nothing, it's just there to tell the compiler that this is the postfix version.
-
Ok thank you for the info =]
As for using macros, they are perfectly fine if you make a good use of them. Besides, my macro should read
Code:
#define READJUST_VALUE(a, min, max) if((a) < (min)) (a) = (min); if((a) > (max)) (a) = (max)
and not
Code:
#define READJUST_VALUE(a, min, max) if(a < min) a = min; if(a > max) a = max
-
And how is that possibly a good use of a macro??
Nice class though.
-
That's real nice, now what happens if that macro is used in a conditional statement, loop statement, or function? Don't design bad code because you know the limited ways it could be used properly. Write good code and then you don't have to worry about whether or not you can use it where you're using it.
-
Gneh, for you ladies ;-)
Code:
template <typename T>
inline T& ReadjustValues(T* a, T min, T max)
{
if((*a) < min) *a = min;
if((*a) > max) *a = max;
return *a;
}
PS: Just kidding by the way ;-)
-
Would you mind capitalizing the 'a' on adjust in your function name? I see that as "read just values".
-
I tried to add a little system to catch the difference between a and min or a and max in case of an overflow/underflow but I get a "OverflowException<T> cannot be raised" error (and the same for UnderflowException), what does that mean ?
Code:
template <typename T>
inline T& ReAdjustValues(T* a, T min, T max) throw()
{
if((*a) < min)
{
*a = min;
throw (UnderflowException<T>(*a - min));
}
if((*a) > max)
{
*a = max;
throw (OverflowException<T>(*a - max));
}
return *a;
}
template <typename T>
class LimitBreakException
{
protected:
enum ExceptionType
{
LBE_OVERFLOW,
LBE_UNDERFLOW
};
public:
LimitBreakException(const T& diff) : Difference(diff)
{
}
virtual ~LimitBreakException() = 0;
const T& GetDifference() const
{
return Difference;
}
const ExceptionType& GetExceptionType() const
{
return EType;
}
const char* GetErrStr()
{
ErrStr.clear();
ErrStr << ((EType == LBE_OVERFLOW) ? "Overflow" : "Underflow");
ErrStr << " error by " << Difference;
return ErrStr.str().c_str();
}
protected:
ExceptionType EType;
T Difference;
private:
std::stringstream ErrStr;
};
template <typename T>
class UnderflowException : public LimitBreakException<T>
{
public:
UnderflowException(const T& diff) : LimitBreakException<T>(diff), EType(LBE_UNDERFLOW)
{
}
~UnderflowException()
{
}
};
template <typename T>
class OverflowException : public LimitBreakException<T>
{
public:
OverflowException(const T& diff) : LimitBreakException<T>(diff), EType(LBE_OVERFLOW)
{
}
~OverflowException()
{
}
};
PS: Yes, I didn't really notice that, I typed too fast I think =/
PS: I did pre-declare the classes before using them, this is not the problem.