/**************************************************************************** Template class that emulates Pascal subrange types. The latter allow you to declare the following: type day_of_week = 1..7; var day : day_of_week; day = 2; // OK day = day*8; // ERROR outside numerical range 1..7 any assignment to day that is not in the range 1..7 generates a runtime error. This is most useful during development to improve the reliability of the software. During release, most Pascal compilers allow you to switch off range checking for speed. Class subrange emulates this behaviour. long is used as the "base type" - other integral types use C++'s implicit conversion. min is the lower bound of the range and max is the upper. The class is written so that all operations on the type are checked to make sure the result will be in the range min..max, otherwise a range_error C++ exception is thrown. The above example can now be recoded in C++ as: typedef subrange<1, 7> day_of_week; day_of_week day; day = 2; // OK day *= 8; // ERROR result = 16, outside of 1..7 To enable range checking, define the pre-processor define SUBRANGE_CHECK otherwise the instantiated class will not perform range checks. ****************************************************************************/ //--------------------------------------------------------------------------- #ifndef SUBRANGE_TYPE_H #define SUBRANGE_TYPE_H //--------------------------------------------------------------------------- #include #include using namespace std; //--------------------------------------------------------------------------- template class subrange { private: protected: T value; unsigned long complement_mask; #ifdef SUBRANGE_CHECK inline void range_check(const T a) const { if (!((min <= a) && (a <= max))) { throw range_error("Subrange is out of range"); } } #endif public: // constructors --------------------------------------------------------- subrange() : value(min) {} subrange(const T a) { #ifdef SUBRANGE_CHECK range_check(a); #endif value = a; } subrange(const subrange& s) { this->value = s; } // operators ------------------------------------------------------------ subrange operator++() { #ifdef SUBRANGE_CHECK T x = value; range_check(++x); value = x; #else ++value; #endif return *this; } subrange operator++(int) { subrange temp = *this; #ifdef SUBRANGE_CHECK T x = value; range_check(++x); value = x; #else ++value; #endif return temp; } subrange operator--() { #ifdef SUBRANGE_CHECK T x = value; range_check(--x); value = x; #else --value; #endif return *this; } subrange operator--(int) { subrange temp = *this; #ifdef SUBRANGE_CHECK T x = value; range_check(--x); value = x; #else --value; #endif return temp; } template subrange operator+(const subrange& b) { return this->value + b.value; } template subrange operator-(const subrange& b) { return this->value - b.value; } template subrange operator*(const subrange& b) { return this->value * b.value; } template subrange operator/(const subrange& b) { return this->value / b.value; } template subrange operator%(const subrange& b) { return this->value % b.value; } template subrange operator&(const subrange& b) { return this->value & b.value; } template subrange operator^(const subrange& b) { return this->value ^ b.value; } template subrange operator|(const subrange& b) { return this->value | b.value; } subrange& operator=(const long x) { #ifdef SUBRANGE_CHECK range_check(x); #endif value = x; return *this; } template subrange& operator=(const subrange& b) { this->value = b.value; return *this; } template subrange& operator+=(const subrange& b) { long x = this->value + b.value; #ifdef SUBRANGE_CHECK range_check(x); #endif this->value = x; return *this; } template subrange& operator-=(const subrange& b) { long x = this->value - b.value; #ifdef SUBRANGE_CHECK range_check(x); #endif this->value = x; return *this; } template subrange& operator*=(const subrange& b) { long x = this->value * b.value; #ifdef SUBRANGE_CHECK range_check(x); #endif this->value = x; return *this; } template subrange& operator/=(const subrange& b) { long x = this->value / b.value; #ifdef SUBRANGE_CHECK range_check(x); #endif this->value = x; return *this; } template subrange& operator%=(const subrange& b) { long x = this->value % b.value; #ifdef SUBRANGE_CHECK range_check(x); #endif this->value = x; return *this; } template subrange& operator^=(const subrange& b) { long x = this->value ^ b.value; #ifdef SUBRANGE_CHECK range_check(x); #endif this->value = x; return *this; } template subrange& operator&=(const subrange& b) { long x = this->value & b.value; #ifdef SUBRANGE_CHECK range_check(x); #endif this->value = x; return *this; } template subrange& operator|=(const subrange& b) { long x = this->value | b.value; #ifdef SUBRANGE_CHECK range_check(x); #endif this->value = x; return *this; } subrange& operator<<=(const int x) { T v = *this; v <<= x; #ifdef SUBRANGE_CHECK range_check(v); #endif this->value = v; return *this; } subrange& operator>>=(const int x) { T v = *this; v >>= x; #ifdef SUBRANGE_CHECK range_check(v); #endif this->value = v; return *this; } // obtain contained value operator T() { return value; } // i/o stream operations template < typename charT, typename traits, typename T, long min, long max > friend basic_istream& operator>>( basic_istream& strm, subrange& s) { strm >> s.value; return strm; } template < typename charT, typename traits, typename T, long min, long max > friend basic_ostream& operator<<( basic_ostream& strm, const subrange& s) { strm << s.value; return strm; } }; // comparison ops - need to be non members for STL assoc containers for some // reason template bool operator==( const subrange& a, const subrange& b) { return static_cast(a) == static_cast(b); } template bool operator<( const subrange& a, const subrange& b) { return static_cast(a) < static_cast(b); } template bool operator>( const subrange& a, const subrange& b) { return static_cast(a) > static_cast(b); } template bool operator<=( const subrange& a, const subrange& b) { return static_cast(a) <= static_cast(b); } template bool operator>=( const subrange& a, const subrange& b) { return static_cast(a) >= static_cast(b); } template bool operator!=( const subrange& a, const subrange& b) { return static_cast(a) != static_cast(b); } // fixes a bug in Borland pre-compiled header mechanism - "header incomplete" // it seems Borland doesn't generate pre-compiled headers for files which // only contain templates - putting a dummy function declaration at the end // corrects the problem... #ifdef __BORLANDC__ namespace { void dummy(); } #endif #endif