diff --git a/src/common/appconfig.cpp b/src/common/appconfig.cpp index 612376022..17a245cdf 100644 --- a/src/common/appconfig.cpp +++ b/src/common/appconfig.cpp @@ -37,13 +37,6 @@ using util::isnil; namespace lumiera { - /** This internal pointer to the single instance is deliberately - * not initialized (i.e. rely on implicit initialisation to 0), - * because when static init reaches this definition, the - * Appconfig::instance() probably already has been called - * by another compilation unit. This is ugliy, but preferable - * to beeing dependant on inclusion order of headers. */ -// scoped_ptr Appconfig::theApp_; #ifndef LUMIERA_VERSION #define LUMIERA_VERSION 3++devel diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp index a7f0fe37a..890a36a86 100644 --- a/src/common/configrules.hpp +++ b/src/common/configrules.hpp @@ -148,7 +148,7 @@ namespace lumiera * @query any goals to be fulfilled by the solution. * @return false if resolution failed. In this case, solution ptr is empty. */ - virtual bool resolve (shared_ptr solution, const Query& q) = 0; + virtual bool resolve (shared_ptr& solution, const Query& q) = 0; }; // TODO: the Idea is to provide specialisations for the concrete types diff --git a/src/common/multithread.hpp b/src/common/multithread.hpp index 1dbbac519..0e936964c 100644 --- a/src/common/multithread.hpp +++ b/src/common/multithread.hpp @@ -27,6 +27,7 @@ #define LUMIERA_MULTITHREAD_H #include "nobugcfg.h" +#include "common/util.hpp" namespace lumiera diff --git a/src/common/query.cpp b/src/common/query.cpp index 266c628e5..09c642f1c 100644 --- a/src/common/query.cpp +++ b/src/common/query.cpp @@ -33,6 +33,7 @@ using std::map; using boost::regex; using boost::smatch; using boost::regex_search; +using boost::sregex_iterator; using boost::algorithm::is_upper; using boost::algorithm::is_alpha; @@ -63,6 +64,9 @@ namespace lumiera namespace // Implementation details { map regexTable; + + Symbol matchArgument = "\\(\\s*([\\w_\\.\\-]+)\\s*\\)"; + regex findPredicate (string("(\\w+)")+matchArgument); } /** (preliminary) helper: instead of really parsing and evaluating the terms, @@ -76,7 +80,7 @@ namespace lumiera { if (!contains (regexTable, sym)) - regexTable[sym] = regex (string(sym)+="\\(\\s*([\\w_\\.\\-]+)\\s*\\)"); + regexTable[sym] = regex (string(sym)+=matchArgument); smatch match; if (regex_search (termString, match, regexTable[sym])) @@ -86,6 +90,23 @@ namespace lumiera } + + /** @note this is a very hackish preliminary implementation. + * The regex used will flounder when applied to nested terms. + * We need a real parser for predicate logic terms (which we + * probably get for free when we embed a prolog system)... + */ + uint + countPraed (const string& q) + { + uint cnt (0); + sregex_iterator end; + for (sregex_iterator i (q.begin(),q.end(), findPredicate); + i != end; ++i) + ++cnt; + return cnt; + } + } // namespace query diff --git a/src/common/query.hpp b/src/common/query.hpp index 591c45c74..b7d22498b 100644 --- a/src/common/query.hpp +++ b/src/common/query.hpp @@ -71,6 +71,11 @@ namespace lumiera const string extractID (Symbol, const string& termString); + /** count the top-level predicates in the query string. + * usable for ordering queries, as more predicates usually + * mean more conditions, i.e. more constriction + */ + uint countPraed (const string&); } // namespace query diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp index 909f7ee50..5a1531cc9 100644 --- a/src/common/query/mockconfigrules.hpp +++ b/src/common/query/mockconfigrules.hpp @@ -111,10 +111,14 @@ namespace lumiera { const any& entry = fetch_from_table_for (q.asKey()); if (!isnil (entry)) - if (! solution - ||(solution && *solution == *candidate) // simulates a real unification - ) - return solution = candidate; + { + const Ret& candidate (any_cast (entry)); + if (! solution +// ||(solution && *solution == *candidate) // simulates a real unification +//////////////TODO: not only Assets (i.e. define comparison Operators on Assets!) + ) + return solution = candidate; + } return solution = Ret(); // fail: return default-constructed empty smart ptr } diff --git a/src/common/util.hpp b/src/common/util.hpp index a4c3c4b54..d56a45432 100644 --- a/src/common/util.hpp +++ b/src/common/util.hpp @@ -124,6 +124,39 @@ namespace util } + /** remove all elements fulfilling a given predicate + * from a (sorted) set. + * @return true if anything has been removed. */ + template + bool remove_if (SET& set, PRD test) + { + typedef typename SET::iterator Itor; + bool found = false; + Itor end = set.end(); + Itor begin = set.begin(); + Itor pos = begin; + while (pos!=end) + { + if (!test (*pos)) ++pos; + else + { + found = true; + if (pos==begin) + { + set.erase(pos); + pos = begin = set.begin(); + } + else + { + set.erase(pos--); + ++pos; + } + end = set.end(); + } } + return found; + } + + /** shortcut for operating on all elements of a container. * Isn't this already defined somewhere? It's so obvious.. */ diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp index 25a14ee25..df6c5f1f6 100644 --- a/src/proc/asset/struct.hpp +++ b/src/proc/asset/struct.hpp @@ -45,13 +45,13 @@ #include #include -using std::string; -using std::wstring; -using boost::scoped_ptr; namespace asset { + using std::string; + using std::wstring; + using boost::scoped_ptr; using lumiera::Query; class Struct; diff --git a/src/proc/mobject/session/defsmanager.cpp b/src/proc/mobject/session/defsmanager.cpp index 9188df748..4c5cd9f82 100644 --- a/src/proc/mobject/session/defsmanager.cpp +++ b/src/proc/mobject/session/defsmanager.cpp @@ -30,7 +30,6 @@ using boost::format; -using asset::Query; using lumiera::ConfigRules; using lumiera::query::QueryHandler; using lumiera::query::LUMIERA_ERROR_CAPABILITY_QUERY; @@ -43,13 +42,10 @@ namespace mobject using std::tr1::shared_ptr; - namespace // Implementation details - { - DefsRegistry& defaultsRegistry; //////////////////TODO - } /** initialize the most basic internal defaults. */ DefsManager::DefsManager () throw() + : defsRegistry(new DefsRegistry) { TODO ("setup basic defaults of the session"); } @@ -59,7 +55,17 @@ namespace mobject shared_ptr DefsManager::search (const Query& capabilities) { - defaultsRegistry.get (capabilities); + shared_ptr res; + QueryHandler& typeHandler = ConfigRules::instance(); + for (DefsRegistry::Iter i = defsRegistry->candidates(capabilities); + i.hasNext(); ++i ) + { + shared_ptr res (*i); + typeHandler.resolve (res, capabilities); + if (res) + return res; + } + return res; // "no solution found" } @@ -71,7 +77,7 @@ namespace mobject QueryHandler& typeHandler = ConfigRules::instance(); typeHandler.resolve (res, capabilities); if (res) - defaultsRegistry.put (res, capabilities); + defsRegistry->put (res, capabilities); return res; } @@ -86,7 +92,7 @@ namespace mobject if (!candidate) return false; else - return defaultsRegistry.put (candidate, capabilities); + return defsRegistry->put (candidate, capabilities); } @@ -94,7 +100,7 @@ namespace mobject bool DefsManager::forget (const shared_ptr& defaultObj) { - defaultsRegistry.forget (defaultObj); + return defsRegistry->forget (defaultObj); } @@ -119,8 +125,9 @@ namespace mobject } // namespace mobject::session } // namespace mobject - - + + + /***************************************************************/ /* explicit template instantiations for querying various Types */ /***************************************************************/ diff --git a/src/proc/mobject/session/defsmanager.hpp b/src/proc/mobject/session/defsmanager.hpp index da98fe6c1..753b452d2 100644 --- a/src/proc/mobject/session/defsmanager.hpp +++ b/src/proc/mobject/session/defsmanager.hpp @@ -27,6 +27,8 @@ #include "common/query.hpp" +#include +#include #include @@ -36,6 +38,9 @@ namespace mobject namespace session { using std::tr1::shared_ptr; + using boost::scoped_ptr; + + class DefsRegistry; /** @@ -48,8 +53,10 @@ namespace mobject * will be created on demand, and any default configuration, once * found, will be remembered and stored with the current session. */ - class DefsManager + class DefsManager : private boost::noncopyable { + scoped_ptr defsRegistry; + protected: DefsManager () throw(); @@ -64,6 +71,7 @@ namespace mobject template shared_ptr operator() (const lumiera::Query&); + /** search through the registered defaults, never create anything. * @return object fulfilling the query, \c empty ptr if not found. */ diff --git a/src/proc/mobject/session/defsregistry.cpp b/src/proc/mobject/session/defsregistry.cpp deleted file mode 100644 index 4eea8df6c..000000000 --- a/src/proc/mobject/session/defsregistry.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - DefsManager - 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. - -* *****************************************************/ - - -#include "proc/mobject/session/defsregistry.hpp" -#include "common/error.hpp" - -//#include - -//using boost::format; - - - -namespace mobject - { - namespace session - { - - /** @todo */ - DefsRegistry::DefsRegistry () - { - UNIMPLEMENTED ("initialize store for default objects"); - } - - - } // namespace mobject::session - -} // namespace mobject - diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index e6f102738..d52867f42 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -25,10 +25,14 @@ #define MOBJECT_SESSION_DEFSREGISTRY_H -#include "common/singleton.hpp" +#include "common/multithread.hpp" +#include "common/query.hpp" +#include "common/util.hpp" +#include +#include #include - +#include namespace mobject @@ -36,27 +40,254 @@ namespace mobject namespace session { using std::tr1::shared_ptr; + using std::tr1::weak_ptr; + using lumiera::Thread; + using lumiera::Query; + + + namespace // Implementation details + { + struct TableEntry + { + virtual ~TableEntry() {}; + }; + + /** we maintain an independent defaults registry + * for every participating kind of objects */ + typedef std::vector > Table; + + uint maxSlots (0); ///< number of different registered Types + + + /** holding a single "default object" entry */ + template + struct Record + { + uint degree; + Query query; + weak_ptr objRef; + + Record (const Query& q, const shared_ptr& obj) + : degree (lumiera::query::countPraed (q)), + query (q), + objRef (obj) + { } + + struct Search ///< Functor searching for a specific object + { + Search (const shared_ptr& obj) + : obj_(obj) { } + + const shared_ptr& obj_; + + bool + operator() (const Record& rec) + { + shared_ptr storedObj (rec.objRef.lock()); + return storedObj && pAsset(storedObj)==pAsset(obj_); //////////////TODO: not only Assets (i.e. define comparison Operators on Assets!) + } + }; + + 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) + ); + } + }; + }; + + /** 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); + + } + /** - * Organize a collection of preconfigured default objects. - * For various kinds of objects we can tweek the default parametrisation - * as part of the general session configuration. A ref to an instance of - * this class is accessible through the current session and can be used - * to fill in parts of the configuration of new objects, if the user - * code didn't give more specific parameters. Necessary sub-objects - * will be created on demand, and any default configuration, once - * found, will be remembered and stored with the current session. + * @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 + class DefsRegistry : private boost::noncopyable { + Table table_; + protected: - DefsRegistry (); - friend class lumiera::singleton::StaticCreate; + DefsRegistry () {}; + friend class DefsManager; public: + /** used for enumerating solutions */ + template + class Iter + { + friend class DefsRegistry; + typedef typename Slot::Registry::iterator II; + + II p,i,e; + shared_ptr 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 + ptr = findNext(); + } + + Iter (II match, II from, II to) ///< returns direct match first, then ennumerates + : p(match), i(from), e(to), + next(), ptr(findNext()) + { } + + shared_ptr findNext () throw() + { + while (!next) + { + if (p==e) break; + next = p->objRef.lock(); + p = i++; + } + return next; + } + + + public: + shared_ptr operator* () { return ptr; } + bool hasNext () { return next || findNext(); } + 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) + { + shared_ptr 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 (shared_ptr& 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) + { + shared_ptr storedObj (pos->objRef.lock()); + if (storedObj) + return (pAsset(storedObj) == pAsset(obj)); //////////////TODO: not only Assets (i.e. define comparison Operators on Assets!) + else + // use the opportunity and purge the expired entry + registry.erase(pos); + } + // no existing entry.... + registry.insert(pos, entry); + 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 shared_ptr& obj) + { + typedef typename Slot::Registry Registry; + typedef typename Record::Search SearchFunc; + + Registry& registry = Slot::access(table_); + return util::remove_if(registry, SearchFunc (obj)); + } }; diff --git a/src/proc/mobject/session/session.cpp b/src/proc/mobject/session/session.cpp index 3544ed4a0..5c4bb8592 100644 --- a/src/proc/mobject/session/session.cpp +++ b/src/proc/mobject/session/session.cpp @@ -34,6 +34,7 @@ #include "proc/mobject/session.hpp" #include "proc/mobject/session/sessionimpl.hpp" #include "proc/mobject/session/defsmanager.hpp" +#include "proc/mobject/session/defsregistry.hpp" #include "common/singleton.hpp" diff --git a/src/proc/mobject/session/sessmanagerimpl.cpp b/src/proc/mobject/session/sessmanagerimpl.cpp index 6b5749463..8195e4fbe 100644 --- a/src/proc/mobject/session/sessmanagerimpl.cpp +++ b/src/proc/mobject/session/sessmanagerimpl.cpp @@ -39,6 +39,7 @@ #include "proc/mobject/session.hpp" #include "proc/mobject/session/sessionimpl.hpp" #include "proc/mobject/session/defsmanager.hpp" +#include "proc/mobject/session/defsregistry.hpp" #include "common/error.hpp" using boost::scoped_ptr; diff --git a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp index a4c18f2d9..fff9e6966 100644 --- a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp +++ b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp @@ -120,7 +120,8 @@ namespace asset // issue a ConfigQuery directly, without involving the DefaultsManager QueryHandler& typeHandler = ConfigRules::instance(); - PPipe pipe1 = typeHandler.resolve (query_for_streamID); + PPipe pipe1; + typeHandler.resolve (pipe1, query_for_streamID); ASSERT (pipe1); ASSERT (!find (pipe1->getPipeID())); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 80a57a86b..5f5dbcd9d 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -865,8 +865,8 @@ Taken precisely, the "degree of constriction" yields only a partial or As we don't have a Prolog interpreter on board yet, we utilize a mock store with preconfigured answers. (see {{{MockConfigQuery}}}). As this preliminary solution is lacking the ability to create new objects, we need to resort to some trickery here (please look away). The overall logic is quite broken, because the system isn't capable to do any real resolution &mdash; if we ignore this fact, the rest of the algorithm can be implemented, tested and used right now. -
-
For several components and properties there is an implicit default value or configuration; it is stored alongside with the session. The intention is that defaults never create an error, instead, they are to be extended silently on demand. Objects configured according to this defaults can be retrieved at the [[Session]] interface by a set of overloaded functions {{{Session::current->default(Query<TYPE> ("query string"))}}}, where the //query string // defines a capability query similar to what is employed for pipes, stream types, codecs etc. The Queries are implemented by [[configuration rules|ConfigRules]]
+
+
For several components and properties there is an implicit default value or configuration; it is stored alongside with the session. The intention is that defaults never create an error, instead, they are to be extended silently on demand. Objects configured according to this defaults can be retrieved at the [[Session]] interface by a set of overloaded functions {{{Session::current->default(Query<TYPE> ("query string"))}}}, where the //query string // defines a capability query similar to what is employed for pipes, stream types, codecs etc. This query mechanism is implemented by [[configuration rules|ConfigRules]]
 
 !!!!what is denoted by {{{default}}}?
 {{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding a exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one soltution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances.