Game Events: refactored event handler storage
This throws out the custom smart_list class in favor of a plain std::list. It also greatly simplifies a few things. First, event handlers no longer remove themselves from the main list in event_handlers. Now they just flag themselves as disabled (which means they will never execute once marked) and cleaned up later in a newly added cleanup stage. This means a handler no longer needs to keep its index in the active handler vector. This removal of reliance on indices also means I could add the aforementioned cleanup stage. With the smart_list code, event handlers were never actually removed from the active vector, nor any weak_ptrs pointing to them removed either. This wasn't exactly a problem, since the handlers were stored via shared_ptrs which would then simply be null after one deleted itself. Still, it's cleaner to drop any invalid ones (and unlockable weak_ptrs) from any relevant containers. I've opted to do this in manager::execute_on_events. Seems a good enough place as any. The net result of this is the code is much cleaner. We're able to get rid of a bunch of unnecessary feelers into various classes. This also makes the manager::iteration dereference code a lot easier to understand. There certainly could be further refactoring, but I think this is a good start.
This commit is contained in:
parent
f62b62b700
commit
056d7ac8f8
8 changed files with 104 additions and 1056 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "config.hpp"
|
||||
#include "utils/smart_list.hpp"
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
@ -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<event_handler> handler_ptr;
|
||||
|
||||
/// Storage of event handlers.
|
||||
typedef std::vector<handler_ptr> handler_vec;
|
||||
using handler_ptr = std::shared_ptr<event_handler>;
|
||||
using weak_handler_ptr = std::weak_ptr<event_handler>;
|
||||
using handler_list = std::list<weak_handler_ptr>;
|
||||
|
||||
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 @@ public:
|
|||
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 @@ public:
|
|||
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<event_handler> internal_ptr;
|
||||
|
||||
/// The underlying list.
|
||||
typedef utils::smart_list<internal_ptr> 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<list_t&>(data_).begin());
|
||||
}
|
||||
|
||||
// The above const_cast is so the iterator can remove obsolete entries.
|
||||
const_iterator end() const
|
||||
{
|
||||
return iterator(const_cast<list_t&>(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_;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -69,23 +69,13 @@ private:
|
|||
// 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_;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -25,15 +25,16 @@ namespace game_events
|
|||
class event_handlers
|
||||
{
|
||||
private:
|
||||
typedef std::unordered_map<std::string, handler_list> map_t;
|
||||
typedef std::unordered_map<std::string, std::weak_ptr<event_handler>> id_map_t;
|
||||
using handler_vec_t = std::vector<handler_ptr>;
|
||||
using map_t = std::unordered_map<std::string, handler_list>;
|
||||
using id_map_t = std::unordered_map<std::string, weak_handler_ptr>;
|
||||
|
||||
/**
|
||||
* 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 @@ private:
|
|||
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 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
/** 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,829 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2014 - 2017 by David White <dave@whitevine.net>
|
||||
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 <cassert>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
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 Data>
|
||||
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 Value, bool Reversed>
|
||||
class iterator_base
|
||||
{
|
||||
friend class smart_list<Data>;
|
||||
typedef typename smart_list<Data>::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 <class V, bool R>
|
||||
iterator_base(const iterator_base<V,R> & 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 <class Value, bool Reversed> 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<Data, false> {
|
||||
/// Default constructor
|
||||
iterator() : iterator_base<Data, false>() {}
|
||||
/// Initialized constructor
|
||||
explicit iterator(node_t * ptr) : iterator_base<Data, false>(ptr) {}
|
||||
/// Copy constructor.
|
||||
iterator(const iterator & that) : iterator_base<Data, false>(that) {}
|
||||
/// Conversion from reverse_iterator.
|
||||
explicit iterator(const iterator_base<Data, true> & that) : iterator_base<Data, false>(that) {}
|
||||
};
|
||||
struct const_iterator : public iterator_base<const Data, false> {
|
||||
/// Default constructor
|
||||
const_iterator() : iterator_base<const Data, false>() {}
|
||||
/// Initialized constructor
|
||||
explicit const_iterator(node_t * ptr) : iterator_base<const Data, false>(ptr) {}
|
||||
/// Copy constructor.
|
||||
const_iterator(const const_iterator & that) : iterator_base<const Data, false>(that) {}
|
||||
/// Conversion from iterator.
|
||||
const_iterator(const iterator_base<Data, false> & that) : iterator_base<const Data, false>(that) {}
|
||||
/// Conversion from const_reverse_iterator.
|
||||
explicit const_iterator(const iterator_base<const Data, true> & that) : iterator_base<const Data, false>(that) {}
|
||||
};
|
||||
struct reverse_iterator : public iterator_base<Data, true> {
|
||||
/// Default constructor
|
||||
reverse_iterator() : iterator_base<Data, true>() {}
|
||||
/// Initialized constructor
|
||||
explicit reverse_iterator(node_t * ptr) : iterator_base<Data, true>(ptr) {}
|
||||
/// Copy constructor.
|
||||
reverse_iterator(const reverse_iterator & that) : iterator_base<Data, true>(that) {}
|
||||
/// Conversion from iterator.
|
||||
explicit reverse_iterator(const iterator_base<Data, false> & that) : iterator_base<Data, true>(that) {}
|
||||
};
|
||||
struct const_reverse_iterator : public iterator_base<const Data, true> {
|
||||
/// Default constructor
|
||||
const_reverse_iterator() : iterator_base<const Data, true>() {}
|
||||
/// Initialized constructor
|
||||
explicit const_reverse_iterator(node_t * ptr) : iterator_base<const Data, true>(ptr) {}
|
||||
/// Copy constructor.
|
||||
const_reverse_iterator(const const_reverse_iterator & that) : iterator_base<const Data, true>(that) {}
|
||||
/// Conversion from reverse_iterator.
|
||||
const_reverse_iterator(const iterator_base<Data, true> & that) : iterator_base<const Data, true>(that) {}
|
||||
/// Conversion from const_iterator.
|
||||
explicit const_reverse_iterator(const iterator_base<const Data, false> & that) : iterator_base<const Data, true>(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 <class InputIterator>
|
||||
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<node_t *>(&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<node_t *>(&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 <class InputIterator>
|
||||
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 <class Predicate>
|
||||
void remove_if(const Predicate & p);
|
||||
|
||||
void unique() { unique(std::equal_to<value_type>()); }
|
||||
template <class BinaryPredicate>
|
||||
void unique(const BinaryPredicate p);
|
||||
|
||||
void merge(smart_list & L) { merge(L, std::less<value_type>()); }
|
||||
template <class BinaryPredicate>
|
||||
void merge(smart_list & L, const BinaryPredicate & p);
|
||||
|
||||
void sort() { sort(std::less<value_type>()); }
|
||||
template <class BinaryPredicate>
|
||||
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 <class Data>
|
||||
inline smart_list<Data>::smart_list(size_type n) : root_()
|
||||
{
|
||||
for ( size_type i = 0; i < n; ++i )
|
||||
push_back(value_type());
|
||||
}
|
||||
/** Sized constructor with a value */
|
||||
template <class Data>
|
||||
inline smart_list<Data>::smart_list(size_type n, const value_type & d) : root_()
|
||||
{
|
||||
for ( size_type i = 0; i < n; ++i )
|
||||
push_back(d);
|
||||
}
|
||||
/** Copy constructor */
|
||||
template <class Data>
|
||||
inline smart_list<Data>::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 <class Data> template <class InputIterator>
|
||||
inline smart_list<Data>::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 <class Data>
|
||||
inline typename smart_list<Data>::size_type smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline typename smart_list<Data>::iterator smart_list<Data>::insert
|
||||
(const iterator & pos, const value_type & d)
|
||||
{
|
||||
return iterator(insert(pos.ptr_, d));
|
||||
}
|
||||
/** Insert a range before @a pos. */
|
||||
template <class Data>
|
||||
template <class InputIterator>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline typename smart_list<Data>::iterator smart_list<Data>::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 <class Data>
|
||||
inline typename smart_list<Data>::iterator smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
template <class Predicate>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
template <class BinaryPredicate>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
template <class BinaryPredicate>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
template <class BinaryPredicate>
|
||||
inline void smart_list<Data>::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<smart_list> 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 <class Data>
|
||||
inline typename smart_list<Data>::node_t * smart_list<Data>::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 <class Data>
|
||||
inline typename smart_list<Data>::node_t * smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline void smart_list<Data>::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 <class Data>
|
||||
inline bool operator==(const smart_list<Data> & a, const smart_list<Data> & b)
|
||||
{
|
||||
typename smart_list<Data>::const_iterator end_a = a.end();
|
||||
typename smart_list<Data>::const_iterator end_b = b.end();
|
||||
typename smart_list<Data>::const_iterator it_a = a.begin();
|
||||
typename smart_list<Data>::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 <class Data>
|
||||
inline bool operator<(const smart_list<Data> & a, const smart_list<Data> & b)
|
||||
{
|
||||
typename smart_list<Data>::const_iterator end_a = a.end();
|
||||
typename smart_list<Data>::const_iterator end_b = b.end();
|
||||
typename smart_list<Data>::const_iterator it_a = a.begin();
|
||||
typename smart_list<Data>::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 <class Data>
|
||||
inline smart_list<Data>::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 <class Data>
|
||||
template <class Value, bool Reversed>
|
||||
inline void smart_list<Data>::iterator_base<Value, Reversed>::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 <class Data>
|
||||
template <class Value, bool Reversed>
|
||||
inline void smart_list<Data>::iterator_base<Value, Reversed>::skip_flagged
|
||||
(bool reverse)
|
||||
{
|
||||
while ( derefable() && smart_list<Data>::flagged(*ptr_) )
|
||||
inc(reverse);
|
||||
}
|
||||
|
||||
/** Add a reference to that to which we point. */
|
||||
template <class Data>
|
||||
template <class Value, bool Reversed>
|
||||
inline void smart_list<Data>::iterator_base<Value, Reversed>::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 <class Data>
|
||||
template <class Value, bool Reversed>
|
||||
inline void smart_list<Data>::iterator_base<Value, Reversed>::unref(node_t * old_ptr)
|
||||
{
|
||||
if ( derefable(old_ptr) ) {
|
||||
// Remove a reference.
|
||||
old_ptr->ref_count -= 2;
|
||||
smart_list<Data>::check_erase(old_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
}// namespace utils
|
Loading…
Add table
Reference in a new issue