Figure 1: Partial listing of range_set.h defines the range_set class
#ifndef RANGE_SET_H #define RANGE_SET_H // Copyright 1998 (c) Andrew W. Phillips // You can freely use this software for any purpose // as long as you preserve this copyright notice. #include <list> #include <iterator> #include <utility> // for make_pair<T1,T2>() #include <functional> // for less<T> #include <istream> // used in << and >> #include <ios> #include <cassert> // for assert() template <class T, class Pred = std::less<T>, class Alloc = std::allocator<T> > class range_set { public: // Define standard types for STL container typedef Alloc allocator_type; typedef Alloc::size_type size_type; typedef Alloc::difference_type difference_type; typedef Alloc::pointer pointer; typedef Alloc::const_pointer const_pointer; typedef Alloc::reference reference; typedef Alloc::const_reference const_reference; typedef Alloc::value_type value_type; typedef Pred value_compare; typedef value_type key_type; // Value == key for sets typedef value_compare key_compare; // All iterators are const (mods via iterators not allowed) class const_iterator; typedef const_iterator iterator; private: friend class const_iterator; // Class segment: one contiguous block from the set // Note: we could have just used std::pair<T,T> rather // than creating this class, but this may have hindered // future additions. struct segment { T sfirst; // Bottom of segment T slast; // One past the top of the segment // Constructor segment(T fst, T lst) : sfirst(fst), slast(lst) { } }; typedef std::list<segment> range_t; mutable range_t range_; // All segments for this range_set Pred compare_; // Object used for comparing elts Alloc allocator_; // Object used for allocating elts bool increasing_; // compare_ uses increasing order? // not shown: functions insert_helper(), erase_helper() T one_more(const T &v) const { // Since we store "half-open" ranges we need a way to // work out what is "one more" than a value. If we're // using less<T> for comparisons this is done by adding // 1 but if using greater<T> then we must subtract. return increasing_ ? v + T(1) : v - T(1); } T one_less(const T &v) const { // We similarly obtain the value "before" another value. return increasing_ ? v - T(1) : v + T(1); } public: // Iterators class const_iterator : public std::iterator<std::bidirectional_iterator_tag, T, difference_type> { private: // Since the container does not "contain" all the actual // values the iterator itself must contain the current // value pointed to. We must also store an iterator // into the list of segments so we know what the // valid values are. T value_; // The value "pointed" to mutable range_t::iterator pr_; // Segment with the value // We must also access a few things in the container. const range_set *pc_; // Pointer to container const_iterator(const range_t::iterator &p, const range_set *pc, const T &v) : pr_(p), pc_(pc), value_(v) { // Check that we are creating a valid iterator assert(pr_ == pc_->range_.end() || (!pc_->compare_(v, pr_->sfirst) && pc_->compare_(v, pr_->slast))); } public: friend class range_set<T,Pred,Alloc>; const_iterator() : pc_(0) // Default constructor { } // Use compiler generated copy constructor, // copy assignment operator, and destructor const_reference operator*() const { // Check that the iterator is valid and // that we don't dereference end() assert(pc_ != 0); assert(pr_ != pc_->range_.end()); return value_; } const_pointer operator->() const { assert(pc_ != 0); assert(pr_ != pc_->range_.end()); return &value_; } const_iterator &operator++() { // Check that the iterator is valid and // that we don't try to move past the end() assert(pc_ != 0); assert(pr_ != pc_->range_.end()); value_ = pc_->one_more(value_); if (!pc_->compare_(value_,pr_->slast) && ++pr_ != pc_->range_.end()) { value_ = pr_->sfirst; } return *this; } const_iterator operator++(int) { assert(pc_ != 0); // Ensure iter is valid const_iterator tmp(*this); ++*this; return tmp; } const_iterator &operator--() { assert(pc_ != 0); // Ensure iter is valid // Check that we don't move back before start assert(pc_->range_.begin() != pc_->range_.end()); assert(pr_ != pc_->range_.begin() || pc_->compare_(pr_->sfirst, value_)); value_ = pc_->one_less(value_); if (pr_ == pc_->range_.end() || pc_->compare_(value_, pr_->sfirst)) { --pr_; value_ = pc_->one_less(pr_->slast); } return *this; } const_iterator operator--(int) { assert(pc_ != 0); // Ensure iter is valid const_iterator tmp(*this); --*this; return tmp; } bool operator==(const const_iterator& p) const { assert(pc_ != 0); // Ensure iter is valid assert(pc_ == p.pc_); // Same container? if (pr_ != p.pr_) // Different segments so they must be different return false; else if (pr_ == pc_->range_.end()) // Both are at end so they compare the same return true; else // The iterators are now the same if their // value_s are the same. The following tests // for equality using compare_. // [Note that A == B is equivalent to // A >= B && A <= B or !(A < B) && !(B < A)] return !pc_->compare_(value_, p.value_) && !pc_->compare_(p.value_, value_); } bool operator!=(const const_iterator& p) const { return !operator==(p); } }; // Create a reverse iterator based on normal (forward) iter typedef std::reverse_bidirectional_iterator<const_iterator, value_type, const_reference, const_pointer, difference_type> const_reverse_iterator; typedef const_reverse_iterator reverse_iterator; // Constructors explicit range_set(const Pred &p = Pred(), const Alloc &a = Alloc()) : compare_(p), allocator_(a) { increasing_ = compare_(T(0), T(1)); } // not shown, range_set constructor based on member templates // ... range_set(const T *p1, const T *p2, const Pred &p = Pred(), const Alloc &a = Alloc()) : compare_(p), allocator_(a) { increasing_ = compare_(T(0), T(1)); const_iterator last_pos = begin(); while (p1 != p2) last_pos = insert(last_pos, *p1++); } range_set(const_iterator p1, const const_iterator &p2, const Pred &p = Pred(), const Alloc &a = Alloc()) : compare_(p), allocator_(a) { assert(p1.pc_ == p2.pc_); // Same container? assert(p1.pc_ != 0); // ... and valid? increasing_ = compare_(T(0), T(1)); const_iterator last_pos = begin(); while (p1 != p2) last_pos = insert(last_pos, *p1++); } // Use compiler generated copy constructor, // copy assignment operator, and destructor // not shown: functions begin(), end(), rbegin(), rend(), // get_allocator(), max_size(), size(), empty(), and insert() std::pair<const_iterator, const_iterator> insert_range(const value_type &ss, const value_type &ee) { return insert_helper(range_.begin(), ss, ee); } std::pair<const_iterator, const_iterator> insert_range(const const_iterator &p, const value_type &ss, const value_type &ee) { assert(p.pc_ == this); // For this container? return insert_helper(p.pr_, ss, ee); } // not shown: erase() functions const_iterator erase_range(const key_type &ss, const key_type &ee) { return erase_helper(range_.begin(), ss, ee); } const_iterator erase_range(const const_iterator &p, const key_type &ss, const key_type &ee) { assert(p.pc_ == this); // For this container? return erase_helper(p.pr_, ss, ee); } // not shown: functions clear(), swap(), key_comp(), find(), // count(), lower_bound(), upper_bound(), operator>>(), // operator<<(), operator==(), operator!=(), operator<(), // operator>(), operator<=(), operator>=() }; #endif