62 #include <boost/functional/hash.hpp> 63 #include <unordered_map> 70 LUMIERA_ERROR_DEFINE (NOT_IN_SESSION,
"referring to a Placement not known to the current session");
71 LUMIERA_ERROR_DEFINE (PLACEMENT_TYPE,
"requested Placement (pointee) type not compatible with data or context");
81 using std::unordered_map;
82 using std::unordered_multimap;
89 using std::placeholders::_1;
103 typedef PlacementIndex::PRef PRef;
104 typedef PlacementIndex::ID ID;
125 typedef PlacementMO::ID PID;
126 typedef unordered_map<PID, PlacementEntry> IDTable;
127 typedef std::unordered_multimap<PID,PID> ScopeTable;
129 typedef pair<ScopeIter, ScopeIter> ScopeContents;
133 IDTable placementTab_;
134 ScopeTable scopeTab_;
149 WARN (
session,
"Problem while purging PlacementIndex: %s", err.
what());
157 return placementTab_.size();
163 return scopeTab_.size();
169 return allocator_.numSlots<PlacementMO>();
173 contains (ID
id)
const 175 return util::contains(placementTab_,
id);
182 REQUIRE (contains (
id));
183 PPlacement
const&
entry = base_entry(
id).element;
186 ENSURE (
id == entry->getID());
191 fetchScope (ID
id)
const 193 REQUIRE (contains (
id));
194 PPlacement
const& scope = base_entry(
id).scope;
197 ENSURE (contains (scope->getID()));
202 queryScopeContents (ID
id)
const 204 REQUIRE (contains (
id));
205 ScopeContents
contents = scopeTab_.equal_range (
id);
207 ,scopeIndexElementsResolver() );
215 INFO (
session,
"Purging Placement Tables...");
217 placementTab_.clear();
230 REQUIRE (0 == placementTab_.size());
231 REQUIRE (0 == scopeTab_.size());
233 root_ = allocator_.create<PlacementMO> (rootDef);
234 ID rootID = root_->getID();
235 placementTab_[rootID].element = root_;
236 placementTab_[rootID].scope = root_;
238 ENSURE (contains (rootID));
239 ENSURE (scopeTab_.empty());
240 ENSURE (1 == size());
247 REQUIRE (0 < size());
248 REQUIRE (contains (root_->getID()));
265 REQUIRE (contains (scopeID));
267 PPlacement newEntry = allocator_.create<PlacementMO> (newObj);
268 ID
newID = newEntry->getID();
270 ASSERT (newID,
"invalid");
271 ASSERT (!contains (newID));
272 placementTab_[
newID].element = newEntry;
273 placementTab_[
newID].scope = placementTab_[scopeID].element;
274 scopeTab_.insert (make_pair (scopeID, newID));
283 ENSURE (!util::contains(scopeTab_,
id));
287 if (util::contains(scopeTab_,
id))
288 throw error::State{
"Unable to remove the specified Placement, " 289 "because it defines an non-empty scope. " 290 "You need to delete any contents first." 291 , LERR_(NONEMPTY_SCOPE)};
293 ASSERT (contains (
id));
295 remove_from_scope (toRemove.scope->getID(), id);
296 ENSURE (!util::contains(scopeTab_,
id));
297 ENSURE (!contains (
id));
302 removeAll (ID scopeID)
304 remove_all_from_scope (scopeID);
305 removeEntry (scopeID);
307 ENSURE (!util::contains(scopeTab_, scopeID));
308 ENSURE (!contains (scopeID));
316 PlacementMO* _root_4check () {
return root_.get(); }
317 PlacementMO* _element_4check (ID
id){
return base_entry(
id).element.get();}
318 PlacementMO* _scope_4check (ID
id) {
return base_entry(
id).scope.get(); }
319 IDIter _eachEntry_4check () {
return eachMapKey (placementTab_); }
320 IDIter _eachScope_4check () {
return eachDistinctKey (scopeTab_); }
321 IDIter _contents_4check(ID
id){
return eachValForKey (scopeTab_,
id);}
325 typedef IDTable::const_iterator Slot;
328 base_entry (ID key)
const 330 Slot pos = placementTab_.find (key);
331 if (pos == placementTab_.end())
332 throw error::Logic(
"lost a Placement expected to be registered within PlacementIndex.");
338 remove_base_entry (ID key)
340 Slot pos = placementTab_.find (key);
341 REQUIRE (pos != placementTab_.end());
343 placementTab_.erase(pos);
348 remove_from_scope (ID scopeID, ID entryID)
350 typedef ScopeTable::const_iterator Pos;
351 pair<Pos,Pos> searchRange = scopeTab_.equal_range(scopeID);
353 Pos pos = searchRange.first;
354 Pos end = searchRange.second;
355 for ( ; pos!=end; ++pos)
356 if (pos->second == entryID)
358 scopeTab_.erase(pos);
366 remove_all_from_scope (ID scopeID)
368 typedef ScopeTable::const_iterator Pos;
369 pair<Pos,Pos> scopeEntries = scopeTab_.equal_range(scopeID);
370 Pos first = scopeEntries.first;
371 Pos end = scopeEntries.second;
375 ChildIDs child (eachVal(first,end));
377 scopeTab_.erase (first,end);
379 for ( ; child; ++child )
381 remove_all_from_scope (*child);
382 remove_base_entry (*child);
384 ENSURE (!util::contains(scopeTab_, *child));
385 ENSURE (!contains (*child));
401 ID elemID (entry.second);
402 ASSERT (contains (elemID));
403 return fetch (elemID);
407 typedef function<PlacementMO& (pair<PID,PID>
const&)> ElementResolver;
408 mutable ElementResolver elementResolver_;
413 if (!elementResolver_)
414 elementResolver_ = bind (&Table::resolveScopeIndexElement,
this, _1 );
415 return elementResolver_;
429 PlacementIndex::PlacementIndex (PlacementMO
const& rootDef)
432 INFO (
session,
"Initialising PlacementIndex...");
434 pTab_->setupRoot(rootDef);
438 PlacementIndex::~PlacementIndex() { }
442 PlacementIndex::getRoot()
const 444 return pTab_->getRootElement();
449 PlacementIndex::size()
const 451 ASSERT (0 < pTab_->size());
452 return pTab_->size() - 1;
457 PlacementIndex::contains (ID
id)
const 459 return pTab_->contains (
id);
464 PlacementIndex::find (ID
id)
const 466 __check_knownID(*
this,
id);
467 return pTab_->fetch (
id);
478 PlacementIndex::getScope (ID
id)
const 480 __check_knownID(*
this,
id);
481 return pTab_->fetchScope (
id);
493 PlacementIndex::getReferrers (ID
id)
const 495 __check_knownID(*
this,
id);
496 return pTab_->queryScopeContents(
id);
513 PlacementIndex::insert (PlacementMO
const& newObj, ID targetScope)
515 if (!contains (targetScope))
516 throw error::Logic (
"Specified a non-registered Placement as scope " 517 "while adding another Placement to the index" 518 ,LERR_(INVALID_SCOPE));
520 return pTab_->addEntry(newObj, targetScope);
530 PlacementIndex::remove (ID
id)
532 if (
id == getRoot().getID())
535 return pTab_->removeEntry (
id);
545 PlacementIndex::clear (ID targetScope)
547 if (targetScope == getRoot().getID())
550 pTab_->removeAll (targetScope);
557 PlacementIndex::clear()
576 :
error::Fatal (
string(
"Failed test: ")+currentTest+
" : "+failure
577 ,LUMIERA_ERROR_INDEX_CORRUPTED)
594 PlacementMO* elm (ID
id) {
return tab._element_4check (
id);}
595 PlacementMO* sco (ID
id) {
return tab._scope_4check (
id); }
599 #define VERIFY(_CHECK_, CHECK_ID, DESCRIPTION) \ 601 throw SelfCheckFailure (CHECK_ID, (DESCRIPTION)); 605 checkRoot (
PMO* root)
607 VERIFY ( root,
"(0.1) Basics",
"Root element missing");
608 VERIFY ( root->
isValid(),
"(0.2) Basics",
"Root Placement invalid");
609 VERIFY ( (*root)->isValid(),
"(0.3) Basics",
"Root MObject self-check failure");
615 VERIFY ( tab.contains(
id),
"(1.1) Elements",
"PlacementIndex main table corrupted");
616 VERIFY ( elm(
id),
"(1.2) Elements",
"Entry doesn't hold a Placement");
617 VERIFY (
id==elm(
id)->getID(),
"(1.3) Elements",
"Element stored with wrong ID");
618 VERIFY ( elm(
id)->isValid(),
"(1.4) Elements",
"Index contains invalid Placement")
619 VERIFY ( sco(
id),
"(1.5) Elements",
"Entry has undefined scope");
620 VERIFY ( sco(
id)->isValid(),
"(1.6) Elements",
"Entry has invalid scope");
621 VERIFY ( tab.contains (sco(
id)->getID()),
622 "(1.7) Elements",
"Element associated with an unknown scope");
624 PMO& theElement = *elm(
id);
625 ID theScope (sco(
id)->getID());
627 && elm(
id)==tab._root_4check()
631 iterator elementsInScope = tab.queryScopeContents(theScope);
632 auto equalsTheElement = [&](
PMO&
entry) {
return entry == theElement; };
633 bool properlyRegistered = has_any (elementsInScope, equalsTheElement);
635 VERIFY ( properlyRegistered,
"(1.8) Elements",
"Element not registered as member of the enclosing scope: "+ theElement);
641 VERIFY ( tab.contains(
id),
"(2.1) Scopes",
"Scope not registered in main table");
642 VERIFY ( elm(
id),
"(2.2) Scopes",
"Scope entry doesn't hold a Placement");
643 VERIFY ( sco(
id),
"(2.3) Scopes",
"Scope entry doesn't hold a containing Scope");
645 PMO* root = tab._root_4check();
646 PMO* scope = sco(
id);
647 while (scope && scope != sco(scope->getID()))
648 scope = sco(scope->getID());
650 VERIFY ( root==scope,
"(2.4) Scopes",
"Found a scope not attached below root.");
652 for_each ( tab._contents_4check(
id), &Validator::checkScopeEntry,
this, id, _1 );
656 checkScopeEntry (ID scope, ID
entry)
658 VERIFY ( tab.contains(entry),
"(2.5) Scopes",
"Scope member not registered in main table");
659 VERIFY ( elm(entry),
"(2.6) Scopes",
"Scope member entry doesn't refer to a valid Placement");
660 VERIFY ( sco(entry),
"(2.7) Scopes",
"Scope member entry is lacking valid scope information");
661 VERIFY ( sco(entry)->getID() == scope,
662 "(2.8) Scopes",
"Scope member registered as belonging to a different scope in main table");
668 VERIFY ( 0 < tab.
size(),
"(4.1) Storage",
"Implementation table is empty");
669 VERIFY ( 0 < tab.element_cnt(),
"(4.2) Storage",
"No Placement instances allocated");
671 "(4.3) Storage",
"Number of elements and scope entries disagree");
672 VERIFY ( tab.
size()==tab.element_cnt(),
673 "(4.4) Storage",
"Number of entries doesn't match number of allocated Placement instances");
681 checkRoot (tab._root_4check());
683 for_each ( tab._eachEntry_4check(), &Validator::checkEntry,
this, _1 );
684 for_each ( tab._eachScope_4check(), &Validator::checkScope,
this, _1 );
706 PlacementIndex::isValid()
const 710 VERIFY ( pTab_,
"(0) Basics" ,
"Implementation tables not initialised");
716 catch(SelfCheckFailure& failure)
719 ERROR (
session,
"%s", failure.what());
ElementResolver scopeIndexElementsResolver() const
<
_MapT< MAP >::ValIter eachValForKey(MAP &map, typename _MapT< MAP >::Key key)
Abstract foundation for building custom allocation managers.
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
inline string literal This is a marker type to indicate that
virtual CStr what() const noexcept override
std::exception interface : yield a diagnostic message
This header is for including and configuring NoBug.
Steam-Layer implementation namespace root.
Namespace of Session and user visible high-level objects.
bool has_any(CON const &elements, FUN function, P1 &&bind1, ARGS &&...args)
Accept binding for arbitrary function arguments.
Derived specific exceptions within Lumiera's exception hierarchy.
PlacementMO & resolveScopeIndexElement(pair< PID, PID > const &entry) const
Helper for building a scope exploring iterator for PlacementIndex: our "reverse index" (scopeTab_) tr...
Core of the session implementation datastructure.
Placement< MObject > PlacementMO
lumiera_err lumiera_error(void)
Get and clear current error state.
void for_each(CON const &elements, FUN function, P1 &&bind1, ARGS &&...args)
Accept binding for arbitrary function arguments.
_MapT< MAP >::KeyIter eachMapKey(MAP &map)
size_t scope_cnt() const
<
Foundation for a custom allocation manager, tracking the created objects by smart-ptrs.
_MapT< MAP >::KeyIter eachDistinctKey(MAP &map)
PlacementIndex self-verification code Executes all built-in checks automatically on object creation...
Lumiera public interface.
Session and SessionServices Implementation classes.
Accessing a STL element range through a Lumiera forward iterator, An instance of this iterator adapte...
virtual bool isValid() const =0
MObject self-test (usable for asserting)
materialised iterator contents.
Storage and implementation backing the PlacementIndex.
Preconfigured adapters for some STL container standard usage situations.
ID addEntry(PlacementMO const &newObj, ID scopeID)
Store a copy of the given Placement as new instance within the index, together with the Scope this Pl...
void setupRoot(PlacementMO const &rootDef)
insert a specially configured root entry into the yet empty table.
Extension module to build an opaque data source, accessible as Lumiera Forward Iterator.
Perform operations "for each element" of a collection.
string contents(Rec const &object)
render the child elements as string data for test/verification
Interface and Base definition for all Lumiera Exceptions.
string newID(Symbol prefix)
create a random new ID
_MapIterT< IT >::ValIter eachVal(IT const &begin, IT const &end)
#define LUMIERA_ERROR_DEFINE(err, msg)
Definition and initialisation of an error constant.