unit_map.hpp

Go to the documentation of this file.
00001 /* $Id: unit_map.hpp 53334 2012-02-29 18:23:00Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2006 - 2009 by Rusty Russell <rusty@rustcorp.com.au>
00004    Copyright (C) 2010 - 2012 by Guillaume Melquiond <guillaume.melquiond@gmail.com>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY.
00013 
00014    See the COPYING file for more details.
00015 */
00016 
00017 /** @file */
00018 
00019 #ifndef UNIT_MAP_H_INCLUDED
00020 #define UNIT_MAP_H_INCLUDED
00021 
00022 #include "utils/reference_counter.hpp"
00023 #include "map_location.hpp"
00024 
00025 #include <cassert>
00026 #include <list>
00027 #include <boost/unordered_map.hpp>
00028 
00029 class unit;
00030 
00031 /**
00032  * Container associating units to locations.
00033  * An indirection location -> underlying_id -> unit ensures that iterators
00034  * stay valid even if WML modifies or moves units on the fly. They even stay
00035  * valid if a unit is erased from the map and another unit with the same
00036  * underlying id is inserted in the map.  In other words it is a doubly indexed ordered map
00037  with persistent iterators (that never invalidate)
00038 
00039  @note The unit_map is implemented as 2 unordered maps storing iterators from a list of reference counted pointers to units.
00040  The unordered maps provide O(1) find times.  The list allows arbitrary ordering of units (not yet implemented).
00041  The reference counting is what guarrantees the persistent iterators.
00042  Storing an iterator prevents only that dead unit's list location from being recovered.
00043 
00044 @note Prefered usages for tight loops follows.
00045 Use the std::pair<iterator, bool> format which checks the preconditions and returns
00046 false in the bool to indicate failure with no change to the unit_map.  true indicates sucess and the new iterator is in first.
00047 Storing the result iterator prevents the old iterator from entering the fallback recovery code.
00048 This is faster than the old methodology of find to check if empty, insert and then find to check for success.
00049 It is the same method as std::map uses, the C++ way.
00050 
00051 unit_iterator i;  //results are stored here
00052 
00053 Add
00054 std::pair<unit_iterator, bool> try_add(units->add(loc, unit));
00055 if(try_add.second){i = try_add.first;}
00056 
00057 Move
00058 std::pair<unit_iterator, bool> try_add(units->move(src, dst));
00059 if(try_add.second){i = try_add.first;}
00060 
00061 Insert
00062 std::pair<unit_iterator, bool> try_add(units->insert(unitp));
00063 if(try_add.second){i = try_add.first;}
00064 
00065 Replace  (preferred over erase and then add)
00066 std::pair<unit_iterator, bool> try_add(units->move(loc, unit));
00067 if(try_add.second){i = try_add.first;}
00068 
00069 
00070 
00071 
00072  @note The previous implementation was 2 binary tree based maps one the location map pointing to the other.
00073  Lookups were O(2*log(N)) and O(log(N)).  Order was implicit in the id map choosen as the base.
00074  Persistence was provided by reference counting all iterators collectively and only recovering space when
00075  there were no iterators outstanding.  Even 1 iterator being stored caused a leak, because
00076  all space for dead units is not recovered.
00077 
00078  * @note Units are owned by the container.
00079  * @note The indirection does not involve map lookups whenever an iterator
00080  *       is dereferenced, it just causes a pointer indirection. The downside
00081  *       is that incrementing iterator is not O(1).
00082  * @note The code does not involve any magic, so units moved while being
00083  *       iterated upon may be skipped or visited twice.
00084  * @note Iterators prevent ghost units from being collected. So they should
00085  *       never be stored into data structures, as it will cause slowdowns!
00086  @note By popular demand iterators are effectively permanant.  They are handles and not iterators.
00087   Any access might cause a full lookup.  Keeping iterators around holds onto memory.
00088  */
00089 class unit_map {
00090     /// The pointer to the unit and a reference counter to record the number of extant iterators
00091     /// pointing to this unit.
00092     struct unit_pod {
00093 
00094         unit_pod()
00095             : unit(NULL)
00096             , ref_count()
00097             , deleted_uid(0)
00098         {
00099         }
00100 
00101         class unit * unit;
00102         mutable n_ref_counter::t_ref_counter<signed int> ref_count;
00103 
00104         unsigned deleted_uid;  ///UID of the deleted, moved, added, or otherwise invalidated iterator to facilitate a new lookup.
00105     };
00106 
00107     /// A list pointing to unit and their reference counters.  Dead units have a unit pointer equal to NULL.
00108     /// The list element is remove iff the reference counter equals zero and there are no more
00109     ///iterators pointing to this unit.
00110     typedef std::list<unit_pod> t_ilist;
00111 
00112     ///Maps of id and location to list iterator.
00113     ///@note list iterators never invalidate due to resizing or deletion.
00114     typedef boost::unordered_map<size_t, t_ilist::iterator> t_umap;
00115     typedef boost::unordered_map<map_location, t_ilist::iterator> t_lmap;
00116 
00117 public:
00118 
00119 // ~~~ Begin iterator code ~~~
00120 
00121     struct standard_iter_types {
00122         typedef unit_map container_type;
00123         typedef unit_map::t_ilist::iterator iterator_type;
00124         typedef unit value_type;
00125     };
00126 
00127     struct const_iter_types {
00128         typedef unit_map const container_type;
00129         typedef unit_map::t_ilist::iterator iterator_type;
00130         typedef const unit value_type;
00131     };
00132 
00133     template<typename iter_types>
00134     struct iterator_base
00135     {
00136         typedef std::forward_iterator_tag iterator_category;
00137         typedef int difference_type;
00138         typedef typename iter_types::value_type value_type;
00139         typedef value_type* pointer;
00140         typedef value_type& reference;
00141         typedef typename iter_types::container_type container_type;
00142         typedef typename iter_types::iterator_type iterator_type;
00143 
00144         ~iterator_base()    { dec(); }
00145 
00146         iterator_base(): i_(), tank_(NULL) { }
00147 
00148         iterator_base(iterator_type i, container_type *m) : i_(i), tank_(m) {
00149             inc();
00150             valid_exit();
00151         }
00152 
00153         iterator_base(const iterator_base &that) : i_(that.i_), tank_(that.tank_) {
00154             inc();
00155             valid_exit();
00156         }
00157 
00158         iterator_base &operator=(const iterator_base &that) {
00159             if(*this != that){
00160                 dec();
00161                 tank_ = that.tank_;
00162                 i_ = that.i_;
00163                 inc();
00164                 valid_exit();
00165             }
00166             return *this;
00167         }
00168 
00169         operator iterator_base<const_iter_types> () const {
00170             return iterator_base<const_iter_types>(i_, tank_);
00171         }
00172 
00173     private:
00174         ///Construct an iterator from the uid map
00175         iterator_base(t_umap::iterator ui, container_type *m) : i_(ui->second), tank_(m) {
00176             inc();
00177             valid_exit();
00178         }
00179 
00180         ///Construct an iterator from the location map
00181         iterator_base(t_lmap::iterator ui, container_type *m) : i_(ui->second), tank_(m) {
00182             inc();
00183             valid_exit();
00184         }
00185 
00186     public:
00187         pointer operator->() const   {
00188             assert(valid());
00189             tank_->self_check();
00190             return  i_->unit; }
00191         reference operator*() const {
00192             tank_->self_check();
00193 #if 0
00194             // debug!
00195             if(!valid()){
00196                 if(!tank_){std::cerr<<"tank is NULL"<<"\n";}
00197                 if(i_==the_end()){std::cerr<<"i_ is the end"<<"\n";}
00198                 if(i_->unit==NULL){std::cerr<<"i_ unit is NULL with uid="<<i_->deleted_uid<<"\n";}
00199             }
00200 #endif
00201             assert(valid());
00202             return *i_->unit; }
00203 
00204         iterator_base& operator++() {
00205             assert( valid_entry() );
00206             tank_->self_check();
00207             iterator_type new_i(i_);
00208             do{
00209                 ++new_i;
00210             }while ((new_i->unit == NULL) && (new_i != the_end() )) ;
00211             dec();
00212             i_ = new_i;
00213             inc();
00214             valid_exit();
00215             return *this;
00216         }
00217 
00218         iterator_base operator++(int) {
00219             iterator_base temp(*this);
00220             operator++();
00221             return temp;
00222         }
00223 
00224         iterator_base& operator--() {
00225             assert(  tank_ && i_ != the_list().begin() );
00226             tank_->self_check();
00227             iterator_type begin(the_list().begin());
00228             dec();
00229             do {
00230                 --i_ ;
00231             }while(i_ != begin && (i_->unit ==  NULL));
00232             inc();
00233 
00234             valid_exit();
00235             return *this;
00236         }
00237 
00238         iterator_base operator--(int) {
00239             iterator_base temp(*this);
00240             operator--();
00241             return temp;
00242         }
00243 
00244         bool valid() const {
00245             if(valid_for_dereference()) {
00246                 if(i_->unit == NULL){
00247                     recover_unit_iterator(); }
00248                 return  i_->unit != NULL;
00249             }
00250             return false; }
00251 
00252         bool operator==(const iterator_base &rhs) const { return (tank_ == rhs.tank_) && ( i_ == rhs.i_ ); }
00253         bool operator!=(const iterator_base &rhs) const { return !operator==(rhs); }
00254 
00255         //      container_type* get_map() const { return tank_; }
00256 
00257         template<typename Y> friend struct iterator_base;
00258 
00259     private:
00260         bool valid_for_dereference() const { return (tank_ != NULL) && (i_ != the_end()); }
00261         bool valid_entry() const { return  ((tank_ != NULL) && (i_ != the_end())) ; }
00262         void valid_exit() const {
00263             if(tank_ != NULL) {
00264                 assert(!the_list().empty());
00265                 assert(i_ != the_list().end());
00266                 if(i_ != the_end()){
00267                     assert(i_->ref_count > 0);
00268                 } else {
00269                     assert(i_->ref_count == 1);
00270                 }
00271             }}
00272         bool valid_ref_count() const { return (tank_ != NULL) && (i_ != the_end()) ; }
00273 
00274         ///Increment the reference counter
00275         void inc() { if(valid_ref_count()) { ++(i_->ref_count); } }
00276 
00277         ///Decrement the reference counter
00278         ///Delete the list element and the dangling umap reference if the unit is gone and the reference counter is zero
00279         ///@note this deletion will advance i_ to the next list element.
00280         void dec() {
00281             if( valid_ref_count() ){
00282                 assert(i_->ref_count != 0);
00283                 if( (--(i_->ref_count) == 0)  && (i_->unit == NULL) ){
00284                     if(tank_->umap_.erase(i_->deleted_uid) != 1){
00285                         tank_->error_recovery_externally_changed_uid(i_); }
00286                     i_ = the_list().erase(i_);
00287                 } } }
00288 
00289         unit_map::t_ilist & the_list() const { return tank_->ilist_; }
00290 
00291         ///Returns the sentinel at the end of the list.
00292         ///This provides a stable unit_iterator for hoisting out of loops
00293         iterator_type the_end() const { return tank_->the_end_; }
00294 
00295         /**
00296      * Attempt to find a deleted unit in the unit_map, by looking up the stored UID.
00297      * @pre deleted_uid != 0
00298      */
00299         void recover_unit_iterator() const {
00300             assert(i_->deleted_uid != 0);
00301             iterator_base new_this( tank_->find( i_->deleted_uid ));
00302             const_cast<iterator_base *>(this)->operator=( new_this );
00303         }
00304         friend class unit_map;
00305 
00306         iterator_type i_; ///local iterator
00307         container_type* tank_; ///the unit_map for i_
00308     };
00309 
00310 
00311 // ~~~ End iterator code ~~~
00312 
00313 
00314     unit_map();
00315     unit_map(const unit_map &that);
00316     unit_map& operator=(const unit_map &that);
00317 
00318     ~unit_map();
00319     void swap(unit_map& o);
00320 
00321     typedef iterator_base<standard_iter_types> unit_iterator;
00322     typedef iterator_base<const_iter_types> const_unit_iterator;
00323 
00324     // Provided as a convenience, since unit_map used to be an std::map.
00325     typedef unit_iterator iterator;
00326     typedef const_unit_iterator const_iterator;
00327 
00328     unit_iterator find(size_t id);
00329     unit_iterator find(const map_location &loc);
00330 
00331     const_unit_iterator find(const map_location &loc) const { return const_cast<unit_map *>(this)->find(loc); }
00332     const_unit_iterator find(size_t id) const { return const_cast<unit_map *>(this)->find(id); }
00333 
00334     unit_iterator find_leader(int side);
00335     const_unit_iterator find_leader(int side) const { return const_cast<unit_map *>(this)->find_leader(side); }
00336     unit_iterator find_first_leader(int side);
00337 
00338     std::vector<unit_iterator> find_leaders(int side);
00339     std::vector<const_unit_iterator> find_leaders(int side) const;
00340 
00341     size_t count(const map_location& loc) const { return static_cast<size_t>(lmap_.count(loc)); }
00342 
00343     unit_iterator begin() { return make_unit_iterator( begin_core() ); }
00344     const_unit_iterator begin() const { return make_const_unit_iterator( begin_core() ); }
00345 
00346     unit_iterator end() { return make_unit_iterator(the_end_); }
00347     const_unit_iterator end() const { return make_const_unit_iterator(the_end_); }
00348 
00349     size_t size() const { return lmap_.size(); }
00350     size_t num_iters() const ;
00351 
00352     void clear(bool force = false);
00353 
00354     /**
00355      * Adds a copy of unit @a u at location @a l of the map.
00356      @return std::pair<unit_iterator, bool>  a bool indicating success and
00357      an iterator pointing to the new unit, or the unit already occupying location.
00358      @note It is 3 times as fast to attempt to insert a unit at @a l and check for
00359      success than it is to verify that the location is empty, insert the unit check the
00360      location for the unit.
00361      */
00362     std::pair<unit_iterator, bool> add(const map_location &l, const unit &u);
00363 
00364     /**
00365      * Adds the unit to the map.
00366      @return std::pair<unit_iterator, bool>  a bool indicating success and
00367      an iterator pointing to the new unit, or the unit already occupying location.
00368      @note It is 3 times as fast to attempt to insert a unit at @a l and check for
00369      success than it is to verify that the location is empty, insert the unit check the
00370      location for the unit.
00371      * @note If the unit::underlying_id is already in use, a new one
00372      *       will be generated.
00373      * @note The map takes ownership of the pointed object, only if it succeeds.
00374      */
00375     std::pair<unit_iterator, bool> insert(unit *p);
00376 
00377     /**
00378      * Moves a unit from location @a src to location @a dst.
00379      @return std::pair<unit_iterator, bool>  a bool indicating success and
00380      an iterator pointing to the new unit, or the unit already occupying location.
00381      @note It is 3 times as fast to attempt to insert a unit at @a l and check for
00382      success than it is to verify that the location is empty, insert the unit check the
00383      location for the unit.
00384      */
00385     std::pair<unit_iterator, bool> move(const map_location &src, const map_location &dst);
00386 
00387     /**
00388      * Works like unit_map::add; but @a l is emptied first, if needed.
00389      @return std::pair<unit_iterator, bool>  a bool indicating success and
00390      an iterator pointing to the new unit, or the unit already occupying location.
00391      */
00392     std::pair<unit_iterator, bool> replace(const map_location &l, const unit &u);
00393 
00394     /**
00395      * Erases the unit at location @a l, if any.
00396      * @returns the number of erased units (0 or 1).
00397      */
00398     size_t erase(const map_location &l);
00399 
00400     /**
00401      * Erases a unit given by a pointer or an iterator.
00402      * @pre The unit is on the map.
00403      */
00404     template <typename T>
00405     size_t erase(const T &iter);
00406 
00407     /**
00408      * Extracts a unit from the map.
00409      * The unit is no longer owned by the map.
00410      * It can be reinserted later, if needed.
00411      */
00412     unit *extract(const map_location &loc);
00413 
00414     ///Checks invariants.  For debugging purposes only.  Doesn't do anything in non-debug mode.
00415     bool self_check() const
00416 #ifndef DEBUG
00417     { return true; }
00418 #endif
00419     ;
00420 
00421     /**
00422      * Is the unit in the map?
00423      *
00424      * @pre                       @p u != @c NULL
00425      *
00426      * @param u                   Pointer to the unit to find.
00427      *
00428      * @returns                   True if found, false otherwise.
00429      */
00430     bool has_unit(const unit * const u);
00431 
00432 private:
00433     ///Creates and inserts a unit_pod called the_end_ as a sentinel in the ilist
00434     void init_end(){
00435         assert(ilist_.empty());
00436         unit_pod upod;
00437         upod.unit = NULL;
00438         upod.deleted_uid = 0;
00439         ++upod.ref_count; //dummy count
00440         ilist_.push_front(upod);
00441         the_end_ = ilist_.begin();
00442     };
00443 
00444     t_ilist::iterator begin_core() const ;
00445 
00446     bool is_valid(const t_ilist::const_iterator &i) const {
00447         return i != the_end_  && is_found(i) && (i->unit !=  NULL); }
00448     bool is_valid(const t_umap::const_iterator &i) const {
00449         return is_found(i) && (i->second->unit != NULL); }
00450     bool is_valid(const t_lmap::const_iterator &i) const {
00451         return is_found(i) && (i->second->unit != NULL); }
00452 
00453     bool is_found(const t_ilist::const_iterator &i) const { return i != ilist_.end(); }
00454     bool is_found(const t_umap::const_iterator &i) const { return i != umap_.end() ; }
00455     bool is_found(const t_lmap::const_iterator &i) const { return i != lmap_.end(); }
00456 
00457     template <typename X>
00458     unit_map::unit_iterator make_unit_iterator(X const & i) {
00459         if (!is_found( i )) { return unit_iterator(the_end_, this); }
00460         return unit_iterator(i , this); }
00461     template <typename X>
00462     unit_map::const_unit_iterator make_const_unit_iterator(X const & i) const {
00463         if (!is_found( i )) { return const_unit_iterator(the_end_, this); }
00464         return const_unit_iterator(i , this); }
00465 
00466     ///Finds and deletes the umap_ item associated with @a lit when the underlying_id()
00467     ///has been changed externally after insertion and before extraction
00468     void error_recovery_externally_changed_uid(t_ilist::iterator const & lit) const;
00469 
00470     /**
00471      * underlying_id -> ilist::iterator. This requires that underlying_id be
00472      * unique (which is enforced in unit_map::insert).
00473      */
00474     mutable t_umap umap_;
00475 
00476     /**
00477      * location -> ilist::iterator.
00478      */
00479     t_lmap lmap_;
00480 
00481     /**
00482      *  List of unit pointers and reference counts for the iterators
00483      */
00484     mutable t_ilist ilist_;
00485 
00486     /// The last list item, a sentinel that allows BOOST::foreach to hoist end()
00487     t_ilist::iterator the_end_;
00488 
00489 };
00490 
00491 template <typename T>
00492 size_t unit_map::erase(const T& iter) {
00493     assert(iter.valid());
00494 
00495     return erase(iter->get_location());
00496 }
00497 
00498 
00499 
00500 #endif  // UNIT_MAP_H_INCLUDED
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:14 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs