Click here to Skip to main content
12,950,272 members (64,028 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as


11 bookmarked
Posted 5 Aug 2010

Home on the range

, 10 Aug 2010 BSD
Rate this:
Please Sign up or sign in to vote.
Declaring a range of any type

Continuing on the train of thought started in bounds class I presented a few days ago in Bounds, and staying within them.

As so often happens, just having bounds available made me think of what variants of it could be useful. For instance, it would be handy to have it work for floating point or non-POD types, which isn’t possible as it is written. Since the bounds class uses ‘non-type template parameters‘ for its limits, only integer types and enums are accepted.[1]

Even disregarding this restriction, I found that I had use for a dynamic range class, as opposed to the static bounds which has its boundaries set at compile time. Just a simple one, and like std::pair only having two values, but with both of the same type, and with them guaranteed to be ordered.

The last part there would make it a bit more complex than the simple std::pair struct, as I'd need to validate the values given in order to ensure that the minimum was lower than or equal to the maximum, but still, a simple enough little class.

template <typename T,               // data type
  typename L = less_than_comparison::closed<T>, // lower comparer
  typename U = less_than_comparison::closed<T> >// upper comparer
class range
  // Member data
  T minimum_, maximum_;
  // Validation function
  virtual void throw_if_invalid(const T& minimum,
	const T& maximum)
    if (maximum < minimum)
      throw std::invalid_argument("Minimum > maximum");
  // Type name for templated type
  typedef typename T type;

  // Default constructor
    : minimum_(T()),maximum_(T())
  // Assignment constructor
  range(T min, T max)
    : minimum_(min),maximum_(max)
    throw_if_invalid(minimum_, maximum_);

  // Get minimum value
  T get_minimum() const
    return minimum_;
  // Get maximum value
  T get_maximum() const
    return maximum_;
  // Set minimum value
  void set_minimum(T min)
    throw_if_invalid(min, maximum_);
    minimum_ = min;
  // Set maximum value
  void set_maximum(T max)
    throw_if_invalid(minimum_, max);
    maximum_ = max;

  // Equality comparison operator
  bool operator==(const range& other) const
    return (minimum_ == other.minimum_) &&
      (maximum_ == other.maximum_);
  // Inequality comparison operator
  bool operator!=(const range& other) const
    return !operator==(other);

  // Get size of range
  int width() const
    return maximum_ - minimum_;

  // Check if value is in range
  bool in_range(T val) const
    return L::less(minimum_, val) && U::less(val, maximum_);
  // Check if other range is subset of this
  bool in_range(const range& other) const
    return L::less(minimum_, other.minimum_) &&
      U::less(other.maximum_, maximum_);

  // Check if other range intersects with this
  bool intersects(const range& other) const
	  (in_range(other.minimum_) || other.in_range(minimum_)) &&
      (in_range(other.maximum_) || other.in_range(maximum_));
  // Create union of this and other range
  range make_union(const range& other) const
    if (!intersects(other))
      throw std::invalid_argument("No union of ranges");

    return range(std::min(minimum_, other.minimum_),
      std::max(maximum_, other.maximum_));
  // Create intersection of this and other range
  range make_intersection(const range& other) const
    if (!intersects(other))
      throw std::invalid_argument("No intersection of ranges");

    return range(std::max(minimum_, other.minimum_),
      std::min(maximum_, other.maximum_));
// Create intersection of two ranges
template <typename T>
range<T> operator&(const range<T>& lhs,
  const range<T>& rhs)
  return lhs.make_intersection(rhs);

// Create union of two ranges
template <typename T>
range<T> operator|(const range<T>& lhs,
  const range<T>& rhs)
  return lhs.make_union(rhs);

This uses the same policy-based design with upper and lower comparators as the bounds class, so that you can have an open, closed, or half-open (either directions) range.

Note that despite the inclusion of union and intersection functions and operators, this is not a class intended for interval arithmetic. If you have such needs, you're much better off with the boost::interval class.

There is one virtual function in the range class: the validation function. The reason for this is that I can simply combine this with the bounds class into a bounded range, and only need to update the validation to take the bounds into consideration to have a fully functioning class. Well, that, and write suitable constructors, and provide another bounds-checking function.

template <typename T, T lower_, T upper_,
  typename L = less_than_comparison::closed<T>,
  typename U = less_than_comparison::closed<T> >
class bounded_range : public range<T, L, U>,
  public bounds<T, lower_, upper_, L, U>
  /*  Overridden validation function to check bounds as well
      as validity.
    Throws if minimum > maximum, or if out of bounds
    \param minimum lower value of range
    \param maximum upper value of range
  virtual void throw_if_invalid(const T& mini, const T& maxi)
    range<T, L, U>::throw_if_invalid(mini, maxi);
    if (!in_bounds(mini))
      throw std::invalid_argument("Minimum out of bounds");
    if (!in_bounds(maxi))
      throw std::invalid_argument("Maximum out of bounds");
  // Type name for templated type
  typedef typename T type;
  // Default constructor
    : range(lower_bound(), upper_bound())
  // Assignment constructor
  bounded_range(const T&  min, const T&  max)
    : range(min, max)
    throw_if_invalid(min, max);
  // Conversion constructor
  bounded_range(const range<type>& other)
    : range(other)
    throw_if_invalid(other.get_minimum(), other.get_maximum());
  // Check if value is within bounds using base class
  using bounds<T, lower_, upper_>::in_bounds;
  // Check if range is within bounds
  static bool in_bounds(const range<T>& other)
    return in_bounds(other.get_minimum()) &&

[1] The C++ language also permits address types (pointer or reference) as non-type parameters, provided they’re known at compile time, but for that loophole to provide a way to implement static bounds checking with float or, say, std::point types, would, if at all possible, require a mastery of template metaprogramming magic that is far beyond my meagre abilities.

Tagged: bounds, C++, template


This article, along with any associated source code and files, is licensed under The BSD License


About the Author

Orjan Westin
Software Developer (Senior)
United Kingdom United Kingdom
Orjan has worked as a professional developer - in Sweden and England - since 1993, using a wide range of languages (C++, Pascal, Delphi, C, C#, Visual Basic, PHP, Python and x86 assembler), but tends to return to C++.

You may also be interested in...

Comments and Discussions

GeneralVery neat, now something more challenging ;) Pin
Stefan6312-Aug-10 3:24
memberStefan6312-Aug-10 3:24 
GeneralRe: Very neat, now something more challenging ;) Pin
Cool Cow Orjan12-Aug-10 4:23
memberCool Cow Orjan12-Aug-10 4:23 
GeneralRange Pin
geoyar11-Aug-10 13:43
membergeoyar11-Aug-10 13:43 
GeneralRe: Range Pin
Cool Cow Orjan11-Aug-10 22:48
memberCool Cow Orjan11-Aug-10 22:48 
GeneralMissing namespace or superfluous }; Pin
Stone Free9-Aug-10 6:38
memberStone Free9-Aug-10 6:38 
GeneralRe: Missing namespace or superfluous }; Pin
Cool Cow Orjan10-Aug-10 2:12
memberCool Cow Orjan10-Aug-10 2:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170525.1 | Last Updated 10 Aug 2010
Article Copyright 2010 by Orjan Westin
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid