/* DEFSREGISTRY.hpp - implementation of the default object store Copyright (C) Lumiera.org 2008, Hermann Vosseler 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; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file defsregistry.hpp ** A piece of implementation code factored out into a separate header (include). ** Only used in defsmanager.cpp and for the unit tests. We can't place it into ** a separate compilation unit, because defsmanager.cpp defines some explicit ** template instantiaton, which cause the different Slots of the DefsrRegistry#table_ ** to be filled with data and defaults for the specific Types. ** ** @see mobject::session::DefsManager ** @see defsregistryimpltest.cpp ** */ #ifndef MOBJECT_SESSION_DEFSREGISTRY_H #define MOBJECT_SESSION_DEFSREGISTRY_H #include "common/multithread.hpp" #include "common/query.hpp" #include "common/util.hpp" #include "common/p.hpp" #include #include #include #include #include #include namespace mobject { namespace session { using lumiera::P; using lumiera::Query; using lumiera::Thread; using std::tr1::weak_ptr; using std::string; using boost::format; using boost::lambda::_1; using boost::lambda::var; namespace impl { namespace { uint maxSlots (0); ///< number of different registered Types format dumpRecord ("%2i| %64s --> %s\n"); } struct TableEntry { virtual ~TableEntry() {}; }; /** we maintain an independent defaults registry * for every participating kind of objects */ typedef std::vector< P > Table; /** * holding a single "default object" entry */ template struct Record { uint degree; Query query; weak_ptr objRef; Record (const Query& q, const P& obj) : degree (lumiera::query::countPraed (q)), query (q), objRef (obj) { } struct Search ///< Functor searching for a specific object { Search (const P& obj) : obj_(obj) { } const P& obj_; bool operator() (const Record& rec) { P storedObj (rec.objRef.lock()); return storedObj && (storedObj == obj_); } }; struct OrderRelation { inline bool operator() (Record one, Record two) ///< @note doesn't touch the objRef { return ( one.degree < two.degree ||(one.degree == two.degree && one.query < two.query) ); } }; operator string () const { return str (dumpRecord % degree % query % dumpObj()); } string dumpObj () const { P o (objRef.lock()); return o? string(*o):"dead"; } }; /** every new kind of object (Type) creates a new * slot in the main Table holding all registered * default objects. Each slot actually holds a * separate tree (set) of registry entries */ template struct Slot : public TableEntry { typedef typename Record::OrderRelation Ordering; typedef std::set, Ordering> Registry; Registry registry; static size_t index; ///< where to find this Slot in every Table static Registry& access (Table& table) { if ( !index || index > table.size() ||!table[index-1]) createSlot (table); ASSERT (0 < index && index<=table.size() && table[index-1]); Slot* item = static_cast (table[index-1].get()); return item->registry; } private: static void createSlot (Table& table) { Thread::Lock guard SIDEEFFECT; if (!index) index = ++maxSlots; if (index > table.size()) table.resize (index); table[index-1].reset(new Slot); } }; // static vars to organize one Table Slot per type.... template size_t Slot::index (0); /** * @internal Helper for organizing preconfigured default objects. * Maintaines a collection of objects known or encountered as "default" * for a given type. This collection is ordered by "degree of constriction", * which is implemented by counting the number of predicates in the query * used to define or identify each object. * Accessing an object identified by a query causes the query to be resolved * (executed in prolog), which may result in some additional properties of * the object being bound or specified. * @todo as of 3/2008 the real query implementation is missing, and the * exact behaviour has to be defined. */ class DefsRegistry : private boost::noncopyable { Table table_; public: /** used for enumerating solutions */ template class Iter { friend class DefsRegistry; typedef typename Slot::Registry::iterator II; II p,i,e; P next, ptr; Iter (II from, II to) ///< just ennumerates the given range : p(from), i(from), e(to) { if (i!=e) ++i; // p is next to be tested, i always one ahead operator++ (); } Iter (II match, II from, II to) ///< returns direct match first, then ennumerates : p(match), i(from), e(to) { operator++ (); // init to first element (or to null if emty) } P findNext () throw() { while (!next) { if (p==e) break; next = p->objRef.lock(); p = i++; } return next; } public: P operator* () { return ptr; } bool hasNext () { return next || findNext(); } Iter operator++ (int) { Iter tmp=*this; operator++(); return tmp; } Iter& operator++ () { ptr=findNext(); next.reset(); return *this; } }; /** find a sequence of "default" objects possibliy matching the query. * If there was a registration for some object of the given kind with * the \em same query, this one will be first in the sequence. Besides, * the sequence will yield all still existing registered "default" objects * of this kind, ordered ascending by "degree of constriction", i.e. starting * with the object registered togehter with the shortest query. * @return a forward input iterator yielding this sequence * @note none of the queries will be evaluated (we're just counting predicates) */ template Iter candidates (const Query& query) { P dummy; Record entry (query, dummy); typedef typename Slot::Registry Registry; Registry& registry = Slot::access(table_); // try to get a possible direct match (same query) typename Registry::iterator pos = registry.find (entry); typename Registry::iterator end = registry.end(); if (pos==end) return Iter (registry.begin(), end); // just ennumerate contents else return Iter (pos, registry.begin(), end); // start with direct match } /** register the object as being "default" when searching something * similar as designated by the given query. Only a weak ref is stored. * @param obj Will be rebound, if another object is already stored. * @return true if object has actually been stored, false if another * object is registered for exactly the same query. In this * case, also the param obj shared-ptr is rebound! */ template bool put (P& obj, const Query& query) { Record entry (query, obj); typedef typename Slot::Registry Registry; typedef typename Registry::iterator RIter; Registry& registry = Slot::access(table_); RIter pos = registry.lower_bound (entry); if ( pos!=registry.end() && pos->query == query) { P storedObj (pos->objRef.lock()); if (storedObj) return (storedObj == obj); else // use the opportunity and purge the expired entry registry.erase(pos++); } // no existing entry.... registry.insert(pos, entry); ENSURE (registry.find (entry) != registry.end()); return true; } /** if this object is registered as "default" in some way, drop the registration. * @return false if the object wasn't registered at all. */ template bool forget (const P& obj) { typedef typename Slot::Registry Registry; typedef typename Record::Search SearchFunc; Registry& registry = Slot::access(table_); return util::remove_if(registry, SearchFunc (obj)); } /** helper for diagnostics. * @note to use it, your objects need an operator string() */ template string dump() { string res; util::for_each ( Slot::access(table_) , var(res) += _1 ); return res; } }; } // (End) impl namespace using impl::DefsRegistry; } // namespace mobject::session } // namespace mobject #endif