lumiera_/src/proc/mobject/session/defsregistry.hpp

347 lines
11 KiB
C++
Raw Normal View History

2008-02-29 18:58:29 +01:00
/*
DEFSREGISTRY.hpp - implementation of the default object store
Copyright (C) Lumiera.org
2008-02-29 18:58:29 +01:00
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.
*/
/** @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
**
*/
2008-02-29 18:58:29 +01:00
#ifndef MOBJECT_SESSION_DEFSREGISTRY_H
#define MOBJECT_SESSION_DEFSREGISTRY_H
#include "lib/sync-classlock.hpp"
#include "lib/query.hpp"
#include "lib/util.hpp"
#include "lib/p.hpp"
2008-02-29 18:58:29 +01:00
#include <set>
#include <vector>
2008-02-29 18:58:29 +01:00
#include <tr1/memory>
#include <boost/format.hpp>
#include <boost/utility.hpp>
#include <boost/lambda/lambda.hpp>
2008-02-29 18:58:29 +01:00
namespace mobject {
namespace session {
using lumiera::P;
using lumiera::Query;
using lib::ClassLock;
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<TableEntry> > Table;
/**
* 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 P<TAR>& obj)
: degree (lumiera::query::countPraed (q)),
query (q),
objRef (obj)
{ }
struct Search ///< Functor searching for a specific object
{
Search (const P<TAR>& obj)
: obj_(obj) { }
const P<TAR>& obj_;
bool
operator() (const Record& rec)
{
P<TAR> 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<TAR> 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<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)
{
ClassLock<TableEntry> guard();
if (!index)
index = ++maxSlots;
if (index > table.size())
table.resize (index);
table[index-1].reset(new Slot);
}
};
2008-12-03 06:02:54 +01:00
// static vars to organise one Table Slot per type....
template<class TAR>
size_t Slot<TAR>::index (0);
2008-02-29 18:58:29 +01:00
/**
* @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.
2008-02-29 18:58:29 +01:00
*/
class DefsRegistry : private boost::noncopyable
2008-02-29 18:58:29 +01:00
{
Table table_;
2008-02-29 18:58:29 +01:00
public:
/** used for enumerating solutions */
template<class TAR>
class Iter
{
friend class DefsRegistry;
typedef typename Slot<TAR>::Registry::iterator II;
II p,i,e;
P<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
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<TAR> findNext () throw()
{
while (!next)
{
if (p==e) break;
next = p->objRef.lock();
p = i++;
}
return next;
}
public:
P<TAR> 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<class TAR>
Iter<TAR> candidates (const Query<TAR>& query)
{
P<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 (P<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)
{
P<TAR> 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;
}
2008-02-29 18:58:29 +01:00
/** 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 P<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));
}
/** helper for diagnostics.
* @note to use it, your objects need an operator string()
*/
template<class TAR>
string dump()
{
string res;
util::for_each ( Slot<TAR>::access(table_)
, var(res) += _1
);
return res;
}
2008-02-29 18:58:29 +01:00
};
} // (End) impl namespace
using impl::DefsRegistry;
2008-02-29 18:58:29 +01:00
} // namespace mobject::session
} // namespace mobject
#endif