grouped_element_iterator.hpp

Go to the documentation of this file.
00001 // -*- mode: c++; indent-tabs-mode: nil; -*-
00002 //
00003 //The Biomolecule Toolkit (BTK) is a C++ library for use in the
00004 //modeling, analysis, and design of biological macromolecules.
00005 //Copyright (C) 2005-2006, Tim Robertson <kid50@users.sourceforge.net>
00006 //
00007 //This program is free software; you can redistribute it and/or modify
00008 //it under the terms of the GNU Lesser General Public License as published
00009 //by the Free Software Foundation; either version 2.1 of the License, or (at
00010 //your option) any later version.
00011 //
00012 //This program is distributed in the hope that it will be useful,  but
00013 //WITHOUT ANY WARRANTY; without even the implied warranty of
00014 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 //Lesser General Public License for more details.
00016 //
00017 //You should have received a copy of the GNU Lesser General Public License
00018 //along with this program; if not, write to the Free Software Foundation,
00019 //Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00020 
00023 
00024 #ifndef BTK_UTILITY_GROUPED_ELEMENT_ITERATOR_HPP
00025 #define BTK_UTILITY_GROUPED_ELEMENT_ITERATOR_HPP
00026 
00027 #include <boost/type_traits/is_convertible.hpp>
00028 #include <boost/utility/enable_if.hpp>
00029 #include <boost/iterator/iterator_facade.hpp>
00030 
00031 #include <btk/core/common/debugging.hpp>
00032 #include <btk/core/common/assertions.hpp>
00033 
00034 namespace BTK {
00035 namespace UTILITY {
00036 
00051 template <typename GroupIterator,
00052           typename ElementIterator,
00053           typename GroupType,
00054           typename ElementType>
00055 class GroupedElementIterator : public
00056  boost::iterator_facade<GroupedElementIterator<GroupIterator,
00057                                                ElementIterator,
00058                                                GroupType,
00059                                                ElementType>,
00060                           ElementType, boost::bidirectional_traversal_tag>
00061 {
00062 
00063 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00064   // This template friend declaration is necessary to allow interoperation
00065   // with GroupedElementIterators that have const referent types.
00066   // (yes, this is the approach recommended in the docs for
00067   //  the boost::iterator_facade class!)
00068   template <typename A, typename B, typename C, typename D> 
00069   friend class GroupedElementIterator;
00070 
00071   // This is a reimplementation of std::mem_fun_ref_t, with the addition
00072   // of a default constructor, and debug checks for null-pointer dereferencing.
00073   //
00074   // This is necessary b/c iterators must be default constructible, and
00075   // it is impossible to default construct std::mem_fun_ref_t, making it
00076   // impossible to default construct a GroupedElementIterator when implemented
00077   // using std::mem_fun_ref_t.
00078   template <class Ret, class T>
00079   class dc_mem_fun_ref
00080   {
00081     typedef Ret (T::*func_ptr)();
00082 
00083   public:
00084     dc_mem_fun_ref() : func(static_cast<func_ptr>(0)) {}
00085     explicit dc_mem_fun_ref(func_ptr fp) : func(fp) {}
00086     Ret operator()(T & r) const
00087     {
00088       // If you get this error message, you are most likely trying to
00089       // increment/decrement an uninitialized GroupedElementIterator.
00090       BTK_ASSERT(func != static_cast<func_ptr>(0),
00091                  "Null ptr dereference in GroupedElementIterator::def_cons_mem_fun_ref");
00092       return (r.*func)();
00093     }
00094 
00095   private:
00096     func_ptr func;
00097   };
00098 #endif // DOXYGEN_SHOULD_SKIP_THIS
00099 
00100  public:
00101 
00109   GroupedElementIterator()
00110   {
00111     // set sensible defaults to minimize the chance of NULL-ptr
00112     // dereferences if increment() or decrement() are called on
00113     // a default-constructed iterator.
00114     _cur_group = _group_container_end = _group_container_begin;
00115     _cur_element = _group_begin = _group_end;
00116   }
00117 
00119 
00120   GroupedElementIterator(GroupIterator begin,
00121                          GroupIterator end,
00122                          ElementIterator (GroupType::*group_begin_fun) (),
00123                          ElementIterator (GroupType::*group_end_fun) (),
00124                          bool at_end = false) :
00125     _cur_group(begin), _group_container_begin(begin), _group_container_end(end),
00126     _cur_element(), _group_begin(), _group_end(),
00127     _group_begin_fun(group_begin_fun),
00128     _group_end_fun(group_end_fun)
00129   {
00130     initialize(begin,end,at_end);
00131   }
00132 
00133   GroupedElementIterator(GroupIterator begin,
00134                          GroupIterator end,
00135                          bool at_end = false) :
00136     _cur_group(begin), _group_container_begin(begin), _group_container_end(end),
00137     _cur_element(), _group_begin(), _group_end(),
00138     _group_begin_fun(&GroupType::begin),
00139     _group_end_fun(&GroupType::end)
00140   {
00141     initialize(begin,end,at_end);
00142   }
00144 
00162   template <typename GI, typename EI, typename GT, typename ET>
00163   GroupedElementIterator(GroupedElementIterator<GI,EI,GT,ET> const & other,
00164                          ElementIterator (GroupType::*group_begin_fun) () =
00165                            &GroupType::begin,
00166                          ElementIterator (GroupType::*group_end_fun) () =
00167                            &GroupType::end,
00168                          typename boost::enable_if<
00169                            boost::is_convertible<GI,GroupIterator>
00170                          >::type * d1 = 0,
00171                          typename boost::enable_if<
00172                            boost::is_convertible<EI,ElementIterator>
00173                          >::type * d2 = 0,
00174                          typename boost::enable_if<
00175                            boost::is_convertible<GT,GroupType>
00176                          >::type * d3 = 0,
00177                          typename boost::enable_if<
00178                            boost::is_convertible<ET,ElementType>
00179                          >::type * d4 = 0) :
00180     _cur_group(other._cur_group),
00181     _group_container_begin(other._group_container_begin),
00182     _group_container_end(other._group_container_end),
00183     _cur_element(other._cur_element),
00184     _group_begin(other._group_begin),
00185     _group_end(other._group_end),
00186     _group_begin_fun(group_begin_fun),
00187     _group_end_fun(group_end_fun) {}
00188 
00189   friend std::ostream & operator<<(std::ostream & os,
00190                                    GroupedElementIterator<GroupIterator,
00191                                    ElementIterator,
00192                                    GroupType,
00193                                    ElementType> const & gei)
00194   {
00195     os << "GroupedElementIterator: " << std::endl
00196        << "  _cur_element: " << &*gei._cur_element << std::endl
00197        << "  _group_begin: " << &*gei._group_begin << std::endl
00198        << "  _group_end: " << &*gei._group_end << std::endl
00199        << "  _cur_group: " << &*gei._cur_group << std::endl
00200        << "  _group_container_begin: " << &*gei._group_container_begin << std::endl
00201        << "  _group_container_end: " << &*gei._group_container_end << std::endl;
00202 
00203     return os;
00204   }
00205 
00206  private:
00207   friend class boost::iterator_core_access;
00208 
00209   void initialize(GroupIterator begin,
00210                   GroupIterator end,
00211                   bool at_end)
00212   {
00213     BTK_ASSERT((begin != end),
00214                "Attempt to create GroupedElementIterator from 0-element range!");
00215 
00216     if (at_end) { // construct an "end" iterator.
00217       // set the current element, group begin/end to what they would be
00218       // had we iterated to the end-of-range.
00219       _cur_group = end;
00220       --_cur_group;
00221       _group_begin = _group_begin_fun(*_cur_group);
00222       _group_end = _group_end_fun(*_cur_group);
00223       _cur_element = _group_end;
00224 
00225       // set the current group to the true "end" group.
00226       _cur_group = end;
00227     } else { // construct a normal iterator (not at end)
00228       _cur_element = _group_begin_fun(*_cur_group);
00229       _group_begin = _cur_element;
00230       _group_end = _group_end_fun(*_cur_group);
00231 
00232       // if the current group is empty, advance to the first
00233       // element in a non-empty group.  This has to be done here,
00234       // because otherwise you can get the anomalous result of 1
00235       // successful increment for an empty list.
00236       if (_group_begin == _group_end) increment();
00237     }
00238   }
00239 
00240   void increment()
00241   {
00242     TRACE_OUT << "In GroupedElementIterator::increment():" << std::endl;
00243 
00244     // advance by 1 element
00245     if (_cur_element == _group_end || ++_cur_element == _group_end) {
00246       // advancing puts us at the end of the current group -- change groups
00247 
00248       TRACE_OUT << "  Advanced one element ("
00249                 << "_cur_element=" << &*_cur_element
00250                 << ", _group_end=" << &*_group_end
00251                 << ")" << std::endl;
00252 
00253       if (_cur_group != _group_container_end &&
00254           ++_cur_group != _group_container_end) {
00255         // normal case: go to next group
00256         _group_begin = _group_begin_fun(*_cur_group);
00257         _group_end = _group_end_fun(*_cur_group);
00258         _cur_element = _group_begin;
00259 
00260         TRACE_OUT << "  Advanced one group ("
00261                   << "_cur_group=" <<  &*_cur_group
00262                   << ", _group_begin=" <<  &*_group_begin
00263                   << ", _group_end=" <<  &*_group_end
00264                   << ")" << std::endl;
00265 
00266         // if the new group is empty, advance again.
00267         if (_group_begin == _group_end) {
00268           TRACE_OUT << "  New group is empty! Re-calling increment()..." << std::endl;
00269           increment();
00270         }
00271       } else {
00272         // terminal case: next group (or current group) is the end
00273         _cur_group = _group_container_end;
00274         _cur_element = _group_end;
00275 
00276         TRACE_OUT << "  Can't move forward -- at last group ("
00277                   << "_cur_group=" << &*_cur_group
00278                   << ", _group_end=" << &*_group_end
00279                   << ")" << std::endl;
00280       }
00281     }
00282   }
00283 
00284   void decrement()
00285   {
00286     TRACE_OUT << "In GroupedElementIterator::decrement():" << std::endl;
00287 
00288     if (_cur_element == _group_begin) {
00289       // at the beginning of the current group.
00290       TRACE_OUT << "  At group beginning ("
00291                 << "_cur_element=_group_begin="
00292                 << &*_group_begin 
00293                 << ", _cur_group=" << &*_cur_group 
00294                 << ")" << std::endl;
00295       
00296       if (_cur_group != _group_container_begin) {
00297         // normal case: go to previous group.
00298         --_cur_group;
00299         _group_begin = _group_begin_fun(*_cur_group);
00300         _group_end = _group_end_fun(*_cur_group);
00301         
00302         TRACE_OUT << "  Moved one group back ("
00303                   << "_cur_group=" << &*_cur_group
00304                   << ", _group_begin=" << &*_group_begin
00305                   << ", _group_end=" << &*_group_end
00306                   << ")" << std::endl;
00307         
00308         // if new group is empty, decrement again.
00309         if (_group_begin == _group_end) {
00310           TRACE_OUT << "  New group is empty! Re-calling deccrement()..." << std::endl;
00311           decrement();
00312         } else {
00313           // group not empty -- go to last element in group.
00314           _cur_element = --(_group_end_fun(*_cur_group));
00315         }
00316       } else {
00317         // terminal case: previous group (or current group) is past the beginning
00318         _cur_group = _group_container_begin;
00319         _cur_element = _group_begin;
00320 
00321         TRACE_OUT << "  Can't move back -- at first group ("
00322                   << "_cur_group=" << &*_cur_group
00323                   << ", _group_begin=" << &*_group_begin
00324                   << ")" << std::endl;
00325       }
00326     } else {
00327       // not at beginning of current group -- OK to retreat by one element
00328       --_cur_element;
00329 
00330       // special case: current iterator is "end" iterator, and we're moving
00331       // it backwards.  Need to move the group iterator back by one.
00332       if (_cur_group == _group_container_end) --_cur_group;
00333 
00334       TRACE_OUT << "  Moved back one element ("
00335                 << "_cur_element=" << &*_cur_element
00336                 << ", _group_begin=" << &*_group_begin
00337                 << ", _cur_group=" << &*_cur_group
00338                 << ")" << std::endl;
00339     }
00340   }
00341 
00342   ElementType & dereference() const
00343   {
00344     return *_cur_element;
00345   }
00346 
00347 
00348   template <typename GI, typename EI, typename GT, typename ET>
00349   bool equal(GroupedElementIterator<GI,EI,GT,ET> const & rhs) const
00350   {
00351     TRACE_OUT << "In GroupedElementIterator::equal():" << std::endl
00352               << "  _cur_element: LHS=" << &*_cur_element
00353               << ", RHS=" << &*rhs._cur_element << std::endl
00354               << "  _cur_group: LHS=" << &*_cur_group
00355               << ", RHS=" << &*rhs._cur_group << std::endl;
00356 
00357     // "normal" equality test -- are element and group
00358     // iterators the same? if so, the iterator is equal.
00359     if (_cur_element == rhs._cur_element &&
00360         _cur_group == rhs._cur_group) return true;
00361 
00362     TRACE_OUT << "  NOT equal." << std::endl;
00363 
00364     return false;
00365   }
00366 
00367   GroupIterator _cur_group;
00368   GroupIterator _group_container_begin;
00369   GroupIterator _group_container_end;
00370 
00371   ElementIterator _cur_element;
00372   ElementIterator _group_begin;
00373   ElementIterator _group_end;
00374 
00375   dc_mem_fun_ref<ElementIterator,GroupType> _group_begin_fun;
00376   dc_mem_fun_ref<ElementIterator,GroupType> _group_end_fun;
00377 };
00378 
00379 } // namespace UTILITY
00380 } // namespace BTK
00381 
00382 #endif

Generated on Sun Jul 15 20:46:25 2007 for BTK Core by  doxygen 1.5.1