Boost logo

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