|
Boost : |
From: Maciej Sobczak (prog_at_[hidden])
Date: 2004-04-01 07:21:39
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/
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk