diff --git a/src/proc/mobject/builder/model-port-registry.cpp b/src/proc/mobject/builder/model-port-registry.cpp index 87a26492e..2346c4b9f 100644 --- a/src/proc/mobject/builder/model-port-registry.cpp +++ b/src/proc/mobject/builder/model-port-registry.cpp @@ -53,19 +53,176 @@ //using lumiera::Symbol; namespace mobject { -namespace builder { - - - /** - * TODO type comment - */ - class ModelPortTable - : boost::noncopyable - { + namespace builder { + + + /** + * TODO type comment + */ + class ModelPortTable + : boost::noncopyable + { + + public: + }; + - public: - }; + typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; + + inline MPDescriptor + accessDescriptor() + { + + } + + ModelPortRegistry& + ModelPortRegistry::setActiveInstance (ModelPortRegistry& newRegistry) + { + UNIMPLEMENTED ("handling of active model port registry"); + } + + + /** */ + ModelPortRegistry& + ModelPortRegistry::globalInstance() + { + UNIMPLEMENTED ("access the globally valid registry instance"); + } + + + + /** */ + bool + ModelPortRegistry::contains (ID pID) const + { + UNIMPLEMENTED ("diagnostics querying the state of the pending transaction"); + } + + + /** @return true if the given pipe-ID actually denotes an + * existing, connected and usable model port. + * @note reflects the state of the publicly visible + * model port registry, \em not any model ports + * being registered within a currently pending + * transaction (ongoing build process). */ + bool + ModelPortRegistry::isRegistered (ID key) const + { + if (!key) return false; + + UNIMPLEMENTED ("query the publicly valid contents"); + } + + + /** */ + MPDescriptor + ModelPortRegistry::operator[] (ID key) const + { + UNIMPLEMENTED ("access registered model port"); + } + + + /** */ + MPDescriptor + ModelPortRegistry::accessDescriptor (ID key) + { + UNIMPLEMENTED ("access the current global registry and fetch model port"); + } + + + /** */ + MPDescriptor + ModelPortRegistry::definePort (ID pipeA, ID element_exposing_this_port) + { + UNIMPLEMENTED ("create and register a new model port entry, within the pending transaction"); + } + + + /** */ + void + ModelPortRegistry::remove (PID key) + { + UNIMPLEMENTED ("remove a model port entry from the pending transaction"); + } + + + /** */ + void + ModelPortRegistry::clear() + { + UNIMPLEMENTED ("schedule removal of all registry contents into the pending transaction"); + } + + + /** */ + void + ModelPortRegistry::commit() + { + UNIMPLEMENTED ("transactional switch for new/modified model ports"); + } + + + /** */ + void + ModelPortRegistry::rollback() + { + UNIMPLEMENTED ("discard current transaction"); + } + + + + LUMIERA_ERROR_DEFINE (DUPLICATE_MODEL_PORT, "Attempt to define a new model port with an pipe-ID already denoting an existing port"); + + } // namespace builder -}} // namespace mobject + + LUMIERA_ERROR_DEFINE (INVALID_MODEL_PORT, "Referral to unknown model port"); + LUMIERA_ERROR_DEFINE (UNCONNECTED_MODEL_PORT, "Attempt to operate on an existing but unconnected model port"); + + + ModelPort::ModelPort (ID refID) + : id_(refID) + { + builder::ModelPortRegistry::accessDescriptor (refID); + } + + + /** */ + bool + ModelPort::exists (ID key) + { + return builder::ModelPortRegistry::globalInstance().isRegistered (key); + } + + + /** */ + ID + ModelPort::pipe() const + { + ENSURE (this->id_ == builder::ModelPortRegistry::accessDescriptor(this->id_).id); + + return builder::ModelPortRegistry::accessDescriptor(this->id_).id; + } + + + /** */ + ID + ModelPort::holder() const + { + return builder::ModelPortRegistry::accessDescriptor(this->id_).holder; + } + + + /** */ + StreamType::ID + ModelPort::streamType() const + { + return this->id_.streamType(); + } + + + + +} // namespace mobject diff --git a/src/proc/mobject/builder/model-port-registry.hpp b/src/proc/mobject/builder/model-port-registry.hpp index 2ef87f126..53ddc6810 100644 --- a/src/proc/mobject/builder/model-port-registry.hpp +++ b/src/proc/mobject/builder/model-port-registry.hpp @@ -34,8 +34,6 @@ ** for setting up such a registry, while all other parts of the system just access the current ** model ports through the mobject::ModelPort frontend. ** - ** TODO fill in more details? - ** ** @see ModelPort ** @see OutputDesignation ** @see ModelPortRegistry_test @@ -45,7 +43,11 @@ #ifndef PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H #define PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H +#include "lib/error.hpp" #include "proc/asset/pipe.hpp" +#include "proc/asset/struct.hpp" +#include "proc/mobject/model-port.hpp" + //#include "lib/opaque-holder.hpp" //#include "lib/meta/typelist-util.hpp" @@ -56,17 +58,105 @@ namespace mobject { namespace builder { + using asset::ID; + using asset::Pipe; +//using asset::PPipe; + using asset::Struct; + + LUMIERA_ERROR_DECLARE (DUPLICATE_MODEL_PORT); ///< Attempt to define a new model port with an pipe-ID already denoting an existing port + + /** - * TODO type comment + * Management facility for tracking model ports. + * ModelPort handles are exposed as frontend for usage + * by client code. Model ports are discovered by the builder + * when re-creating the low-level model; during such an ongoing + * build process, newly discovered ports are accumulated within + * a transaction, which then gets committed atomically when the + * new model is complete and ready for use. */ class ModelPortRegistry : boost::noncopyable { + typedef ID PID; + typedef ID StID; + public: + /** @internal record to describe a model port */ + struct ModelPortDescriptor; + + + static ModelPortRegistry& + setActiveInstance (ModelPortRegistry& newRegistry); + + static ModelPortRegistry& + globalInstance(); + + static ModelPortDescriptor const& + accessDescriptor (PID); + + + ModelPortDescriptor const& + definePort (PID pipe, StID element_exposing_this_port); + + bool contains (PID) const; + bool isRegistered (PID) const; + + ModelPortDescriptor const& + operator[] (PID) const; + + + void remove (PID); + void clear(); + + + /** activate pending model port changes. + * Any accumulated changes and newly defined model ports + * are promoted to become part of the current active configuration + * with a single atomic (transactional) switch. + */ + void commit(); + + /** discard pending changes. + * Silently drop model port definition changes since the last commit. + */ + void rollback(); }; + /** ModelPortDescriptor records are used as actual storage + * within the model port registration table; they are never + * exposed to client code directly. + */ + struct ModelPortRegistry::ModelPortDescriptor + { + const PID id; + const StID holder; + + bool + isValid() const + { + return bool(id); + } + + + ModelPortDescriptor() + : id(PID::INVALID) + , holder(StID::INVALID) + { } + + // default copy operations permitted + + protected: + ModelPortDescriptor (PID pipe, StID element_exposing_this_port) + : id(pipe) + , holder(element_exposing_this_port) + { } + }; + + + }} // namespace mobject::builder #endif diff --git a/src/proc/mobject/model-port.hpp b/src/proc/mobject/model-port.hpp index 634d1e0dc..cf98bc149 100644 --- a/src/proc/mobject/model-port.hpp +++ b/src/proc/mobject/model-port.hpp @@ -30,7 +30,7 @@ ** and low-level view. A model port can be associated both to a pipe within a timeline ** in the HighLevelModel, as well as to denote a set of corresponding exit nodes within ** the segments of the render nodes network. Model ports are keyed by Pipe-ID and thus - ** are unique within the application. + ** are bound to be unique within the application. ** ** A model port is rather derived than configured; it emerges during the build process ** when a pipe claims an OutputDesignation and some other entity actually uses this @@ -41,21 +41,22 @@ ** ** Because model ports are discovered this way, dynamically during the build process, ** at some point there is a transactional switch to promote the new configuration - ** to be come the valid current model port configuration. After that switch, model ports + ** to become the valid current model port configuration. After that switch, model ports ** are immutable. ** ** Model ports are to be accessed, enumerated and grouped in various ways, because each ** port belongs to a specific timeline and is used for producing data of a single ** StreamType solely. But all those referrals, searching and grouping happens only ** after the build process has discovered all model ports currently available. - ** Thus we provide a MPortRef smart-pointer to ease handling of those access - ** operations. The actual model port descriptors are owned and managed by - ** the fixture; they are bulk allocated in a similar manner than the + ** Thus actually the ModelPort elements handed out to client code are just + ** smart-handles, accessing a global ModelPortRegistry behind the scenes. + ** Validity of these handles will be checked on each access. The actual + ** model port descriptors are owned and managed by the fixture; + ** they are bulk allocated in a similar manner than the ** ProcNode and WiringDescriptor objects. ** - ** TODO fill in more details? - ** TODO where to put the ModelPortTable - ** + ** @see ModelPortRegistry_test abstract usage example + ** @see ModelPortRegistry management interface ** @see OutputDesignation ** @see OutputMapping ** @see Timeline @@ -66,23 +67,83 @@ #define PROC_MOBJECT_MODEL_PORT_H #include "proc/asset/pipe.hpp" -//#include "lib/opaque-holder.hpp" -//#include "lib/meta/typelist-util.hpp" - -//extern "C" { -//#include "lib/luid.h" -//} +#include "lib/bool-checkable.hpp" +#include "lib/streamtype.hpp" namespace mobject { + LUMIERA_ERROR_DECLARE (INVALID_MODEL_PORT); ///< Referral to unknown model port + LUMIERA_ERROR_DECLARE (UNCONNECTED_MODEL_PORT); ///< Attempt to operate on an existing but unconnected model port + + + using asset::ID; + using lumiera::StreamType; + + /** - * TODO type comment + * Handle denoting a port within the model, + * where actually output data can be pulled. + * ModelPort is a frontend to be used by clients. + * These ModelPort handle objects may be copied and stored + * at will, but their validity will be verified on each access. + * Actually, the Builder will discover any model ports and + * maintain a ModelPortRegistry behind the scenes. + * + * Each model port corresponds to a (global) pipe within a + * specific Timeline; consequently each such port is also + * bound to produce data of a specific StreamType (as defined by + * the corresponding pipe). A model port may be in \em unconnected + * state, which can be checked by \c bool conversion. While the + * ModelPort handles are value objects, the identity of the + * underlying model port (descriptor) is given by the + * corresponding pipe-ID, thus effectively resulting + * in a global namespace for model ports. + * + * @see builder::ModelPortRegistry management interface + * @see ModelPortRegistry_test abstract usage example */ class ModelPort - : boost::noncopyable + : public lib::BoolCheckable { + ID id_; public: + ModelPort() ///< \em unconnected model port + : id_(ID::INVALID) + { } + + ModelPort (ID refID); ///< @note conversion from pipe-ID + + // using default copy operations + + + static bool exists (ID); + + ID pipe() const; + ID holder() const; + StreamType::ID streamType() const; + + bool + isValid() const + { + return exists (this->id_); + } + + + friend bool + operator== (ModelPort const& mp1, ModelPort const& mp2) + { + return mp1.id_ == mp2.id_; + } + + friend bool + operator!= (ModelPort const& mp1, ModelPort const& mp2) + { + return mp1.id_ != mp2.id_; + } + + private: + }; diff --git a/tests/44builder.tests b/tests/44builder.tests index 6424f453f..5df8ef0a4 100644 --- a/tests/44builder.tests +++ b/tests/44builder.tests @@ -15,6 +15,11 @@ PLANNED "BuildSegment_test" BuildSegment_test < //#include #include -//using boost::format; -//using boost::scoped_ptr; -using util::isSameObject; -using util::isnil; -using std::string; - namespace mobject { namespace builder { namespace test { + //using boost::format; + //using boost::scoped_ptr; + using util::isSameObject; + using util::isnil; + using std::string; + using asset::Pipe; using asset::PPipe; + using asset::Struct; + using asset::Timeline; + using asset::PTimeline; + using lumiera::Query; + + typedef asset::ID PID; + typedef asset::ID TID; + - //typedef asset::ID PID; namespace { // test environment + inline PID + getPipe (string id) + { + return Pipe::query("id("+id+")"); + } + + inline TID + getTimeline (string id) + { + return asset::Struct::retrieve (Query ("id("+id+")"))->getID(); + } + + typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor; + struct TestContext { ModelPortRegistry registry_; @@ -88,30 +111,35 @@ namespace test { { TestContext ctx; - fabricating_ModelPorts (ctx); + fabricating_ModelPorts (ctx.registry_); accessing_ModelPorts(); - transactionalSwitch (ctx); + transactionalSwitch (ctx.registry_); } void fabricating_ModelPorts (ModelPortRegistry& registry) { - ModelPortDescriptor& p1 = registry.definePort (pipeA, someTimeline); - ModelPortDescriptor& p2 = registry.definePort (pipeB, someTimeline); + /* == some Assets to play with == */ + PID pipeA = getPipe ("pipeA"); + PID pipeB = getPipe ("pipeB"); + PID pipeWC = getPipe ("WCpipe"); + TID someTimeline = getTimeline ("some_test_Timeline"); - CHECK (p1); - CHECK (p2); + // start out with defining some new model ports...... + MPDescriptor p1 = registry.definePort (pipeA, someTimeline); + MPDescriptor p2 = registry.definePort (pipeB, someTimeline); + + CHECK (registry.contains (pipeA)); + CHECK (registry.contains (pipeB)); VERIFY_ERROR (DUPLICATE_MODEL_PORT, registry.definePort(pipeB, someTimeline) ); - CHECK (p2); + CHECK (registry.contains (pipeB)); - CHECK (p1.getID() == pipeA); - CHECK (p2.getID() == pipeB); - CHECK (p1.getPipe() == pipeA); - CHECK (p2.getPipe() == pipeB); - CHECK (p1.getTimeline() == someTimeline); - CHECK (p2.getTimeline() == someTimeline); + CHECK (p1.id == pipeA); + CHECK (p2.id == pipeB); + CHECK (p1.holder == someTimeline); + CHECK (p2.holder == someTimeline); registry.commit(); } @@ -120,6 +148,10 @@ namespace test { void accessing_ModelPorts () { + PID pipeA = getPipe ("pipeA"); + PID pipeB = getPipe ("pipeB"); + PID pipeWC = getPipe ("WCpipe"); + ModelPort mp1(pipeA); ModelPort mp2(pipeB); @@ -149,26 +181,32 @@ namespace test { CHECK (mp1x.pipe() == pipeA); VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.pipe()); - CHECK (mp1.streamType() == pipeA.getStreamType()); + CHECK (mp1.streamType() == pipeA.streamType()); } void transactionalSwitch (ModelPortRegistry& registry) { + PID pipeA = getPipe ("pipeA"); + PID pipeB = getPipe ("pipeB"); + PID pipeWC = getPipe ("WCpipe"); + CHECK ( ModelPort::exists (pipeB)); CHECK (!ModelPort::exists (pipeWC)); CHECK (ModelPort::exists (pipeA)); CHECK (registry.contains (pipeA)); registry.remove (pipeA); - CHECK (ModelPort::exists (pipeA)); + CHECK ( ModelPort::exists (pipeA)); CHECK (!registry.contains (pipeA)); - - ModelPortDescriptor& p1 = registry.definePort (pipeA, anotherTimeline); + + // now create a new and differing definition of port A + TID anotherTimeline = getTimeline ("another_test_Timeline"); + MPDescriptor p1 = registry.definePort (pipeA, anotherTimeline); CHECK (registry.contains (pipeA)); - CHECK (p1.getTimeline() == anotherTimeline); - CHECK (ModelPort(pipeA).timeline() != anotherTimeline); + CHECK (p1.holder == anotherTimeline); + CHECK (ModelPort(pipeA).holder() != anotherTimeline); registry.remove (pipeB); registry.definePort (pipeWC,anotherTimeline); @@ -184,7 +222,7 @@ namespace test { CHECK (portB); CHECK (portA.pipe() == pipeA); CHECK (portB.pipe() == pipeB); - CHECK (portA.timeline() != anotherTimeline); + CHECK (portA.holder() != anotherTimeline); registry.commit(); CHECK ( ModelPort::exists (pipeA)); @@ -192,14 +230,25 @@ namespace test { CHECK ( ModelPort::exists (pipeWC)); CHECK ( portA); CHECK (!portB); - CHECK (portA.timeline() == anotherTimeline); + CHECK (portA.holder() == anotherTimeline); CHECK (portA.pipe() == pipeA); VERIFY_ERROR (UNCONNECTED_MODEL_PORT, portB.pipe()); ModelPort pwc(pipeWC); CHECK (pwc); CHECK (pwc.pipe() == pipeWC); - CHECK (pwc.timeline() == anotherTimeline); + CHECK (pwc.holder() == anotherTimeline); + + registry.remove (pipeA); + registry.clear(); + CHECK (!registry.contains (pipeA)); + CHECK (!registry.contains (pipeB)); + CHECK (!registry.contains (pipeWC)); + + registry.rollback(); + CHECK ( registry.contains (pipeA)); + CHECK ( registry.contains (pipeB)); + CHECK ( registry.contains (pipeWC)); } };