WIP code for handling registration of defaults objects.

Missing some TODOs and test coverage
This commit is contained in:
Fischlurch 2008-03-31 03:21:28 +02:00
parent c4128c9816
commit 6596699f6e
16 changed files with 350 additions and 92 deletions

View file

@ -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> Appconfig::theApp_;
#ifndef LUMIERA_VERSION
#define LUMIERA_VERSION 3++devel

View file

@ -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<TY> solution, const Query<TY>& q) = 0;
virtual bool resolve (shared_ptr<TY>& solution, const Query<TY>& q) = 0;
};
// TODO: the Idea is to provide specialisations for the concrete types

View file

@ -27,6 +27,7 @@
#define LUMIERA_MULTITHREAD_H
#include "nobugcfg.h"
#include "common/util.hpp"
namespace lumiera

View file

@ -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<Symbol, regex> 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

View file

@ -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

View file

@ -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<const Ret&> (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
}

View file

@ -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<class SET, typename PRD>
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..
*/

View file

@ -45,13 +45,13 @@
#include <boost/scoped_ptr.hpp>
#include <string>
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;

View file

@ -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<TAR>
DefsManager::search (const Query<TAR>& capabilities)
{
defaultsRegistry.get (capabilities);
shared_ptr<TAR> res;
QueryHandler<TAR>& typeHandler = ConfigRules::instance();
for (DefsRegistry::Iter<TAR> i = defsRegistry->candidates(capabilities);
i.hasNext(); ++i )
{
shared_ptr<TAR> res (*i);
typeHandler.resolve (res, capabilities);
if (res)
return res;
}
return res; // "no solution found"
}
@ -71,7 +77,7 @@ namespace mobject
QueryHandler<TAR>& 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<TAR>& 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 */
/***************************************************************/

View file

@ -27,6 +27,8 @@
#include "common/query.hpp"
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
#include <tr1/memory>
@ -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> defsRegistry;
protected:
DefsManager () throw();
@ -64,6 +71,7 @@ namespace mobject
template<class TAR>
shared_ptr<TAR> operator() (const lumiera::Query<TAR>&);
/** search through the registered defaults, never create anything.
* @return object fulfilling the query, \c empty ptr if not found.
*/

View file

@ -1,48 +0,0 @@
/*
DefsManager - implementation of the default object store
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
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 <boost/format.hpp>
//using boost::format;
namespace mobject
{
namespace session
{
/** @todo */
DefsRegistry::DefsRegistry ()
{
UNIMPLEMENTED ("initialize store for default objects");
}
} // namespace mobject::session
} // namespace mobject

View file

@ -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 <set>
#include <vector>
#include <tr1/memory>
#include <boost/utility.hpp>
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<shared_ptr<TableEntry> > Table;
uint maxSlots (0); ///< number of different registered Types
/** holding a single "default object" entry */
template<class TAR>
struct Record
{
uint degree;
Query<TAR> query;
weak_ptr<TAR> objRef;
Record (const Query<TAR>& q, const shared_ptr<TAR>& obj)
: degree (lumiera::query::countPraed (q)),
query (q),
objRef (obj)
{ }
struct Search ///< Functor searching for a specific object
{
Search (const shared_ptr<TAR>& obj)
: obj_(obj) { }
const shared_ptr<TAR>& obj_;
bool
operator() (const Record& rec)
{
shared_ptr<TAR> 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<class TAR>
struct Slot : public TableEntry
{
typedef typename Record<TAR>::OrderRelation Ordering;
typedef std::set<Record<TAR>, 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<Slot*> (table[index-1].get());
return item->registry;
}
private:
static void
createSlot (Table& table)
{
Thread::Lock<TableEntry> 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<class TAR>
size_t Slot<TAR>::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<SessManagerImpl>;
DefsRegistry () {};
friend class DefsManager;
public:
/** used for enumerating solutions */
template<class TAR>
class Iter
{
friend class DefsRegistry;
typedef typename Slot<TAR>::Registry::iterator II;
II p,i,e;
shared_ptr<TAR> 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<TAR> findNext () throw()
{
while (!next)
{
if (p==e) break;
next = p->objRef.lock();
p = i++;
}
return next;
}
public:
shared_ptr<TAR> 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<class TAR>
Iter<TAR> candidates (const Query<TAR>& query)
{
shared_ptr<TAR> dummy;
Record<TAR> entry (query, dummy);
typedef typename Slot<TAR>::Registry Registry;
Registry& registry = Slot<TAR>::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<TAR> (registry.begin(), end); // just ennumerate contents
else
return Iter<TAR> (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<class TAR>
bool put (shared_ptr<TAR>& obj, const Query<TAR>& query)
{
Record<TAR> entry (query, obj);
typedef typename Slot<TAR>::Registry Registry;
typedef typename Registry::iterator RIter;
Registry& registry = Slot<TAR>::access(table_);
RIter pos = registry.lower_bound (entry);
if ( pos!=registry.end()
&& pos->query == query)
{
shared_ptr<TAR> 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<class TAR>
bool forget (const shared_ptr<TAR>& obj)
{
typedef typename Slot<TAR>::Registry Registry;
typedef typename Record<TAR>::Search SearchFunc;
Registry& registry = Slot<TAR>::access(table_);
return util::remove_if(registry, SearchFunc (obj));
}
};

View file

@ -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"

View file

@ -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;

View file

@ -120,7 +120,8 @@ namespace asset
// issue a ConfigQuery directly, without involving the DefaultsManager
QueryHandler<Pipe>& typeHandler = ConfigRules::instance();
PPipe pipe1 = typeHandler.resolve (query_for_streamID);
PPipe pipe1;
typeHandler.resolve (pipe1, query_for_streamID);
ASSERT (pipe1);
ASSERT (!find (pipe1->getPipeID()));

View file

@ -865,8 +865,8 @@ Taken precisely, the &quot;degree of constriction&quot; 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 &amp;mdash; if we ignore this fact, the rest of the algorithm can be implemented, tested and used right now.
</pre>
</div>
<div title="DefaultsManagement" modifier="Ichthyostega" modified="200802200023" created="200801121708" tags="def spec" changecount="11">
<pre>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-&gt;default(Query&lt;TYPE&gt; (&quot;query string&quot;))}}}, 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]]
<div title="DefaultsManagement" modifier="Ichthyostega" modified="200803212244" created="200801121708" tags="def spec" changecount="12">
<pre>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-&gt;default(Query&lt;TYPE&gt; (&quot;query string&quot;))}}}, 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//&amp;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.