Summary
I have recently updated my constrained_value class which among other things allows one to easily define range checking integers.
Advertisement
I wanted to share my overhauled constrained_value policy driven class with the community. A constrained_value value type allows you to take an arbitrary value type and define how assignment occurs through a policy parameter. One common usage is to do range checking, but it is much more flexible than this.
The class itself is as follows:
template<class Policy_T>
struct constrained_value
{
public:
// public typedefs
typedef typename Policy_T policy_type;
typedef typename Policy_T::value_type value_type;
typedef constrained_value self;
// default constructor
constrained_value() : m(Policy_T::default_value) { }
constrained_value(const self& x) : m(x.m) { }
explicit constrained_value(const value_type& x) { Policy_T::assign(m, x); }
operator value_type() const { return m; }
void assign(const value_type& x) {
Policy_T::assign(m, x);
}
// assignment operations
self& operator=(const value_type& x) { assign(x); return *this; }
self& operator+=(const value_type& x) { assign(m + x); return *this; }
self& operator-=(const value_type& x) { assign(m - x); return *this; }
self& operator*=(const value_type& x) { assign(m * x); return *this; }
self& operator/=(const value_type& x) { assign(m / x); return *this; }
self& operator%=(const value_type& x) { assign(m % x); return *this; }
self& operator>>=(int x) { assign(m >> x); return *this; }
self& operator<<=(int x) { assign(m << x); return *this; }
// unary operations
self operator-() { return self(-m); }
self operator+() { return self(-m); }
self operator!() { return self(!m); }
self operator~() { return self(~m); }
// binary operations
friend self operator+(const self& x, const value_type& y) { return self(x) += y; }
friend self operator-(const self& x, const value_type& y) { return self(x) -= y; }
friend self operator*(const self& x, const value_type& y) { return self(x) *= y; }
friend self operator/(const self& x, const value_type& y) { return self(x) /= y; }
friend self operator%(const self& x, const value_type& y) { return self(x) %= y; }
friend self operator>>(const self& x, int y) { return self(x) >>= y; }
friend self operator<<(const self& x, int y) { return self(x) <<= y; }
// comparison operators
friend bool operator<(const self& x, const self& y) { return x.m < y.m; }
friend bool operator>(const self& x, const self& y) { return x.m > y.m; }
friend bool operator<=(const self& x, const self& y) { return x.m <= y.m; }
friend bool operator>=(const self& x, const self& y) { return x.m >= y.m; }
friend bool operator==(const self& x, const self& y) { return x.m == y.m; }
friend bool operator!=(const self& x, const self& y) { return x.m != y.m; }
private:
value_type m;
};
If you want to use this to define a range checking integer, you need to supply a policy such as this one:
template<int Min_N, int Max_N>
struct range_throwing_int_policy
{
typedef int value_type;
const static value_type default_value = Min_N;
static void assign(value_type& lvalue, const value_type& rvalue) {
STATIC_ASSERT(Min_N < Max_N);
if ((rvalue < Min_N) || (rvalue > Max_N)) {
throw std::range_error("out of valid range");
}
lvalue = rvalue;
}
};
typedef constrained_value<range_throwing_int_policy<0, 6> > weekday;
typedef constrained_value<range_throwing_int_policy<0, 23> > hour;
typedef constrained_value<range_throwing_int_policy<0, 60> > minute;
typedef constrained_value<range_throwing_int_policy<0, 60> > second;
typedef constrained_value<range_throwing_int_policy<0, 31> > day;
typedef constrained_value<range_throwing_int_policy<0, 11> > month;
More creative policies are also possible, such as assuring that the number is
always even or odd:
struct even_numbers_policy
{
typedef int value_type;
const static value_type default_value = 0;
static void assign(value_type& lvalue, const value_type& rvalue) {
if (rvalue % 2 != 0) {
// rounds towards zero
if (rvalue > 0) lvalue = rvalue - 1;
else lvalue = rvalue + 1;
}
else {
lvalue = rvalue;
}
}
};
struct odd_numbers_policy
{
typedef int value_type;
const static value_type default_value = 1;
static void assign(value_type& lvalue, const value_type& rvalue) {
if (rvalue % 2 == 0) {
// round towards zero policy
if (rvalue > 0) lvalue = rvalue - 1;
else lvalue = rvalue + 1;
}
else {
lvalue = rvalue;
}
}
};
typedef constrained_value<even_numbers_policy> even_number;
typedef constrained_value<odd_numbers_policy> odd_number;
I welcome your comments and improvements.