53 #include <boost/functional/hash.hpp> 54 #include <unordered_map> 61 LUMIERA_ERROR_DEFINE (NOT_IN_SESSION,
"referring to a Placement not known to the current session");
62 LUMIERA_ERROR_DEFINE (PLACEMENT_TYPE,
"requested Placement (pointee) type not compatible with data or context");
72 using std::unordered_map;
73 using std::unordered_multimap;
80 using std::placeholders::_1;
94 typedef PlacementIndex::PRef PRef;
95 typedef PlacementIndex::ID ID;
116 typedef PlacementMO::ID PID;
117 typedef unordered_map<PID, PlacementEntry> IDTable;
118 typedef std::unordered_multimap<PID,PID> ScopeTable;
120 typedef pair<ScopeIter, ScopeIter> ScopeContents;
124 IDTable placementTab_;
125 ScopeTable scopeTab_;
140 WARN (
session,
"Problem while purging PlacementIndex: %s", err.
what());
148 return placementTab_.size();
154 return scopeTab_.size();
160 return allocator_.numSlots<PlacementMO>();
164 contains (ID
id)
const 166 return util::contains(placementTab_,
id);
173 REQUIRE (contains (
id));
174 PPlacement
const&
entry = base_entry(
id).element;
177 ENSURE (
id == entry->getID());
182 fetchScope (ID
id)
const 184 REQUIRE (contains (
id));
185 PPlacement
const& scope = base_entry(
id).scope;
188 ENSURE (contains (scope->getID()));
193 queryScopeContents (ID
id)
const 195 REQUIRE (contains (
id));
196 ScopeContents
contents = scopeTab_.equal_range (
id);
198 ,scopeIndexElementsResolver() );
206 INFO (
session,
"Purging Placement Tables...");
208 placementTab_.clear();
221 REQUIRE (0 == placementTab_.size());
222 REQUIRE (0 == scopeTab_.size());
224 root_ = allocator_.create<PlacementMO> (rootDef);
225 ID rootID = root_->getID();
226 placementTab_[rootID].element = root_;
227 placementTab_[rootID].scope = root_;
229 ENSURE (contains (rootID));
230 ENSURE (scopeTab_.empty());
231 ENSURE (1 == size());
238 REQUIRE (0 < size());
239 REQUIRE (contains (root_->getID()));
256 REQUIRE (contains (scopeID));
258 PPlacement newEntry = allocator_.create<PlacementMO> (newObj);
259 ID
newID = newEntry->getID();
261 ASSERT (newID,
"invalid");
262 ASSERT (!contains (newID));
263 placementTab_[
newID].element = newEntry;
264 placementTab_[
newID].scope = placementTab_[scopeID].element;
265 scopeTab_.insert (make_pair (scopeID, newID));
274 ENSURE (!util::contains(scopeTab_,
id));
278 if (util::contains(scopeTab_,
id))
279 throw error::State{
"Unable to remove the specified Placement, " 280 "because it defines an non-empty scope. " 281 "You need to delete any contents first." 282 , LERR_(NONEMPTY_SCOPE)};
284 ASSERT (contains (
id));
286 remove_from_scope (toRemove.scope->getID(), id);
287 ENSURE (!util::contains(scopeTab_,
id));
288 ENSURE (!contains (
id));
293 removeAll (ID scopeID)
295 remove_all_from_scope (scopeID);
296 removeEntry (scopeID);
298 ENSURE (!util::contains(scopeTab_, scopeID));
299 ENSURE (!contains (scopeID));
307 PlacementMO* _root_4check () {
return root_.get(); }
308 PlacementMO* _element_4check (ID
id){
return base_entry(
id).element.get();}
309 PlacementMO* _scope_4check (ID
id) {
return base_entry(
id).scope.get(); }
310 IDIter _eachEntry_4check () {
return eachMapKey (placementTab_); }
311 IDIter _eachScope_4check () {
return eachDistinctKey (scopeTab_); }
312 IDIter _contents_4check(ID
id){
return eachValForKey (scopeTab_,
id);}
316 typedef IDTable::const_iterator Slot;
319 base_entry (ID key)
const 321 Slot pos = placementTab_.find (key);
322 if (pos == placementTab_.end())
323 throw error::Logic(
"lost a Placement expected to be registered within PlacementIndex.");
329 remove_base_entry (ID key)
331 Slot pos = placementTab_.find (key);
332 REQUIRE (pos != placementTab_.end());
334 placementTab_.erase(pos);
339 remove_from_scope (ID scopeID, ID entryID)
341 typedef ScopeTable::const_iterator Pos;
342 pair<Pos,Pos> searchRange = scopeTab_.equal_range(scopeID);
344 Pos pos = searchRange.first;
345 Pos end = searchRange.second;
346 for ( ; pos!=end; ++pos)
347 if (pos->second == entryID)
349 scopeTab_.erase(pos);
357 remove_all_from_scope (ID scopeID)
359 typedef ScopeTable::const_iterator Pos;
360 pair<Pos,Pos> scopeEntries = scopeTab_.equal_range(scopeID);
361 Pos first = scopeEntries.first;
362 Pos end = scopeEntries.second;
366 ChildIDs child (eachVal(first,end));
368 scopeTab_.erase (first,end);
370 for ( ; child; ++child )
372 remove_all_from_scope (*child);
373 remove_base_entry (*child);
375 ENSURE (!util::contains(scopeTab_, *child));
376 ENSURE (!contains (*child));
392 ID elemID (entry.second);
393 ASSERT (contains (elemID));
394 return fetch (elemID);
398 typedef function<PlacementMO& (pair<PID,PID>
const&)> ElementResolver;
399 mutable ElementResolver elementResolver_;
404 if (!elementResolver_)
405 elementResolver_ = bind (&Table::resolveScopeIndexElement,
this, _1 );
406 return elementResolver_;
420 PlacementIndex::PlacementIndex (PlacementMO
const& rootDef)
423 INFO (
session,
"Initialising PlacementIndex...");
425 pTab_->setupRoot(rootDef);
429 PlacementIndex::~PlacementIndex() { }
433 PlacementIndex::getRoot()
const 435 return pTab_->getRootElement();
440 PlacementIndex::size()
const 442 ASSERT (0 < pTab_->size());
443 return pTab_->size() - 1;
448 PlacementIndex::contains (ID
id)
const 450 return pTab_->contains (
id);
455 PlacementIndex::find (ID
id)
const 457 __check_knownID(*
this,
id);
458 return pTab_->fetch (
id);
469 PlacementIndex::getScope (ID
id)
const 471 __check_knownID(*
this,
id);
472 return pTab_->fetchScope (
id);
484 PlacementIndex::getReferrers (ID
id)
const 486 __check_knownID(*
this,
id);
487 return pTab_->queryScopeContents(
id);
504 PlacementIndex::insert (PlacementMO
const& newObj, ID targetScope)
506 if (!contains (targetScope))
507 throw error::Logic (
"Specified a non-registered Placement as scope " 508 "while adding another Placement to the index" 509 ,LERR_(INVALID_SCOPE));
511 return pTab_->addEntry(newObj, targetScope);
521 PlacementIndex::remove (ID
id)
523 if (
id == getRoot().getID())
526 return pTab_->removeEntry (
id);
536 PlacementIndex::clear (ID targetScope)
538 if (targetScope == getRoot().getID())
541 pTab_->removeAll (targetScope);
548 PlacementIndex::clear()
567 :
error::Fatal (
string(
"Failed test: ")+currentTest+
" : "+failure
568 ,LUMIERA_ERROR_INDEX_CORRUPTED)
585 PlacementMO* elm (ID
id) {
return tab._element_4check (
id);}
586 PlacementMO* sco (ID
id) {
return tab._scope_4check (
id); }
590 #define VERIFY(_CHECK_, CHECK_ID, DESCRIPTION) \ 592 throw SelfCheckFailure (CHECK_ID, (DESCRIPTION)); 596 checkRoot (
PMO* root)
598 VERIFY ( root,
"(0.1) Basics",
"Root element missing");
599 VERIFY ( root->
isValid(),
"(0.2) Basics",
"Root Placement invalid");
600 VERIFY ( (*root)->isValid(),
"(0.3) Basics",
"Root MObject self-check failure");
606 VERIFY ( tab.contains(
id),
"(1.1) Elements",
"PlacementIndex main table corrupted");
607 VERIFY ( elm(
id),
"(1.2) Elements",
"Entry doesn't hold a Placement");
608 VERIFY (
id==elm(
id)->getID(),
"(1.3) Elements",
"Element stored with wrong ID");
609 VERIFY ( elm(
id)->isValid(),
"(1.4) Elements",
"Index contains invalid Placement")
610 VERIFY ( sco(
id),
"(1.5) Elements",
"Entry has undefined scope");
611 VERIFY ( sco(
id)->isValid(),
"(1.6) Elements",
"Entry has invalid scope");
612 VERIFY ( tab.contains (sco(
id)->getID()),
613 "(1.7) Elements",
"Element associated with an unknown scope");
615 PMO& theElement = *elm(
id);
616 ID theScope (sco(
id)->getID());
618 && elm(
id)==tab._root_4check()
622 iterator elementsInScope = tab.queryScopeContents(theScope);
623 auto equalsTheElement = [&](
PMO&
entry) {
return entry == theElement; };
624 bool properlyRegistered = has_any (elementsInScope, equalsTheElement);
626 VERIFY ( properlyRegistered,
"(1.8) Elements",
"Element not registered as member of the enclosing scope: "+ theElement);
632 VERIFY ( tab.contains(
id),
"(2.1) Scopes",
"Scope not registered in main table");
633 VERIFY ( elm(
id),
"(2.2) Scopes",
"Scope entry doesn't hold a Placement");
634 VERIFY ( sco(
id),
"(2.3) Scopes",
"Scope entry doesn't hold a containing Scope");
636 PMO* root = tab._root_4check();
637 PMO* scope = sco(
id);
638 while (scope && scope != sco(scope->getID()))
639 scope = sco(scope->getID());
641 VERIFY ( root==scope,
"(2.4) Scopes",
"Found a scope not attached below root.");
643 for_each ( tab._contents_4check(
id), &Validator::checkScopeEntry,
this, id, _1 );
647 checkScopeEntry (ID scope, ID
entry)
649 VERIFY ( tab.contains(entry),
"(2.5) Scopes",
"Scope member not registered in main table");
650 VERIFY ( elm(entry),
"(2.6) Scopes",
"Scope member entry doesn't refer to a valid Placement");
651 VERIFY ( sco(entry),
"(2.7) Scopes",
"Scope member entry is lacking valid scope information");
652 VERIFY ( sco(entry)->getID() == scope,
653 "(2.8) Scopes",
"Scope member registered as belonging to a different scope in main table");
659 VERIFY ( 0 < tab.
size(),
"(4.1) Storage",
"Implementation table is empty");
660 VERIFY ( 0 < tab.element_cnt(),
"(4.2) Storage",
"No Placement instances allocated");
662 "(4.3) Storage",
"Number of elements and scope entries disagree");
663 VERIFY ( tab.
size()==tab.element_cnt(),
664 "(4.4) Storage",
"Number of entries doesn't match number of allocated Placement instances");
672 checkRoot (tab._root_4check());
674 for_each ( tab._eachEntry_4check(), &Validator::checkEntry,
this, _1 );
675 for_each ( tab._eachScope_4check(), &Validator::checkScope,
this, _1 );
697 PlacementIndex::isValid()
const 701 VERIFY ( pTab_,
"(0) Basics" ,
"Implementation tables not initialised");
707 catch(SelfCheckFailure& failure)
710 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.