/** \file dynamic_set_union.h */ // -*-c++-*- // Copyright (C) 2010 Daniel Burrows // // 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; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, write to // the Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307, USA. #ifndef DYNAMIC_SET_UNION_H #define DYNAMIC_SET_UNION_H #include "dynamic_set.h" #include #include #include #include namespace aptitude { namespace util { /** \brief A read-only dynamic set representing the union of one * or more dynamic sets. */ template class dynamic_set_union : public dynamic_set, public boost::enable_shared_from_this > { typedef boost::unordered_map value_counts_t; // Counts how many times each element occurs. By assumption, // individual sets can contain an element only once, so this // effectively counts the number of individual sets containing // the element. value_counts_t value_counts; // Keeps sets contained in this union alive, and also stores the // connections binding each set to this object's handlers. typedef boost::unordered_multimap >, sigc::connection> contained_sets_t; // Maintains pointers to the individual sets this contains. contained_sets_t contained_sets; sigc::signal signal_inserted; sigc::signal signal_removed; void handle_inserted(const T &t); void handle_removed(const T &t); class set_enumerator; public: dynamic_set_union(); static boost::shared_ptr create(); void insert_set(const boost::shared_ptr > &set); void remove_set(const boost::shared_ptr > &set); std::size_t size(); boost::shared_ptr > enumerate(); sigc::connection connect_inserted(const sigc::slot &slot); sigc::connection connect_removed(const sigc::slot &slot); }; template dynamic_set_union::dynamic_set_union() { } template boost::shared_ptr > dynamic_set_union::create() { return boost::make_shared >(); } template void dynamic_set_union::insert_set(const boost::shared_ptr > &set) { if(contained_sets.find(set) == contained_sets.end()) { for(boost::shared_ptr > e = set->enumerate(); e->advance(); ) handle_inserted(e->get_current()); sigc::connection inserted_connection = set->connect_inserted(sigc::mem_fun(*this, &dynamic_set_union::handle_inserted)); sigc::connection removed_connection = set->connect_removed(sigc::mem_fun(*this, &dynamic_set_union::handle_removed)); contained_sets.insert(std::make_pair(set, inserted_connection)); contained_sets.insert(std::make_pair(set, removed_connection)); } } template void dynamic_set_union::remove_set(const boost::shared_ptr > &set) { // Paranoia: defensively take an extra reference in case "set" // is somehow being kept alive by our reference (which should be // impossible). const boost::shared_ptr > set_copy = set; typedef typename contained_sets_t::iterator contained_iterator; const std::pair found = contained_sets.equal_range(set); if(found.first != found.second) { for(boost::shared_ptr > e = set->enumerate(); e->advance(); ) handle_removed(e->get_current()); for(contained_iterator it = found.first; it != found.second; ++it) it->second.disconnect(); contained_sets.erase(found.first, found.second); } } template std::size_t dynamic_set_union::size() { return value_counts.size(); } template boost::shared_ptr > dynamic_set_union::enumerate() { return boost::make_shared(this->shared_from_this(), value_counts.begin(), value_counts.end()); } template sigc::connection dynamic_set_union::connect_inserted(const sigc::slot &slot) { return signal_inserted.connect(slot); } template sigc::connection dynamic_set_union::connect_removed(const sigc::slot &slot) { return signal_removed.connect(slot); } template void dynamic_set_union::handle_inserted(const T &t) { typename value_counts_t::iterator found = value_counts.find(t); if(found != value_counts.end()) ++found->second; else { value_counts.insert(std::make_pair(t, 1)); signal_inserted(t); } } template void dynamic_set_union::handle_removed(const T &t) { typename value_counts_t::iterator found = value_counts.find(t); if(found != value_counts.end()) { --found->second; if(found->second == 0) { // Copy the value to send it to the signal. const T value = found->first; value_counts.erase(found); signal_removed(value); } } } template class dynamic_set_union::set_enumerator : public enumerator { public: typedef typename dynamic_set_union::value_counts_t::const_iterator values_iterator; private: boost::shared_ptr > parent; values_iterator begin, end; bool is_first; public: set_enumerator(const boost::shared_ptr > &_parent, const values_iterator &_begin, const values_iterator &_end); T get_current(); bool advance(); }; template dynamic_set_union::set_enumerator::set_enumerator(const boost::shared_ptr > &_parent, const values_iterator &_begin, const values_iterator &_end) : parent(_parent), begin(_begin), end(_end), is_first(true) { } template T dynamic_set_union::set_enumerator::get_current() { return begin->first; } template bool dynamic_set_union::set_enumerator::advance() { if(is_first) is_first = false; else ++begin; return begin != end; } } } #endif // DYNAMIC_SET_UNION_H