diff --git a/src/game_events/handlers.cpp b/src/game_events/handlers.cpp index ff62c621c17bb..bc95824f19a08 100644 --- a/src/game_events/handlers.cpp +++ b/src/game_events/handlers.cpp @@ -45,90 +45,40 @@ static lg::log_domain log_event_handler("event_handler"); // This file is in the game_events namespace. namespace game_events { -/* ** handler_list::iterator ** */ - -/** - * Dereference. - * If the current element has become invalid, we will increment first. - */ -handler_ptr handler_list::iterator::operator*() -{ - // Check for an available handler. - while(iter_.derefable()) { - // Handler still accessible? - if(handler_ptr lock = iter_->lock()) { - return lock; - } else { - // Remove the now-defunct entry. - iter_ = list_t::erase(iter_); - } - } - - // End of the list. - return handler_ptr(); -} - /* ** event_handler ** */ -event_handler::event_handler(const config& cfg, bool imi, handler_vec::size_type index, manager& man) +event_handler::event_handler(const config& cfg, bool imi) : first_time_only_(cfg["first_time_only"].to_bool(true)) , is_menu_item_(imi) - , index_(index) - , man_(&man) + , disabled_(false) , cfg_(cfg) { } -/** - * Disables *this, removing it from the game. - * (Technically, the handler is only removed once no one is hanging on to a - * handler_ptr to *this. So be careful how long they persist.) - * - * WARNING: *this may be destroyed at the end of this call, unless - * the caller maintains a handler_ptr to this. - */ void event_handler::disable() { - assert(man_); - assert(man_->event_handlers_); - - // Handlers must have an index after they're created. - assert(index_ < man_->event_handlers_->size()); - - // Disable this handler. - (*man_->event_handlers_)[index_].reset(); + assert(!disabled_);; + disabled_ = true; } -/** - * Handles the queued event, according to our WML instructions. - * WARNING: *this may be destroyed at the end of this call, unless - * the caller maintains a handler_ptr to this. - * - * @param[in] event_info Information about the event that needs handling. - * @param[in,out] handler_p The caller's smart pointer to *this. It may be - * reset() during processing. - */ -void event_handler::handle_event(const queued_event& event_info, handler_ptr& handler_p, game_lua_kernel& lk) + +void event_handler::handle_event(const queued_event& event_info, game_lua_kernel& lk) { - // We will need our config after possibly self-destructing. Make a copy now. - // TODO: instead of copying possibly huge config objects we should use shared things and only increase a refcount - // here. - vconfig vcfg(cfg_, true); + // If this even is disabled, do nothing. + if(disabled_) { + return; + } if(is_menu_item_) { DBG_NG << cfg_["name"] << " will now invoke the following command(s):\n" << cfg_; } + // Disable this handler if it's a one-time event. if(first_time_only_) { - // Disable this handler. disable(); - - // Also remove our caller's hold on us. - handler_p.reset(); } - // *WARNING*: At this point, dereferencing this could be a memory violation! - lk.run_wml_action("command", vcfg, event_info); + lk.run_wml_action("command", vconfig(cfg_, false), event_info); sound::commit_music_changes(); } diff --git a/src/game_events/handlers.hpp b/src/game_events/handlers.hpp index 4af20f4b60486..acdba02e32190 100644 --- a/src/game_events/handlers.hpp +++ b/src/game_events/handlers.hpp @@ -24,8 +24,8 @@ #pragma once #include "config.hpp" -#include "utils/smart_list.hpp" +#include #include #include #include @@ -39,21 +39,18 @@ struct queued_event; class event_handler; // Defined a few lines down. class manager; -/// Shared pointer to handler objects. -typedef std::shared_ptr handler_ptr; - -/// Storage of event handlers. -typedef std::vector handler_vec; +using handler_ptr = std::shared_ptr; +using weak_handler_ptr = std::weak_ptr; +using handler_list = std::list; class event_handler { public: - event_handler(const config& cfg, bool is_menu_item, handler_vec::size_type index, manager&); + event_handler(const config& cfg, bool is_menu_item); - /** The index of *this should only be of interest when controlling iterations. */ - handler_vec::size_type index() const + bool disabled() const { - return index_; + return disabled_; } bool matches_name(const std::string& name, const game_data* data) const; @@ -63,9 +60,15 @@ class event_handler return is_menu_item_; } - /** Disables *this, removing it from the game. */ + /** Flag this handler as disabled. */ void disable(); - void handle_event(const queued_event& event_info, handler_ptr& handler_p, game_lua_kernel&); + + /** + * Handles the queued event, according to our WML instructions. + * + * @param[in] event_info Information about the event that needs handling. + */ + void handle_event(const queued_event& event_info, game_lua_kernel&); const config& get_config() const { @@ -75,109 +78,8 @@ class event_handler private: bool first_time_only_; bool is_menu_item_; - handler_vec::size_type index_; - manager* man_; + bool disabled_; config cfg_; }; -/** - * This is a wrapper for a list of weak pointers to handlers. It allows forward - * iterations of the list, with each element returned as a shared pointer. - * (Weak pointers that fail to lock are silently removed from the list.) These - * iterations can be used recursively, even when the innermost iteration might - * erase arbitrary elements from the list. - * - * The interface is not the standard list interface because that would be - * inconvenient. The functionality implemented is that required by Wesnoth. - */ -class handler_list -{ - /// The weak pointers that are used internally. - typedef std::weak_ptr internal_ptr; - - /// The underlying list. - typedef utils::smart_list list_t; - -public: - /** - * Handler list iterators are rather limited. They can be constructed - * from a reference iterator (not default constructed), incremented, - * and dereferenced. Consecutive dereferences are not guaranteed to - * return the same element (if the list mutates between them, the next - * element might be returned). An increment guarantees that the next - * dereference will differ from the previous (unless at the end of the - * list). The end of the list is indicated by dereferencing to a null - * pointer. - */ - class iterator - { - /// The current element. - list_t::iterator iter_; - - public: - /// Initialized constructor (to be called by handler_list). - explicit iterator(const list_t::iterator& base_iter) - : iter_(base_iter) - { - } - - /// Increment. - iterator& operator++() - { - ++iter_; - return *this; - } - /// Dereference. - handler_ptr operator*(); - }; - friend class iterator; - typedef iterator const_iterator; - -public: - /** - * Default constructor. - * Note: This explicit definition is required (by the more pedantic - * compilers) in order to declare a default-constructed, static, - * and const variable in event_handlers::get(), in handlers.cpp. - */ - handler_list() - : data_() - { - } - - const_iterator begin() const - { - return iterator(const_cast(data_).begin()); - } - - // The above const_cast is so the iterator can remove obsolete entries. - const_iterator end() const - { - return iterator(const_cast(data_).end()); - } - - // push_front() is probably unneeded, but I'll leave the code here, just in case. - // (These lists must be maintained in index order, which means pushing to the back.) - void push_front(const handler_ptr& p) - { - data_.push_front(internal_ptr(p)); - } - - void push_back(const handler_ptr& p) - { - data_.push_back(internal_ptr(p)); - } - - void clear() - { - data_.clear(); - } - -private: - /// No implementation of operator=() since smart_list does not support it. - handler_list& operator=(const handler_list&); - - /// The actual list. - list_t data_; -}; } diff --git a/src/game_events/manager.cpp b/src/game_events/manager.cpp index 0f76ce5168905..fed8df722d578 100644 --- a/src/game_events/manager.cpp +++ b/src/game_events/manager.cpp @@ -44,7 +44,7 @@ namespace game_events /** Create an event handler. */ void manager::add_event_handler(const config& handler, bool is_menu_item) { - event_handlers_->add_event_handler(handler, *this, is_menu_item); + event_handlers_->add_event_handler(handler, is_menu_item); } /** Removes an event handler. */ @@ -101,7 +101,6 @@ manager::iteration::iteration(const std::string& event_name, manager& man) : main_list_(man.event_handlers_->get(event_name)) , var_list_(man.event_handlers_->get_dynamic()) , event_name_(event_name) - , end_(man.event_handlers_->size()) , current_is_known_(false) , main_is_current_(false) , main_it_(main_list_.begin()) @@ -113,7 +112,7 @@ manager::iteration::iteration(const std::string& event_name, manager& man) /** * Increment * Incrementing guarantees that the next dereference will differ from the - * previous derference (unless the iteration is exhausted). However, multiple + * previous dereference (unless the iteration is exhausted). However, multiple * increments between dereferences are allowed to have the same effect as a * single increment. */ @@ -139,6 +138,20 @@ manager::iteration& manager::iteration::operator++() return *this; } +// Small helper function to ensure we don't try to dereference an invalid iterator. +static handler_ptr lock_ptr(const handler_list& list, handler_list::iterator iter) +{ + if(iter != list.end()) { + if(handler_ptr ptr = iter->lock()) { + return ptr; + } else { + assert(false && "Found null handler in handler list!"); + } + } + + return nullptr; +} + /** * Dereference * Will return a null pointer when the end of the iteration is reached. @@ -146,22 +159,23 @@ manager::iteration& manager::iteration::operator++() handler_ptr manager::iteration::operator*() { // Get the candidate for the current element from the main list. - handler_ptr main_ptr = *main_it_; - handler_vec::size_type main_index = ptr_index(main_ptr); + handler_ptr main_ptr = lock_ptr(main_list_, main_it_); // Get the candidate for the current element from the var list. - handler_ptr var_ptr = *var_it_; + handler_ptr var_ptr = lock_ptr(var_list_, var_it_); - // (Loop while var_ptr would be chosen over main_ptr, but the name does not match.) - while(var_ptr && var_ptr->index() < main_index && !var_ptr->matches_name(event_name_, gamedata_)) { - var_ptr = *++var_it_; + // If we have a variable-name event but its name doesn't match event_name_, + // keep iterating until we find a match. If we reach var_list_ end var_ptr + // will be nullptr. + while(var_ptr && !var_ptr->matches_name(event_name_, gamedata_)) { + var_ptr = lock_ptr(var_list_, ++var_it_); } - handler_vec::size_type var_index = ptr_index(var_ptr); + // Are either of the handler ptrs valid? + current_is_known_ = main_ptr != nullptr || var_ptr != nullptr; - // Which list? (Index ties go to the main list.) - current_is_known_ = main_index < end_ || var_index < end_; - main_is_current_ = main_index <= var_index; + // If var_ptr is invalid, we use the ptr from the main list. + main_is_current_ = var_ptr == nullptr; if(!current_is_known_) { return nullptr; // End of list; return a null pointer. @@ -212,6 +226,9 @@ void manager::execute_on_events(const std::string& event_id, manager::event_func func(*this, hand); ++iter; } + + // Clean up expired ptrs. This saves us effort later since it ensures every ptr is valid. + event_handlers_->clean_up_expired_handlers(event_id); } game_events::wml_event_pump& manager::pump() diff --git a/src/game_events/manager.hpp b/src/game_events/manager.hpp index 60b4df60894df..ea97288606cc4 100644 --- a/src/game_events/manager.hpp +++ b/src/game_events/manager.hpp @@ -69,23 +69,13 @@ class manager // Dereference: handler_ptr operator*(); - private: - /// Gets the index from a pointer, capped at end_. - handler_vec::size_type ptr_index(const handler_ptr& ptr) const - { - return !bool(ptr) ? end_ : std::min(ptr->index(), end_); - } - private: /// The fixed-name event handlers for this iteration. - const handler_list& main_list_; + handler_list& main_list_; /// The varying-name event handlers for this iteration. - const handler_list& var_list_; + handler_list& var_list_; /// The event name for this iteration. const std::string event_name_; - /// The end of this iteration. We intentionally exclude handlers - /// added after *this is constructed. - const handler_vec::size_type end_; /// Set to true upon dereferencing. bool current_is_known_; diff --git a/src/game_events/manager_impl.cpp b/src/game_events/manager_impl.cpp index 20b1e3013c301..8e86ba6cd9e24 100644 --- a/src/game_events/manager_impl.cpp +++ b/src/game_events/manager_impl.cpp @@ -80,10 +80,10 @@ std::string event_handlers::standardize_name(const std::string& name) /** * Read-only access to the handlers with fixed event names, by event name. */ -const handler_list& event_handlers::get(const std::string& name) const +handler_list& event_handlers::get(const std::string& name) { // Empty list for the "not found" case. - static const handler_list empty_list; + static handler_list empty_list; // Look for the name in the name map. auto find_it = by_name_.find(standardize_name(name)); @@ -95,7 +95,7 @@ const handler_list& event_handlers::get(const std::string& name) const * An event with a nonempty ID will not be added if an event with that * ID already exists. */ -void event_handlers::add_event_handler(const config& cfg, manager& man, bool is_menu_item) +void event_handlers::add_event_handler(const config& cfg, bool is_menu_item) { const std::string name = cfg["name"]; std::string id = cfg["id"]; @@ -111,7 +111,7 @@ void event_handlers::add_event_handler(const config& cfg, manager& man, bool is_ // Create a new handler. DBG_EH << "inserting event handler for name=" << name << " with id=" << id << "\n"; - handler_ptr new_handler(new event_handler(cfg, is_menu_item, active_.size(), man)); + handler_ptr new_handler(new event_handler(cfg, is_menu_item)); active_.push_back(new_handler); @@ -162,6 +162,26 @@ void event_handlers::remove_event_handler(const std::string& id) log_handlers(); } +void event_handlers::clean_up_expired_handlers(const std::string& event_name) +{ + // First, remove all disabled handlers from the main list. + auto to_remove = std::remove_if(active_.begin(), active_.end(), + [](handler_ptr p) { return p->disabled(); } + ); + + active_.erase(to_remove, active_.end()); + + // Then remove any now-unlockable weak_ptrs from tbe by-name list. + get(event_name).remove_if( + [](weak_handler_ptr ptr) { return ptr.expired(); } + ); + + // And finally remove any now-unlockable weak_ptrs from the with-variables name list. + dynamic_.remove_if( + [](weak_handler_ptr ptr) { return ptr.expired(); } + ); +} + const handler_ptr event_handlers::get_event_handler_by_id(const std::string& id) { auto find_it = id_map_.find(id); diff --git a/src/game_events/manager_impl.hpp b/src/game_events/manager_impl.hpp index c8557993ed878..523de0d938b35 100644 --- a/src/game_events/manager_impl.hpp +++ b/src/game_events/manager_impl.hpp @@ -25,15 +25,16 @@ namespace game_events class event_handlers { private: - typedef std::unordered_map map_t; - typedef std::unordered_map> id_map_t; + using handler_vec_t = std::vector; + using map_t = std::unordered_map; + using id_map_t = std::unordered_map; /** * Active event handlers. Will not have elements removed unless the event_handlers is clear()ed. * This is the only container that actually 'owns' any events in the form of shared_ptrs. The other * three storage methods own weak_ptrs. */ - handler_vec active_; + handler_vec_t active_; /** Active event handlers with fixed event names, organized by event name. */ map_t by_name_; @@ -50,9 +51,6 @@ class event_handlers static std::string standardize_name(const std::string& name); public: - // TODO: remove - typedef handler_vec::size_type size_type; - event_handlers() : active_() , by_name_() @@ -61,41 +59,43 @@ class event_handlers { } - /** Read-only access to the handlers with varying event names. */ - const handler_list& get_dynamic() const + /** Access to the handlers with varying event names. */ + handler_list& get_dynamic() { return dynamic_; } /** Read-only access to the active event handlers. Essentially gives all events. */ - const handler_vec& get_active() const + const handler_vec_t& get_active() const { return active_; } - /** Read-only access to the handlers with fixed event names, by event name. */ - const handler_list& get(const std::string& name) const; + /** Access to the handlers with fixed event names, by event name. */ + handler_list& get(const std::string& name); /** Adds an event handler. */ - void add_event_handler(const config& cfg, manager& man, bool is_menu_item = false); + void add_event_handler(const config& cfg, bool is_menu_item = false); /** Removes an event handler, identified by its ID. */ void remove_event_handler(const std::string& id); + /** + * Removes all expired event handlers and any weak_ptrs to them. + * + * @param event_name The event name from whose by-name queue to clean + * up handlers. + */ + void clean_up_expired_handlers(const std::string& event_name); + /** Gets an event handler, identified by its ID. */ const handler_ptr get_event_handler_by_id(const std::string& id); /** The number of active event handlers. */ - size_type size() const + size_t size() const { return active_.size(); } - - /** Access to active event handlers by index. */ - handler_ptr& operator[](size_type index) - { - return active_[index]; - } }; } // end namespace game_events diff --git a/src/game_events/pump.cpp b/src/game_events/pump.cpp index 0fad7b1244437..bf2e4cc6a740f 100644 --- a/src/game_events/pump.cpp +++ b/src/game_events/pump.cpp @@ -303,7 +303,7 @@ void wml_event_pump::process_event(handler_ptr& handler_p, const queued_event& e ++impl_->internal_wml_tracking; context::scoped evc(impl_->contexts_); assert(resources::lua_kernel != nullptr); - handler_p->handle_event(ev, handler_p, *resources::lua_kernel); + handler_p->handle_event(ev, *resources::lua_kernel); // NOTE: handler_p may be null at this point! if(ev.name == "select") { @@ -596,8 +596,6 @@ pump_result_t wml_event_pump::operator()() // Let this handler process our event. process_event(ptr, ev); - - // NOTE: ptr may be null at this point! }); } else { // Get the handler directly via ID diff --git a/src/utils/smart_list.hpp b/src/utils/smart_list.hpp deleted file mode 100644 index 457546bb4acc8..0000000000000 --- a/src/utils/smart_list.hpp +++ /dev/null @@ -1,829 +0,0 @@ -/* - Copyright (C) 2014 - 2017 by David White - Part of the Battle for Wesnoth Project http://www.wesnoth.org/ - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY. - - See the COPYING file for more details. -*/ - -/** - * @file A list whose iterators never invalidate. - */ - -#pragma once - -#include -#include -#include - -namespace utils -{ - - -/* ** smart_list ** */ - -/// This is a variant on a list that never invalidates iterators (unless, of -/// course, the list ceases to exist). In particular, an erase() will only flag -/// an element for removal from the list. Flagged elements are ignored by list -/// functions, but they still exist from the perspective of iterators that had -/// pointed (and still point) to the element. -/// -/// Assignment is incompatible with the goal of preserving iterators, so it is -/// not implemented (declared private though, to emphasize that this is -/// intentional). -/// -/// The insert() and erase() members are static, as they do not require a -/// reference to the list. -/// -/// Instead of being undefined, ++end() is equal to begin(), and --begin() is -/// equal to end(). -template -class smart_list -{ - /// Nodes in the smart list. - struct node_t - { - /// Default constructor. This is for creating the root of a list. - node_t() : dat_ptr(nullptr), ref_count(1), next(this), prev(this) - {} - /// Initialized constructor. This is for creating a node in a list. - explicit node_t(const Data & d) : dat_ptr(new Data(d)), ref_count(1), next(nullptr), prev(nullptr) - {} - /// Destructor. - ~node_t(); - - /// The data of the node. This is nullptr for a list's root. - Data * const dat_ptr; - /// ref_count counts 1 for the list and 2 for each iterator pointing to - /// this node. (So nodes are flagged for deletion when ref_count is even.) - mutable size_t ref_count; - // List linking. - node_t * next; - node_t * prev; - - private: // Disallowed (and unneeded) functions (declared but not implemented). - /// No assignments. - node_t & operator=(const node_t &); - /// No copying. - node_t(const node_t & that); - }; - - /// The base for the list's iterator classes. - /// Value is the value type of this iterator. - /// Reversed is true for the reverse iterators. - /// The actual iterators must have neither data nor destructors. - template - class iterator_base - { - friend class smart_list; - typedef typename smart_list::node_t node_t; - - public: - // Types required of an iterator: - typedef Value value_type; - typedef value_type * pointer; - typedef value_type & reference; - typedef std::bidirectional_iterator_tag iterator_category; - typedef ptrdiff_t difference_type; - - protected: // Construct this via derived classes. - /// Default constructor - iterator_base() : ptr_(nullptr) {} - /// Initialized constructor - explicit iterator_base(node_t * ptr) : ptr_(ptr) - { skip_flagged(); refer(); } - /// Conversion constructors. - template - iterator_base(const iterator_base & that) : ptr_(that.ptr_) - { refer(); } - /// Copy constructor (the default overrides the conversion template). - iterator_base(const iterator_base & that) : ptr_(that.ptr_) - { refer(); } - public: - /// Destructor - /// (Not virtual, since the derived classes are mere shells.) - ~iterator_base() { unref(ptr_); } - - // Assignment - iterator_base & operator=(const iterator_base & that) - // NOTE: This is defined here because MSVC was unable to match the - // definition to the declaration when they were separate. - { - // Update our pointer. - node_t * old_ptr = ptr_; - ptr_ = that.ptr_; - - // Update reference counts. - refer(); - unref(old_ptr); - - return *this; - } - - // Comparison: - bool operator==(const iterator_base & that) const { return ptr_ == that.ptr_; } - bool operator!=(const iterator_base & that) const { return ptr_ != that.ptr_; } - - // Dereference: - reference operator*() const { return *ptr_->dat_ptr; } - pointer operator->() const { return ptr_->dat_ptr; } - - // Increment/decrement: - iterator_base & operator++() { advance(Reversed); return *this; } - iterator_base & operator--() { advance(!Reversed); return *this; } - iterator_base operator++(int) - // NOTE: This is defined here because MSVC was unable to match the - // definition to the declaration when they were separate. - { - iterator_base retval(*this); - advance(Reversed); - return retval; - } - iterator_base operator--(int) - // NOTE: This is defined here because MSVC was unable to match the - // definition to the declaration when they were separate. - { - iterator_base retval(*this); - advance(!Reversed); - return retval; - } - - /// Test for being in a list, rather than past-the-end (or unassigned). - bool derefable() const { return derefable(ptr_); } - - - private: // functions - /// Test for being in a list, rather than past-the-end (or unassigned). - static bool derefable(node_t * ptr){ return ptr && ptr->dat_ptr; } - /// Advances our pointer to an unflagged node, possibly in the reverse direction. - void advance(bool reverse); - /// Advances our pointer one step, possibly in the reverse direction. - void inc(bool reverse) { ptr_ = reverse ? ptr_->prev : ptr_->next; } - /// Make sure we are not pointing to a flagged node. - void skip_flagged(bool reverse=Reversed); - /// Add a reference. - void refer() const; - /// Remove a reference. - static void unref(node_t * old_ptr); - - private: // data - node_t * ptr_; - }; - template friend class iterator_base; - -public: // The standard list interface, minus assignment. - typedef Data value_type; - typedef value_type * pointer; - typedef value_type & reference; - typedef const value_type & const_reference; - typedef size_t size_type; - - // Making these structs instead of typedefs cuts down on the length of - // compiler messages and helps define which conversions are allowed. - struct iterator : public iterator_base { - /// Default constructor - iterator() : iterator_base() {} - /// Initialized constructor - explicit iterator(node_t * ptr) : iterator_base(ptr) {} - /// Copy constructor. - iterator(const iterator & that) : iterator_base(that) {} - /// Conversion from reverse_iterator. - explicit iterator(const iterator_base & that) : iterator_base(that) {} - }; - struct const_iterator : public iterator_base { - /// Default constructor - const_iterator() : iterator_base() {} - /// Initialized constructor - explicit const_iterator(node_t * ptr) : iterator_base(ptr) {} - /// Copy constructor. - const_iterator(const const_iterator & that) : iterator_base(that) {} - /// Conversion from iterator. - const_iterator(const iterator_base & that) : iterator_base(that) {} - /// Conversion from const_reverse_iterator. - explicit const_iterator(const iterator_base & that) : iterator_base(that) {} - }; - struct reverse_iterator : public iterator_base { - /// Default constructor - reverse_iterator() : iterator_base() {} - /// Initialized constructor - explicit reverse_iterator(node_t * ptr) : iterator_base(ptr) {} - /// Copy constructor. - reverse_iterator(const reverse_iterator & that) : iterator_base(that) {} - /// Conversion from iterator. - explicit reverse_iterator(const iterator_base & that) : iterator_base(that) {} - }; - struct const_reverse_iterator : public iterator_base { - /// Default constructor - const_reverse_iterator() : iterator_base() {} - /// Initialized constructor - explicit const_reverse_iterator(node_t * ptr) : iterator_base(ptr) {} - /// Copy constructor. - const_reverse_iterator(const const_reverse_iterator & that) : iterator_base(that) {} - /// Conversion from reverse_iterator. - const_reverse_iterator(const iterator_base & that) : iterator_base(that) {} - /// Conversion from const_iterator. - explicit const_reverse_iterator(const iterator_base & that) : iterator_base(that) {} - }; - - - smart_list() : root_() {} - smart_list(size_type n); - smart_list(size_type n, const value_type & d); - smart_list(const smart_list & that); - template - smart_list(const InputIterator & f, const InputIterator & l); - ~smart_list() { clear(); } - - iterator begin() { return iterator(root_.next); } - iterator end() { return iterator(&root_); } - const_iterator begin() const { return const_iterator(root_.next); } - const_iterator end() const { return const_iterator(const_cast(&root_)); } - reverse_iterator rbegin() { return reverse_iterator(root_.prev); } - reverse_iterator rend() { return reverse_iterator(&root_); } - const_reverse_iterator rbegin() const { return const_reverse_iterator(root_.prev); } - const_reverse_iterator rend() const { return const_reverse_iterator(const_cast(&root_)); } - - size_type size() const; - size_type max_size() const { return size_type(-1); } - /// Note: if empty() returns true, the list might still contain nodes - /// flagged for deletion. - bool empty() const { return begin() == end(); } - - // Note: these functions use iterators so flagged nodes are skipped. - reference front() { return *begin(); } - const_reference front() const { return *begin(); } - reference back() { return *rbegin(); } - const_reference back() const { return *rbegin(); } - void push_front(const value_type & d) { insert(root_.next, d); } - void push_back(const value_type & d) { insert(&root_, d); } - void pop_front() { erase(begin()); } - void pop_back() { erase(iterator(rbegin())); } - - void swap(smart_list & that); - - static iterator insert(const iterator & pos, const value_type & d); - template - static void insert(const iterator & pos, const InputIterator & f, const InputIterator & l); - static void insert(const iterator & pos, size_type n, const value_type & d); - - static iterator erase(const iterator & pos); - static iterator erase(const iterator & start, const iterator & stop); - void clear() { erase(begin(), end()); } - - void resize(size_type n) { resize(n, value_type()); } - void resize(size_type n, const value_type & d); - - void splice(const iterator & pos, smart_list & L); - void splice(const iterator & pos, smart_list & L, const iterator & i); - void splice(const iterator & pos, smart_list & L, const iterator & f, const iterator & l); - - void remove(const value_type & value); - template - void remove_if(const Predicate & p); - - void unique() { unique(std::equal_to()); } - template - void unique(const BinaryPredicate p); - - void merge(smart_list & L) { merge(L, std::less()); } - template - void merge(smart_list & L, const BinaryPredicate & p); - - void sort() { sort(std::less()); } - template - void sort(const BinaryPredicate & p); - -private: // functions - /// No implementation of operator=() since that would not preserve iterators. - smart_list & operator=(const smart_list &); - - /// Returns true if @a node has been flagged for deletion. - static bool flagged(const node_t & node) { return node.ref_count % 2 == 0; } - /// Flags @a node for deletion. - static void flag(const node_t & node) { node.ref_count &= ~size_t(1); } - - // Low-level implementations. The list is designed so that a reference - // to the list itself is not needed. - static node_t * insert(node_t * const pos, const value_type & d); - static node_t * check_erase(node_t * const pos); - static void link(node_t * const pos, node_t & begin_link, node_t & end_link); - static void unlink(node_t & begin_unlink, node_t & end_unlink); - static void splice(node_t * const pos, node_t & f, node_t & l); - - -private: // data - /// Root of the list. - node_t root_; - // I started doing this using STL and Boost, but it was taking longer than - // it would take for me to write stuff from scratch. -}; - -/** Sized constructor */ -template -inline smart_list::smart_list(size_type n) : root_() -{ - for ( size_type i = 0; i < n; ++i ) - push_back(value_type()); -} -/** Sized constructor with a value */ -template -inline smart_list::smart_list(size_type n, const value_type & d) : root_() -{ - for ( size_type i = 0; i < n; ++i ) - push_back(d); -} -/** Copy constructor */ -template -inline smart_list::smart_list(const smart_list & that) : root_() -{ - // Copy non-flagged nodes. - for ( const_iterator it = that.begin(); it.derefable(); ++it ) - push_back(*it); -} -/** Constructor from a range */ -template template -inline smart_list::smart_list(const InputIterator & f, const InputIterator & l) : - root_() -{ - for ( InputIterator it = f; f != l; ++f ) - push_back(*it); -} - - -/** The size of the list, not counting flagged nodes. */ -template -inline typename smart_list::size_type smart_list::size() const -{ - size_type count = 0; - // Look for nodes not flagged for deletion. - for ( const_iterator it = begin(); it.derefable(); ++it ) - ++count; - return count; -} - -/** Swaps two lists. Done in constant time, with no iterator invalidation. */ -template -inline void smart_list::swap(smart_list & that) -{ - smart_list temp_list; - - // Swap, using splices instead of assignments. - temp_list.splice(temp_list.end(), that); - that.splice(that.end(), *this); - splice(end(), temp_list); -} - -/** Insert a node before @a pos. */ -template -inline typename smart_list::iterator smart_list::insert - (const iterator & pos, const value_type & d) -{ - return iterator(insert(pos.ptr_, d)); -} -/** Insert a range before @a pos. */ -template -template -inline void smart_list::insert(const iterator & pos, const InputIterator & f, - const InputIterator & l) -{ - for ( InputIterator it = f; it != l; ++it ) - insert(pos.ptr_, *it); -} -/** Insert @a n copies of @a d at @a pos. */ -template -inline void smart_list::insert(const iterator & pos, size_type n, - const value_type & d) -{ - for ( size_type i = 0; i < n; ++i ) - insert(pos.ptr_, d); -} - -/** Erase the node at @a pos. */ -template -inline typename smart_list::iterator smart_list::erase - (const iterator & pos) -{ - flag(*pos.ptr_); // We know *pos cannot get deleted yet because pos points to it. - return iterator(pos.ptr_->next); -} -/** Erase a range of nodes. */ -template -inline typename smart_list::iterator smart_list::erase - (const iterator & start, const iterator & stop) -{ - // Loop through all nodes from start to stop. - // We cannot rely on iterators because *stop might be flagged. - node_t * node_ptr = start.ptr_; - while ( node_ptr != stop.ptr_ && iterator::derefable(node_ptr) ) { - flag(*node_ptr); - node_ptr = check_erase(node_ptr); - } - - // node_ptr is more reliable than stop because of the derefable() condition. - return iterator(node_ptr); -} - -/** - * Resize the list. - * This does not count flagged nodes. - */ -template -inline void smart_list::resize(size_type n, const value_type & d) -{ - size_type count = 0; - iterator it = begin(); - iterator _end = end(); - - // See how our current size compares to n. - // (Not calling size(), since that would do at least as much work.) - while ( it != _end && count < n ) { - ++it; - ++count; - } - - if ( count < n ) - // We need more nodes. - insert(_end, n - count, d); - else if ( it != _end ) - // We need to remove nodes. - erase(it, _end); -} - -/** Splice all of @a L into *this before @a pos. */ -template -inline void smart_list::splice(const iterator & pos, smart_list & L) -{ - if ( L.root_.next != &L.root ) // if L has nodes to splice. - splice(pos.ptr_, *L.root_.next, *L.root_.prev); -} -/** Splice the node @a i points to (assumed from @a L) into *this before @a pos. */ -template -inline void smart_list::splice(const iterator & pos, smart_list & /*L*/, - const iterator & i) -{ - if ( i.ptr_.derefable() ) - splice(pos.ptr_, *i.ptr_, *i.ptr_); -} -/** Splice a range (assumed from @a L) into *this before @a pos. */ -template -inline void smart_list::splice(const iterator & pos, smart_list & /*L*/, - const iterator & f, const iterator & l) -{ - // Abort on degenerate cases. - if ( !f.derefable() || f == l ) - return; - - // Splice from f to the node before l. - splice(pos.ptr_, *(f.ptr_), *(l.ptr_->prev)); -} - -/** Remove all nodes whose data equals @a value. */ -template -inline void smart_list::remove(const value_type & value) -{ - // Look for elements to erase. - iterator it = begin(), _end = end(); - while ( it != _end ) - if ( *it == value ) - it = erase(it); - else - ++it; -} -/** Remove all nodes whose data satisfies @a p. */ -template -template -inline void smart_list::remove_if(const Predicate & p) -{ - // Look for elements to erase. - iterator it = begin(), _end = end(); - while ( it != _end() ) - if ( p(*it) ) - it = erase(it); - else - ++it; -} - -/** Remove nodes equal (under @a p) to their immediate predecessor. */ -template -template -inline void smart_list::unique(const BinaryPredicate p) -{ - // Initial state. - iterator _end = end(); - iterator cur = begin(); - iterator prev = cur; - // Look at the second node, making the first node "previous". - if ( cur != _end ) - ++cur; - - // Look for elements equal to their predecessors. - while ( cur != _end ) - if ( p(*prev, *cur) ) - // Duplicate. Remove it. - cur = erase(cur); - else - // Update the tracking of our previous element. - prev = cur++; -} - -/** - * Merge @a L into *this, using @a p to determine order. - * If both lists are sorted under @a p, then so is the merged list. - * This is stable; equivalent nodes retain their order, with nodes of *this - * considered to come before elements of @a L. - */ -template -template -inline void smart_list::merge(smart_list & L, const BinaryPredicate & p) -{ - // Merging a list into itself is a no-op, not a duplication. - if ( this == &L ) - return; - - node_t * dest = root_.next; - node_t * source = L.root_.next; - - // Basic merge loop. Continues until one of the lists is depleted. - while ( iterator::derefable(dest) && iterator::derefable(source) ) { - if ( p(*(source->dat_ptr), *(dest->dat_ptr)) ) { - // We found something to merge in. See if we can merge multiple - // nodes at once. - node_t * end_merge = source->next; - while ( iterator::derefable(end_merge) && p(*(end_merge->dat_ptr), *(dest->dat_ptr)) ) - end_merge = end_merge->next; - // Now end_merge is one past the nodes to merge. - // Move the nodes over. - splice(dest, *source, *(end_merge->prev)); - // Advance the source. - source = end_merge; - } - else - // Advance the destination. - dest = dest->next; - } - - // Append any remaining nodes from L. - if ( iterator::derefable(source) ) // hence, we made it to our end. - splice(&root_, *source, *(L.root_.prev)); -} - -/** - * Sort *this, using the order given by @a p. - * This is stable; equivalent nodes retain their order. - * This is an efficient sort, O(n lg n). - */ -template -template -inline void smart_list::sort(const BinaryPredicate & p) -{ - // We'll leverage merge() to do a merge sort. - - // First, get the real size of the list, so we can allocate memory before - // altering *this (exception safety). - size_type count = 0; - for ( node_t * node_ptr = root_.next; iterator::derefable(node_ptr); node_ptr = node_ptr->next ) - ++count; - // Abort on trivial cases. - if ( count < 2 ) - return; - - // Split *this into 1-length pieces. - std::vector sorter(count); // No memory allocations after this point. - for ( size_type i = 0; i < count; ++i ) - sorter[i].splice(&(sorter[i].root_), *(root_.next), *(root_.next)); - - // At the start of each iteration, step will be the distance between lists - // containing data. (There will be O(lg n) iterations.) - for ( size_type step = 1; step < count; step *= 2 ) - // Merge consecutive data-bearing lists. Summing over all iterations of - // this (inner) loop, there will be O(n) comparisons made. - for ( size_type i = 0; i + step < count; i += 2*step ) - sorter[i].merge(sorter[i+step], p); - - // The entire (sorted) list is now in the first element of the vector. - swap(sorter[0]); -} - - -/** - * Low-level insertion. - * The insertion will occur before @a pos. Pass &root_ to append to the list. - */ -template -inline typename smart_list::node_t * smart_list::insert - (node_t * const pos, const value_type & d) -{ - node_t * new_node = new node_t(d); - // The new node is essentially a 1-node list. - link(pos, *new_node, *new_node); - // Return a pointer to the new node. - return new_node; -} -/** - * Low-level erasure. - * The node will only be erased if its ref_count is 0. (So the caller must - * remove their reference before calling this.) - * @returns the next node in the list (could be a flagged node). - */ -template -inline typename smart_list::node_t * smart_list::check_erase - (node_t * const pos) -{ - // Sanity check. - if ( !iterator::derefable(pos) ) - return nullptr; - - // Remember our successor. - node_t * ret_val = pos->next; - - // Can we actually erase? - if ( pos->ref_count == 0 ) { - // Disconnect *pos from the list. - unlink(*pos, *pos); - // Now we can delete. - delete pos; - } - - return ret_val; -} - -/** - * Assuming the nodes from @a begin_link to @a end_link are linked, they will - * be inserted into *this before @a pos. (Pass &root_ to append to the list.) - * Note that *end_link is included in the insert; this is not exactly - * analogous to a range of iterators. - * This is a constant-time operation; reference counts are not updated. - */ -template -inline void smart_list::link(node_t * const pos, node_t & begin_link, node_t & end_link) -{ - // Link the new nodes into the list. - begin_link.prev = pos->prev; - end_link.next = pos; - - // Link the list to the new nodes. - begin_link.prev->next = &begin_link; - end_link.next->prev = &end_link; -} -/** - * Assuming @a begin_unlink and @a end_unlink are nodes from *this, with - * @a end_unlink a successor of @a begin_unlink, that chain of nodes will - * be removed from *this. - * Ownership of the removed chain is transferred to the caller, who becomes - * repsonsible for not leaving the nodes in limbo. - * This is a constant-time operation; reference counts are not updated. - */ -template -inline void smart_list::unlink(node_t & begin_unlink, node_t & end_unlink) -{ - // Disconnect the list from the nodes. - begin_unlink.prev->next = end_unlink.next; - end_unlink.next->prev = begin_unlink.prev; - - // Disconnect the nodes from the list. This leaves the nodes in limbo. - end_unlink.next = nullptr; - begin_unlink.prev = nullptr; -} - -/** - * Low-level splice of the chain from @a b to @a e (including b and e) - * to the spot before @a pos. - */ -template -inline void smart_list::splice(node_t * const pos, node_t & b, node_t & e) -{ - // Remove from the old list. - unlink(b, e); - // Splice into the new list. - link(pos, b, e); -} - - -/** - * Equality, if lists contain equal elements in the same order. - */ -template -inline bool operator==(const smart_list & a, const smart_list & b) -{ - typename smart_list::const_iterator end_a = a.end(); - typename smart_list::const_iterator end_b = b.end(); - typename smart_list::const_iterator it_a = a.begin(); - typename smart_list::const_iterator it_b = b.begin(); - - for ( ; it_a != end_a && it_b != end_b; ++it_a, ++it_b ) - if ( *it_a != *it_b ) - // mismatch - return false; - - // All comparisons were equal, so they are the same if we compared everything. - return it_a == end_a && it_b == end_b; -} -/** - * Lexicographical order. - */ -template -inline bool operator<(const smart_list & a, const smart_list & b) -{ - typename smart_list::const_iterator end_a = a.end(); - typename smart_list::const_iterator end_b = b.end(); - typename smart_list::const_iterator it_a = a.begin(); - typename smart_list::const_iterator it_b = b.begin(); - - for ( ; it_a != end_a && it_b != end_b; ++it_a, ++it_b ) - if ( *it_a < *it_b ) - // a is less than b. - return true; - else if ( *it_b < *it_a ) - // b is less than a. - return false; - - // All comparisons were equal, so a is less than b if a is shorter. - return it_b != end_b; -} - - -/* ** smart_list::node_t ** */ - -template -inline smart_list::node_t::~node_t() -{ - // Some safety checks. - if ( dat_ptr == nullptr ) - // Root node: make sure there are no lingering iterators to the list. - assert(next == this); - else { - // Normal node: make sure we are not still in a list. - assert(next == nullptr && prev == nullptr); - // Make sure no iterators point to us. - assert(ref_count == 0); - } - - delete dat_ptr; -} - - -/* ** smart_list::iterator_base ** */ - -/** - * Advances our pointer to an unflagged node, possibly in the reverse direction. - * This will advance at least one step, and will not stop on a flagged node. - * In addition, this takes care of updating reference counts. - * (This is the code shared by increments and decrements.) - */ -template -template -inline void smart_list::iterator_base::advance(bool reverse) -{ - node_t * old_ptr = ptr_; - - // Guarantee a change. - inc(reverse); - // Skip as necessary. - skip_flagged(reverse); - - // Update reference counts. - refer(); - unref(old_ptr); -} - -/** Make sure we are not pointing to a flagged node. */ -template -template -inline void smart_list::iterator_base::skip_flagged - (bool reverse) -{ - while ( derefable() && smart_list::flagged(*ptr_) ) - inc(reverse); -} - -/** Add a reference to that to which we point. */ -template -template -inline void smart_list::iterator_base::refer() const -{ - if ( derefable() ) - ptr_->ref_count += 2; -} - -/** - * Remove a reference. - * May delete old_ptr. So call this after updating ptr_ to a new value. - */ -template -template -inline void smart_list::iterator_base::unref(node_t * old_ptr) -{ - if ( derefable(old_ptr) ) { - // Remove a reference. - old_ptr->ref_count -= 2; - smart_list::check_erase(old_ptr); - } -} - -}// namespace utils