
HI, Brian Martin wrote:
I have a suggestion for a simple Boost feature - subranges. [...] The big question is, do any of you think this is a useful enough feature?
Yes, it is. What's more, the biggest gain (in my humble opinion, of course) is not the bare fact that you can have range checking at run-time, but that it is possible to provide range checking at *compile-time*, thus strengthening the type system. Consider: void fun(range<0, 100> i) { // do sth with i, being sure that it falls within [0, 100) range } // ... range<0, 200> i(200); fun(i); // this should NOT compile int j; cin >> j; range<0, 100> k(j); // this should compile, but do run-time check For your amusement, please take a look at the code I've written recently (I do not use it, it was just to explore the idea): #include <iostream> #include <stdexcept> template <bool> struct static_assert; template <> struct static_assert<true> {}; template <long L, long U> class range { public: static long check(long v) { if (v < L || v >= U) throw std::range_error("range exceeded"); return v; } range(long v) : v_(check(v)) { static_assert<L < U>(); } template <long L2, long U2> range(range<L2, U2> rhs) : v_(rhs.get()) { static_assert<L2 >= L && U2 <= U>(); } range & operator=(long v) { v_ = check(v); return *this; } template <long L2, long U2> range & operator=(range<L2, U2> rhs) { static_assert<L2 >= L && U2 <= U>(); v_ = rhs.get(); return *this; } range & operator++() { check(++v_); return *this; } range operator++(int) { range t(*this); check(++v_); return t; } range & operator--() { check(--v_); return *this; } range operator--(int) { range t(*this); check(--v_); return t; } long get() const { return v_; } void set(long v) { v_ = check(v); } enum { lBound = L, uBound = U }; private: long v_; }; template <long L1, long U1, long L2, long U2> range<L1, U1> range_cast(range<L2, U2> r) { static_assert<L2 <= U1 && U2 >= L1>(); return range<L1, U1>(r.get()); } template <long L1, long U1, long L2, long U2> range<L1 + L2, U1 + U2> operator+(range<L1, U1> lhs, range<L2, U2> rhs) { return range<L1 + L2, U1 + U2>(lhs.get() + rhs.get()); } template <long L1, long U1, long L2, long U2> range<L1 - U2, U1 - L2> operator-(range<L1, U1> lhs, range<L2, U2> rhs) { return range<L1 - U2, U1 - L2>(lhs.get() - rhs.get()); } template <long L1, long L2> struct static_minmax2 { enum { min_val = L1 < L2 ? L1 : L2, max_val = L1 > L2 ? L1 : L2}; }; template <long L1, long L2, long L3 = L1, long L4 = L1> class static_minmax { private: enum { t1 = static_minmax2<L1, L2>::min_val, t2 = static_minmax2<L3, L4>::min_val, t3 = static_minmax2<L1, L2>::max_val, t4 = static_minmax2<L3, L4>::max_val}; public: enum { min_val = t1 < t2 ? t1 : t2, max_val = t3 > t4 ? t3 : t4}; }; template <long L1, long U1, long L2, long U2> range < static_minmax<L1 * L2, L1 * U2, U1 * L2, U1 * U2>::min_val, static_minmax<L1 * L2, L1 * U2, U1 * L2, U1 * U2>::max_val
operator*(range<L1, U1> lhs, range<L2, U2> rhs) { return range < static_minmax<L1 * L2, L1 * U2, U1 * L2, U1 * U2>::min_val, static_minmax<L1 * L2, L1 * U2, U1 * L2, U1 * U2>::max_val
(lhs.get() * rhs.get()); }
template <long L1, long U1, long L2, long U2> range < static_minmax<L1 / L2, L1 / U2, U1 / L2, U1 / U2>::min_val, static_minmax<L1 / L2, L1 / U2, U1 / L2, U1 / U2>::max_val
operator/(range<L1, U1> lhs, range<L2, U2> rhs) { return range < static_minmax<L1 / L2, L1 / U2, U1 / L2, U1 / U2>::min_val, static_minmax<L1 / L2, L1 / U2, U1 / L2, U1 / U2>::max_val
(lhs.get() / rhs.get()); }
// and later (commented code should not compile): #define assert(c) if (c); else { std::cerr << "assertion failed at " << __LINE__ << '\n'; } int main() { try { range<0, 100> i(4); long l = i.get(); assert(l == 4); i = 56; assert(i.get() == 56); i.set(45); assert(i.get() == 45); range<0, 50> i2(10); i = i2; assert(i.get() == 10); range<0, 200> k1(i + i2); //range<0, 40> k2(i - i2); range<30, 350> i3(35); i = range_cast<0, 100>(i3); i = 98; i++; //++i; range<10, 20> i4(15); range<30, 40> i5(35); //i3 = range_cast<30, 350>(i4 * i5); std::cout << (i5 / i4).lBound << '-' << (i5 / i4).uBound << '\n'; } catch (const std::exception &e) { std::cout << "error: " << e.what() << '\n'; } } -- Maciej Sobczak : http://www.msobczak.com/ Programming : http://www.msobczak.com/prog/