The Artima Developer Community
Sponsored Link

Heron-Centric: Ruminations of a Language Designer
Policy Based Range Checking Class for C++
by Christopher Diggins
June 21, 2005
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.

Talk Back!

Have an opinion? Readers have already posted 1 comment about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Christopher Diggins adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Christopher Diggins is a software developer and freelance writer. Christopher loves programming, but is eternally frustrated by the shortcomings of modern programming languages. As would any reasonable person in his shoes, he decided to quit his day job to write his own ( www.heron-language.com ). Christopher is the co-author of the C++ Cookbook from O'Reilly. Christopher can be reached through his home page at www.cdiggins.com.

This weblog entry is Copyright © 2005 Christopher Diggins. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use