2009-05-09 03:08:38 +02:00
|
|
|
/*
|
|
|
|
|
PLACEMENT-INDEX.hpp - tracking individual Placements and their relations
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2009, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
2010-12-17 23:28:49 +01:00
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
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.
|
2010-12-17 23:28:49 +01:00
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2009-11-13 04:52:48 +01:00
|
|
|
/** @file placement-index.hpp
|
2009-12-27 06:36:52 +01:00
|
|
|
** Core of the session implementation datastructure.
|
2009-11-13 04:52:48 +01:00
|
|
|
** The PlacementIndex is attached to and controlled by the SessionImpl.
|
|
|
|
|
** Client code is not intended to interface directly to this API. Even
|
|
|
|
|
** Proc-Layer internal facilities use the session datastructure through
|
|
|
|
|
** SessionServices. Embedded within the implementation of PlacementIndex
|
|
|
|
|
** is a flat table structure holding all the Placement instances \em contained
|
|
|
|
|
** in the session. Any further structuring exists on the logical level only.
|
|
|
|
|
**
|
|
|
|
|
** \par PlacementIndex, PlacementRef and MObjectRef
|
2009-12-27 06:36:52 +01:00
|
|
|
** Objects are attached to the session by adding (copying) a Placement instance,
|
|
|
|
|
** and doing so creates a new Placement-ID, which from then on acts as a shorthand for
|
|
|
|
|
** "the object instance" within the session. As long as this instance isn't removed from
|
|
|
|
|
** the session / PlacementIndex, a direct (language) reference can be used to work with
|
|
|
|
|
** "the object instance"; accessing this way is adequate for implementation code living
|
|
|
|
|
** within Lumiera's Proc-Layer.
|
2009-11-13 04:52:48 +01:00
|
|
|
**
|
2009-12-27 06:36:52 +01:00
|
|
|
** To avoid the dangerous dependency on a direct reference, external code would rather
|
|
|
|
|
** rely on the Placement-ID. Moreover, being a simple value, such an ID can be passed
|
|
|
|
|
** through plain C APIs. PlacementRef is a smart-ptr like wrapper, containing just
|
|
|
|
|
** such an ID; dereferentiation transparently causes a lookup operation through the
|
|
|
|
|
** PlacementIndex of the current session. (accessing an invalid PlacementRef throws)
|
|
|
|
|
**
|
|
|
|
|
** When it comes to ownership and lifecycle management, external client code should
|
|
|
|
|
** use MObjectRef instances. In addition to containing a PlacementRef, these set up
|
|
|
|
|
** a smart-ptr managing the MObject instance and sharing ownership with the Placement
|
|
|
|
|
** contained within the PlacementIndex. Usually, the commands expressing any mutating
|
|
|
|
|
** operations on the session, bind MObjectRef instances as arguments; similarly, the
|
|
|
|
|
** public API functions on the Session interface (and similar facade interfaces) are
|
2010-01-09 05:10:32 +01:00
|
|
|
** written in terms of MObectRef.
|
2009-12-27 06:36:52 +01:00
|
|
|
**
|
|
|
|
|
** \par placement scopes
|
|
|
|
|
** When adding a Placement to the index, it is mandatory to specify a Scope: this is
|
|
|
|
|
** another Placement already registered within the index; the new Placement can be thought
|
2014-10-19 05:54:20 +02:00
|
|
|
** of as being located "within" or "below" this scope-defining reference Placement. An
|
2009-12-27 06:36:52 +01:00
|
|
|
** typical example would be the addition of a \c Placement<session::Clip>, specifying
|
2015-05-31 02:03:24 +02:00
|
|
|
** a \c Placement<session::Fork> as scope. This would bring the mentioned Clip onto the
|
|
|
|
|
** "Track", as implemented by a Fork-MObject. Thus, all "object instances" within the
|
2009-12-27 06:36:52 +01:00
|
|
|
** session are arranged in a tree-like fashion. On creation of the PlacementIndex,
|
|
|
|
|
** a root element needs to be provided. While this root element has a meaning for
|
|
|
|
|
** the session, within the index it is just a scope-providing element.
|
|
|
|
|
** Note that a non-empty scope can't be deleted from the Index.
|
|
|
|
|
**
|
|
|
|
|
** \par querying and contents discovery
|
|
|
|
|
** As "the object instance within the session" is synonymous to the placement instance
|
|
|
|
|
** managed by PlacementIndex, the (hash)-ID of such a placement can be used as an
|
|
|
|
|
** object identifier (it is implemented as LUID and stored within the Placement instance).
|
|
|
|
|
** Thus, a basic operation of the index is to fetch a (language) reference to a Placement,
|
|
|
|
|
** given this hash-ID. Another basic operation is to retrieve the scope an given object
|
|
|
|
|
** is living in, represented by the Placement defining this scope (called "scope top").
|
|
|
|
|
** The reverse operation is also possible: given a scope-defining Placement, we can
|
|
|
|
|
** \em discover all the other Placements directly contained within this scope:
|
|
|
|
|
** \c getReferrers(ID) returns an (possibly empty) "Lumiera Forward Iterator",
|
|
|
|
|
** allowing to enumerate the nested elements. Client code within Lumiera's Proc-Layer
|
|
|
|
|
** typically uses this functionality through a ScopeQuery passed to the SessionServices,
|
|
|
|
|
** while external client code would use either QueryFocus and the Scope wrapper objects,
|
|
|
|
|
** or the specific query functions available on the facade objects accessible through
|
|
|
|
|
** the public session API.
|
|
|
|
|
**
|
|
|
|
|
** \par type handling
|
|
|
|
|
** MObjects form a hierarchy and contain RTTI. By special definition trickery, the
|
|
|
|
|
** various instances of the Placement template mirror this hierarchy to some extent.
|
|
|
|
|
** By using the vtable of the referred MObject, a given \c Placement<MObject> can
|
|
|
|
|
** be casted into a more specifically typed Placement, thus allowing to re-gain
|
|
|
|
|
** the fully typed context. This technique plays an important role when it comes
|
|
|
|
|
** to generic processing of the session contents by a visitor, and especially
|
|
|
|
|
** within the Builder. This is a fundamental design decision within Proc-Layer:
|
|
|
|
|
** code should not operate on MObjects and do type/capability queries -- rather
|
|
|
|
|
** any processing is assumed to happen in a suitable typed context. Consequently,
|
|
|
|
|
** client code will never need to fetch Placements directly from the index. This
|
|
|
|
|
** allows all type information to be discarded on adding (copying) a Placement
|
2010-01-09 05:10:32 +01:00
|
|
|
** instance into the PlacementIndex.
|
2009-12-16 04:54:36 +01:00
|
|
|
**
|
|
|
|
|
** @note PlacementIndex is <b>not threadsafe</b>.
|
2009-05-09 03:08:38 +02:00
|
|
|
**
|
2009-05-09 17:32:29 +02:00
|
|
|
** @see PlacementRef
|
|
|
|
|
** @see PlacementIndex_test
|
2009-05-09 03:08:38 +02:00
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-02 16:10:03 +01:00
|
|
|
#ifndef PROC_MOBJECT_PLACEMENT_INDEX_H
|
|
|
|
|
#define PROC_MOBJECT_PLACEMENT_INDEX_H
|
2009-05-09 03:08:38 +02:00
|
|
|
|
2009-06-09 09:05:19 +02:00
|
|
|
#include "lib/util.hpp"
|
2009-11-27 20:30:06 +01:00
|
|
|
#include "lib/error.hpp"
|
2011-12-02 17:50:44 +01:00
|
|
|
#include "lib/symbol.hpp"
|
2009-11-13 04:52:48 +01:00
|
|
|
#include "lib/itertools.hpp"
|
2009-05-20 03:36:12 +02:00
|
|
|
#include "proc/mobject/placement.hpp"
|
2009-05-13 03:46:08 +02:00
|
|
|
#include "proc/mobject/placement-ref.hpp"
|
2009-05-09 03:08:38 +02:00
|
|
|
|
2009-06-06 07:12:09 +02:00
|
|
|
#include <boost/noncopyable.hpp>
|
2017-01-05 00:56:46 +01:00
|
|
|
#include <unordered_map>
|
|
|
|
|
#include <memory>
|
2009-05-13 03:46:08 +02:00
|
|
|
#include <vector>
|
2009-05-09 03:08:38 +02:00
|
|
|
|
2014-04-03 22:43:35 +02:00
|
|
|
namespace std {
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////TICKET #722 : should provide a generic bridge to use hash_value
|
|
|
|
|
template<>
|
|
|
|
|
struct hash<proc::mobject::PlacementMO::ID>
|
|
|
|
|
{
|
|
|
|
|
size_t
|
|
|
|
|
operator() (proc::mobject::PlacementMO::ID const& val) const noexcept
|
|
|
|
|
{
|
|
|
|
|
return hash_value(val);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
|
2011-12-02 16:10:03 +01:00
|
|
|
namespace proc {
|
2009-11-06 19:27:53 +01:00
|
|
|
namespace mobject {
|
2010-01-09 05:10:32 +01:00
|
|
|
|
2009-11-06 19:27:53 +01:00
|
|
|
class MObject;
|
|
|
|
|
|
|
|
|
|
namespace session {
|
2009-06-09 09:05:19 +02:00
|
|
|
|
2009-11-27 20:30:06 +01:00
|
|
|
LUMIERA_ERROR_DECLARE (NOT_IN_SESSION); ///< referring to a Placement not known to the current session
|
|
|
|
|
LUMIERA_ERROR_DECLARE (PLACEMENT_TYPE); ///< requested Placement (pointee) type not compatible with data or context
|
2009-11-28 22:18:09 +01:00
|
|
|
LUMIERA_ERROR_DECLARE (NONEMPTY_SCOPE); ///< Placement scope (still) contains other elements
|
2009-11-27 02:43:09 +01:00
|
|
|
|
|
|
|
|
|
2017-01-05 00:56:46 +01:00
|
|
|
using std::unique_ptr;
|
2009-06-09 09:05:19 +02:00
|
|
|
|
2009-11-13 04:52:48 +01:00
|
|
|
|
2010-01-16 08:51:22 +01:00
|
|
|
/**
|
|
|
|
|
* Helper for building Placement-ID types
|
|
|
|
|
* @todo this is a rather half-baked solution //////////TICKET #523
|
|
|
|
|
*/
|
|
|
|
|
template<typename PLA>
|
|
|
|
|
struct BuildID;
|
|
|
|
|
|
|
|
|
|
/// @note just ignoring the second (parent) type encoded into Placement
|
|
|
|
|
template<typename MO, typename BMO>
|
2016-12-23 04:23:03 +01:00
|
|
|
struct BuildID<Placement<MO,BMO>>
|
2010-01-16 08:51:22 +01:00
|
|
|
{
|
|
|
|
|
typedef PlacementMO::Id<MO> Type;
|
|
|
|
|
typedef MO Target;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
|
|
|
|
|
/**
|
2009-11-13 04:52:48 +01:00
|
|
|
* Structured compound of Placement instances
|
|
|
|
|
* with lookup capabilities. Core of the session datastructure.
|
|
|
|
|
* Adding a Placement creates a separate instance within this network,
|
|
|
|
|
* owned and managed by the backing implementation. All placements are
|
|
|
|
|
* related in a tree-like hierarchy of scopes, where each Placement is
|
|
|
|
|
* within the scope of a parent Placement. There is an additional
|
|
|
|
|
* reverse index, allowing to find the immediate children of any
|
|
|
|
|
* given Placement efficiently. All lookup is based on the
|
|
|
|
|
* Placement's hash-IDs.
|
2009-05-09 03:08:38 +02:00
|
|
|
*/
|
2009-05-09 17:32:29 +02:00
|
|
|
class PlacementIndex
|
2009-11-13 04:52:48 +01:00
|
|
|
: boost::noncopyable
|
2009-05-09 03:08:38 +02:00
|
|
|
{
|
2009-05-20 03:36:12 +02:00
|
|
|
class Table;
|
2010-01-05 01:31:22 +01:00
|
|
|
class Validator;
|
2009-05-20 03:36:12 +02:00
|
|
|
|
2017-01-05 00:56:46 +01:00
|
|
|
unique_ptr<Table> pTab_;
|
2009-05-20 03:36:12 +02:00
|
|
|
|
2009-11-12 20:15:52 +01:00
|
|
|
|
2009-11-13 04:52:48 +01:00
|
|
|
typedef PlacementMO::ID _PID;
|
2014-04-03 22:42:48 +02:00
|
|
|
typedef std::unordered_multimap<_PID,_PID>::const_iterator ScopeIter;
|
2011-12-02 17:50:44 +01:00
|
|
|
typedef lib::RangeIter<ScopeIter> ScopeRangeIter;
|
|
|
|
|
typedef lib::TransformIter<ScopeRangeIter, PlacementMO&> _ID_TableIterator;
|
2009-11-13 04:52:48 +01:00
|
|
|
|
2009-11-12 20:15:52 +01:00
|
|
|
|
|
|
|
|
|
2009-05-20 03:36:12 +02:00
|
|
|
public:
|
2009-06-02 03:31:52 +02:00
|
|
|
typedef PlacementRef<MObject> PRef;
|
2009-06-09 09:05:19 +02:00
|
|
|
typedef PlacementMO::ID const& ID;
|
2009-05-20 03:36:12 +02:00
|
|
|
|
2011-12-02 17:50:44 +01:00
|
|
|
typedef _ID_TableIterator iterator;
|
2009-11-01 03:47:35 +01:00
|
|
|
|
2009-06-06 07:12:09 +02:00
|
|
|
|
2009-12-27 06:36:52 +01:00
|
|
|
/* == query operations == */
|
|
|
|
|
|
|
|
|
|
PlacementMO& find (ID) const;
|
2009-05-20 03:36:12 +02:00
|
|
|
|
|
|
|
|
template<class MO>
|
2009-12-27 06:36:52 +01:00
|
|
|
Placement<MO>& find (PlacementMO::Id<MO>) const;
|
2009-06-02 03:31:52 +02:00
|
|
|
template<class MO>
|
2009-11-01 03:47:35 +01:00
|
|
|
Placement<MO>& find (PlacementRef<MO> const&) const;
|
2009-05-20 03:36:12 +02:00
|
|
|
|
2009-12-27 06:36:52 +01:00
|
|
|
PlacementMO& getScope (PlacementMO const&) const;
|
|
|
|
|
PlacementMO& getScope (ID) const;
|
|
|
|
|
|
|
|
|
|
iterator getReferrers (ID) const;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** retrieve the logical root scope */
|
|
|
|
|
PlacementMO& getRoot() const;
|
|
|
|
|
|
|
|
|
|
size_t size() const;
|
|
|
|
|
bool contains (PlacementMO const&) const;
|
|
|
|
|
bool contains (ID) const;
|
|
|
|
|
|
|
|
|
|
bool isValid() const;
|
2009-12-11 02:49:12 +01:00
|
|
|
|
2009-11-12 02:15:02 +01:00
|
|
|
|
2009-10-29 04:32:00 +01:00
|
|
|
|
|
|
|
|
|
2009-05-20 03:36:12 +02:00
|
|
|
/* == mutating operations == */
|
|
|
|
|
|
2009-11-28 23:37:58 +01:00
|
|
|
ID insert (PlacementMO const& newObj, ID targetScope);
|
2009-05-20 03:36:12 +02:00
|
|
|
bool remove (PlacementMO&);
|
|
|
|
|
bool remove (ID);
|
2009-06-06 07:12:09 +02:00
|
|
|
|
2010-01-16 08:51:22 +01:00
|
|
|
template<class PLA>
|
|
|
|
|
typename BuildID<PLA>::Type insert (PLA const&, ID);
|
|
|
|
|
|
2009-06-06 07:12:09 +02:00
|
|
|
|
|
|
|
|
|
2009-12-16 04:54:36 +01:00
|
|
|
PlacementIndex(PlacementMO const&);
|
2009-12-11 02:49:12 +01:00
|
|
|
~PlacementIndex() ;
|
2009-06-09 09:05:19 +02:00
|
|
|
|
2010-03-28 05:15:45 +02:00
|
|
|
void clear (ID targetScope);
|
|
|
|
|
void clear ();
|
2009-10-16 03:13:57 +02:00
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
};
|
|
|
|
|
|
2009-11-13 04:52:48 +01:00
|
|
|
|
2009-05-09 03:08:38 +02:00
|
|
|
|
|
|
|
|
|
2009-06-09 09:05:19 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* === forwarding implementations of the templated API === */
|
|
|
|
|
|
2010-01-09 05:10:32 +01:00
|
|
|
|
2009-11-28 22:18:09 +01:00
|
|
|
namespace { // shortcuts...
|
|
|
|
|
|
|
|
|
|
template<class MOX>
|
|
|
|
|
inline void
|
|
|
|
|
___check_compatibleType(PlacementMO& questionable)
|
|
|
|
|
{
|
|
|
|
|
if (!questionable.isCompatible<MOX>())
|
|
|
|
|
throw lumiera::error::Logic ("Attempt to retrieve a Placement of specific type, "
|
|
|
|
|
"while the actual type of the pointee (MObject) "
|
|
|
|
|
"registered within the index isn't compatible with the "
|
|
|
|
|
"requested specific MObject subclass"
|
|
|
|
|
,LUMIERA_ERROR_PLACEMENT_TYPE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void
|
|
|
|
|
__check_knownID(PlacementIndex const& idx, PlacementMO::ID id)
|
|
|
|
|
{
|
2009-12-13 05:49:08 +01:00
|
|
|
if (!id)
|
|
|
|
|
throw lumiera::error::Logic ("Encountered a NIL Placement-ID marker"
|
|
|
|
|
,LUMIERA_ERROR_BOTTOM_PLACEMENTREF);
|
2009-11-28 22:18:09 +01:00
|
|
|
if (!idx.contains (id))
|
|
|
|
|
throw lumiera::error::Invalid ("Accessing Placement not registered within the index"
|
|
|
|
|
,LUMIERA_ERROR_NOT_IN_SESSION); ///////////////////////TICKET #197
|
|
|
|
|
}
|
|
|
|
|
}//(End) shortcuts
|
2010-01-09 05:10:32 +01:00
|
|
|
|
|
|
|
|
|
2009-11-27 20:30:06 +01:00
|
|
|
|
|
|
|
|
|
2009-06-09 09:05:19 +02:00
|
|
|
template<class MO>
|
|
|
|
|
inline Placement<MO>&
|
|
|
|
|
PlacementIndex::find (PlacementMO::Id<MO> id) const
|
|
|
|
|
{
|
2010-01-16 08:51:22 +01:00
|
|
|
PlacementMO& result (find ((ID)id));
|
2009-11-27 20:30:06 +01:00
|
|
|
|
|
|
|
|
___check_compatibleType<MO> (result);
|
2009-06-09 09:05:19 +02:00
|
|
|
return static_cast<Placement<MO>&> (result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class MO>
|
|
|
|
|
inline Placement<MO>&
|
|
|
|
|
PlacementIndex::find (PlacementRef<MO> const& pRef) const
|
|
|
|
|
{
|
|
|
|
|
PlacementMO::Id<MO> id (pRef);
|
|
|
|
|
return find (id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline Placement<MObject>&
|
|
|
|
|
PlacementIndex::getScope (PlacementMO const& p) const
|
|
|
|
|
{
|
|
|
|
|
return getScope(p.getID());
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-16 08:51:22 +01:00
|
|
|
|
2009-06-09 09:05:19 +02:00
|
|
|
inline bool
|
|
|
|
|
PlacementIndex::contains (PlacementMO const& p) const
|
|
|
|
|
{
|
|
|
|
|
return contains (p.getID());
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-16 08:51:22 +01:00
|
|
|
|
|
|
|
|
/** convenience shortcut to insert a placement
|
|
|
|
|
* immediately followed by creating a typed-ID,
|
|
|
|
|
* allowing to retain the original typed context
|
|
|
|
|
* @todo this solution is half-baked ///////////////////////////////////TICKET #523
|
2010-03-28 05:15:45 +02:00
|
|
|
* @todo is this API used in application code? or just used in tests?
|
2010-01-16 08:51:22 +01:00
|
|
|
*/
|
|
|
|
|
template<class PLA>
|
|
|
|
|
typename BuildID<PLA>::Type
|
|
|
|
|
PlacementIndex::insert (PLA const& newObj, ID targetScope)
|
|
|
|
|
{
|
|
|
|
|
typedef typename BuildID<PLA>::Target TargetMO;
|
|
|
|
|
PlacementMO const& genericPlacement(newObj);
|
|
|
|
|
|
|
|
|
|
return find (insert (genericPlacement, targetScope))
|
|
|
|
|
.template recastID<TargetMO>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-06-09 09:05:19 +02:00
|
|
|
inline bool
|
|
|
|
|
PlacementIndex::remove (PlacementMO& p)
|
|
|
|
|
{
|
|
|
|
|
return remove (p.getID());
|
|
|
|
|
}
|
|
|
|
|
|
2009-06-06 07:12:09 +02:00
|
|
|
|
|
|
|
|
|
2011-12-02 16:10:03 +01:00
|
|
|
}}} // namespace proc::mobject::session
|
2009-05-09 03:08:38 +02:00
|
|
|
#endif
|