From aea5ed323a577035cb0947dde8f625f96ac1f575 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 23 Apr 2008 04:16:45 +0200 Subject: [PATCH 01/11] refactor to use the customized lumiera::P instead of shared_ptr --- src/common/configrules.hpp | 8 ++-- src/common/query/mockconfigrules.hpp | 2 +- src/proc/asset/buildinstruct.hpp | 5 ++- src/proc/asset/clip.hpp | 2 +- src/proc/asset/db.hpp | 2 +- src/proc/asset/media.cpp | 2 +- src/proc/asset/media.hpp | 11 +++--- src/proc/asset/meta.hpp | 2 +- src/proc/asset/pipe.hpp | 3 +- src/proc/asset/proc.hpp | 4 +- src/proc/asset/procpatt.cpp | 2 +- src/proc/asset/procpatt.hpp | 8 ++-- src/proc/asset/struct.cpp | 12 +++--- src/proc/asset/struct.hpp | 6 +-- src/proc/asset/track.hpp | 2 +- src/proc/assetmanager.cpp | 32 ++++++++-------- src/proc/assetmanager.hpp | 6 +-- src/proc/lumiera.hpp | 1 + src/proc/mobject/mobject.hpp | 15 ++++++-- src/proc/mobject/session/abstractmo.cpp | 10 ++++- src/proc/mobject/session/abstractmo.hpp | 2 + src/proc/mobject/session/clip.hpp | 4 +- src/proc/mobject/session/defsmanager.cpp | 20 +++++----- src/proc/mobject/session/defsmanager.hpp | 14 +++---- src/proc/mobject/session/defsregistry.hpp | 37 ++++++++++--------- src/proc/mobject/session/mobjectfactory.hpp | 2 +- src/proc/mobject/session/track.hpp | 6 ++- .../mobject/session/defsregistryimpltest.cpp | 11 ++++-- 28 files changed, 128 insertions(+), 103 deletions(-) diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp index 81da041ae..0968c88d4 100644 --- a/src/common/configrules.hpp +++ b/src/common/configrules.hpp @@ -46,6 +46,7 @@ #ifndef LUMIERA_CONFIGRULES_H #define LUMIERA_CONFIGRULES_H +#include "common/p.hpp" #include "common/query.hpp" #include "common/typelistutil.hpp" #include "common/singletonsubclass.hpp" @@ -57,14 +58,13 @@ #include "proc/asset/track.hpp" #include -#include namespace lumiera { using std::string; - using std::tr1::shared_ptr; + using lumiera::P; @@ -148,13 +148,13 @@ namespace lumiera * @query any goals to be fulfilled by the solution. * @return false if resolution failed. In this case, solution ptr is empty. */ - virtual bool resolve (shared_ptr& solution, const Query& q) = 0; + virtual bool resolve (P& solution, const Query& q) = 0; }; // TODO: the Idea is to provide specialisations for the concrete types // we want to participate in the ConfigRules system.... // Thus we get the possibility to create a specific return type, - // e.g. return a shared_ptr but a Placement, using the appropriate factory. + // e.g. return a P but a Placement, using the appropriate factory. // Of course then the definitions need to be split up in separate headers. diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp index f5bea3264..f56519ed6 100644 --- a/src/common/query/mockconfigrules.hpp +++ b/src/common/query/mockconfigrules.hpp @@ -74,7 +74,7 @@ namespace lumiera /** a traits-class to define the smart-ptr to wrap the result */ template - struct WrapReturn { typedef shared_ptr Wrapper; }; + struct WrapReturn { typedef P Wrapper; }; template<> struct WrapReturn { typedef PProcPatt Wrapper; }; diff --git a/src/proc/asset/buildinstruct.hpp b/src/proc/asset/buildinstruct.hpp index 577a108c1..8b1162103 100644 --- a/src/proc/asset/buildinstruct.hpp +++ b/src/proc/asset/buildinstruct.hpp @@ -41,12 +41,13 @@ using std::string; namespace asset { + using lumiera::P; class Proc; class ProcPatt; - typedef shared_ptr PProc; - typedef shared_ptr PProcPatt; + typedef P PProc; + typedef P PProcPatt; static Symbol CURRENT = "current"; diff --git a/src/proc/asset/clip.hpp b/src/proc/asset/clip.hpp index f5e1ea629..53a74faa5 100644 --- a/src/proc/asset/clip.hpp +++ b/src/proc/asset/clip.hpp @@ -53,7 +53,7 @@ namespace asset }; - typedef shared_ptr PClipAsset; + typedef P PClipAsset; const string CLIP_SUBFOLDER = "clips"; // TODO: handling of hard-wired constants.... diff --git a/src/proc/asset/db.hpp b/src/proc/asset/db.hpp index cb32868d7..f35785e5e 100644 --- a/src/proc/asset/db.hpp +++ b/src/proc/asset/db.hpp @@ -100,7 +100,7 @@ namespace asset bool del (ID hash) { return table.erase (hash); } template - shared_ptr + P get (ID hash) const { return dynamic_pointer_cast (find (hash)); diff --git a/src/proc/asset/media.cpp b/src/proc/asset/media.cpp index a91e8284b..68fcb0b56 100644 --- a/src/proc/asset/media.cpp +++ b/src/proc/asset/media.cpp @@ -219,7 +219,7 @@ namespace asset * @throw Invalid if the given media asset is not top-level, * but rather part or a multichannel (compound) media */ - shared_ptr + P MediaFactory::operator() (asset::Media& mediaref) throw(lumiera::error::Invalid) { if (mediaref.checkCompound()) diff --git a/src/proc/asset/media.hpp b/src/proc/asset/media.hpp index 4e122cd53..b14399ce5 100644 --- a/src/proc/asset/media.hpp +++ b/src/proc/asset/media.hpp @@ -52,6 +52,7 @@ namespace asset class MediaFactory; class ProcPatt; + using lumiera::P; using lumiera::Time; @@ -74,9 +75,9 @@ namespace asset const Time len_; public: - typedef shared_ptr PMedia; - typedef shared_ptr PClip; - typedef shared_ptr PProcPatt; + typedef P PMedia; + typedef P PClip; + typedef P PProcPatt; typedef mobject::session::PClipMO PClipMO; @@ -142,7 +143,7 @@ namespace asset class MediaFactory : public lumiera::Factory { public: - typedef shared_ptr PType; + typedef P PType; PType operator() (Asset::Ident& key, const string& file=""); PType operator() (const string& file, const Category& cat); @@ -152,7 +153,7 @@ namespace asset PType operator() (const char* file, const Category& cat); PType operator() (const char* file, asset::Kind); - shared_ptr + P operator() (asset::Media& mediaref) throw(lumiera::error::Invalid); }; diff --git a/src/proc/asset/meta.hpp b/src/proc/asset/meta.hpp index 02be992db..c15f14fbf 100644 --- a/src/proc/asset/meta.hpp +++ b/src/proc/asset/meta.hpp @@ -95,7 +95,7 @@ namespace asset class MetaFactory : public lumiera::Factory { public: - typedef shared_ptr PType; + typedef P PType; PType operator() (Asset::Ident& key); ////////////TODO define actual operation diff --git a/src/proc/asset/pipe.hpp b/src/proc/asset/pipe.hpp index 110dd1975..c2a9a8ff0 100644 --- a/src/proc/asset/pipe.hpp +++ b/src/proc/asset/pipe.hpp @@ -31,9 +31,10 @@ namespace asset { + using lumiera::P; class Pipe; - typedef shared_ptr PPipe; + typedef P PPipe; template<> diff --git a/src/proc/asset/proc.hpp b/src/proc/asset/proc.hpp index 05eca4ddf..724c8a786 100644 --- a/src/proc/asset/proc.hpp +++ b/src/proc/asset/proc.hpp @@ -48,7 +48,7 @@ namespace asset class Proc; class ProcFactory; - typedef shared_ptr PProc; + typedef P PProc; @@ -97,7 +97,7 @@ namespace asset class ProcFactory : public lumiera::Factory { public: - typedef shared_ptr PType; + typedef P PType; PType operator() (Asset::Ident& key); ////////////TODO define actual operation diff --git a/src/proc/asset/procpatt.cpp b/src/proc/asset/procpatt.cpp index 1adf8736c..d5173b9ea 100644 --- a/src/proc/asset/procpatt.cpp +++ b/src/proc/asset/procpatt.cpp @@ -60,7 +60,7 @@ namespace asset * some ProcPatt as a template for creating more * spezialized patterns. */ - shared_ptr + P ProcPatt::newCopy (string newID) const { TODO ("implement the Pattern-ID within the propDescriptor!"); diff --git a/src/proc/asset/procpatt.hpp b/src/proc/asset/procpatt.hpp index 6c8cf37e0..65f832b41 100644 --- a/src/proc/asset/procpatt.hpp +++ b/src/proc/asset/procpatt.hpp @@ -35,13 +35,14 @@ using std::vector; namespace asset { + using lumiera::P; using lumiera::Symbol; class Proc; class ProcPatt; class BuildInstruct; - typedef shared_ptr PProc; - typedef shared_ptr PProcPatt; + typedef P PProc; + typedef P PProcPatt; typedef vector InstructionSequence; @@ -61,14 +62,13 @@ namespace asset friend class StructFactoryImpl; public: - shared_ptr newCopy (string newID) const; + P newCopy (string newID) const; ProcPatt& attach (Symbol where, PProc& node); ProcPatt& operator+= (PProcPatt& toReuse); }; - typedef shared_ptr PProcPatt; diff --git a/src/proc/asset/struct.cpp b/src/proc/asset/struct.cpp index 6e85c50c2..5813ce339 100644 --- a/src/proc/asset/struct.cpp +++ b/src/proc/asset/struct.cpp @@ -89,10 +89,10 @@ namespace asset * created as a side effect of calling the concrete Struct subclass ctor. */ template - shared_ptr + P StructFactory::operator() (const Query& capabilities) { - shared_ptr res; + P res; QueryHandler& typeHandler = ConfigRules::instance(); typeHandler.resolve (res, capabilities); @@ -117,7 +117,7 @@ namespace asset * @see ProcPatt * @see DefaultsManager */ - shared_ptr + P StructFactory::operator() (string pipeID, string streamID) { normalizeID (pipeID); @@ -147,9 +147,9 @@ namespace asset namespace asset { - template shared_ptr StructFactory::operator() (const Query& query); - template shared_ptr StructFactory::operator() (const Query& query); - template PProcPatt StructFactory::operator() (const Query& query); + template P StructFactory::operator() (const Query& query); + template P StructFactory::operator() (const Query& query); + template PProcPatt StructFactory::operator() (const Query& query); } // namespace asset diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp index 7c4c4367b..5941bd13a 100644 --- a/src/proc/asset/struct.hpp +++ b/src/proc/asset/struct.hpp @@ -120,12 +120,12 @@ namespace asset public: - typedef shared_ptr PType; + typedef P PType; template - shared_ptr operator() (const Query& query); ////////////TODO actually do something sensible here + P operator() (const Query& query); ////////////TODO actually do something sensible here - shared_ptr operator() (string pipeID, string streamID); + P operator() (string pipeID, string streamID); }; diff --git a/src/proc/asset/track.hpp b/src/proc/asset/track.hpp index a36a6242c..01abe5e8e 100644 --- a/src/proc/asset/track.hpp +++ b/src/proc/asset/track.hpp @@ -48,7 +48,7 @@ namespace asset }; - typedef shared_ptr PTrack; + typedef P PTrack; diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index fcdf45dc0..9acb29daa 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -130,11 +130,11 @@ namespace asset * of the stored object differs and can't be casted. */ template - shared_ptr + P AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid) { - if (shared_ptr obj = registry.get (id)) + if (P obj = registry.get (id)) return obj; else if (known (id)) // provide Ident tuple of existing Asset @@ -143,14 +143,14 @@ namespace asset throw UnknownID (id); } - /** convienience shortcut for fetching the registered shared_ptr + /** convienience shortcut for fetching the registered smart-ptr * which is in charge of the given asset instance. By querying * directly asset.id (of type ID), the call to registry.get() * can bypass the dynamic cast, because the type of the asset * is explicitely given by type KIND. */ template - shared_ptr + P AssetManager::wrap (const KIND& asset) { ENSURE (instance().known(asset.id), @@ -169,7 +169,7 @@ namespace asset { return ( registry.get (ID(id)) ); } // query most general Asset ID-kind and use implicit - // conversion from shared_ptr to bool (test if empty) + // conversion from smart-ptr to bool (test if empty) /** @@ -245,18 +245,18 @@ namespace asset template ID AssetManager::reg (Asset* obj, const Asset::Ident& idi); - template shared_ptr AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); - template shared_ptr AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); - template shared_ptr AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); - template shared_ptr AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); - template shared_ptr AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); + template P AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); + template P AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); + template P AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); + template P AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); + template P AssetManager::getAsset (const ID& id) throw(lumiera::error::Invalid); - template shared_ptr AssetManager::wrap (const Asset& asset); - template shared_ptr AssetManager::wrap (const Media& asset); - template shared_ptr AssetManager::wrap (const Clip& asset); - template shared_ptr AssetManager::wrap (const Track& asset); - template shared_ptr AssetManager::wrap (const Pipe& asset); - template shared_ptr AssetManager::wrap (const ProcPatt& asset); + template P AssetManager::wrap (const Asset& asset); + template P AssetManager::wrap (const Media& asset); + template P AssetManager::wrap (const Clip& asset); + template P AssetManager::wrap (const Track& asset); + template P AssetManager::wrap (const Pipe& asset); + template P AssetManager::wrap (const ProcPatt& asset); } // namespace asset diff --git a/src/proc/assetmanager.hpp b/src/proc/assetmanager.hpp index 22138065d..3f62d2ecd 100644 --- a/src/proc/assetmanager.hpp +++ b/src/proc/assetmanager.hpp @@ -73,13 +73,13 @@ namespace asset /** provide the unique ID for given Asset::Ident tuple */ static ID getID (const Asset::Ident&); - /** retrieve the registerd shared_ptr for any asset */ + /** retrieve the registerd smart-ptr for any asset */ template - static shared_ptr wrap (const KIND& asset); + static P wrap (const KIND& asset); /** find and return corresponging object */ template - shared_ptr getAsset (const ID& id) throw(lumiera::error::Invalid); + P getAsset (const ID& id) throw(lumiera::error::Invalid); /** @return true if the given id is registered in the internal asset DB */ diff --git a/src/proc/lumiera.hpp b/src/proc/lumiera.hpp index b56ab69b6..c00a70254 100644 --- a/src/proc/lumiera.hpp +++ b/src/proc/lumiera.hpp @@ -30,6 +30,7 @@ /* common types frequently used... */ +#include "common/p.hpp" #include "common/util.hpp" #include "common/time.hpp" #include "common/error.hpp" ///< pulls in NoBug via nobugcfg.hpp diff --git a/src/proc/mobject/mobject.hpp b/src/proc/mobject/mobject.hpp index a8c02c761..28ab788ed 100644 --- a/src/proc/mobject/mobject.hpp +++ b/src/proc/mobject/mobject.hpp @@ -26,17 +26,18 @@ #include "pre.hpp" -#include -#include #include "proc/lumiera.hpp" #include "proc/mobject/builder/buildertool.hpp" #include "proc/mobject/placement.hpp" #include "proc/asset.hpp" // TODO finally not needed? +#include +#include +#include + using std::list; -using std::tr1::shared_ptr; #include "proc/assetmanager.hpp" using proc_interface::IDA; // TODO finally not needed? @@ -45,6 +46,7 @@ using proc_interface::AssetManager; namespace mobject { + using lumiera::P; namespace session { @@ -58,7 +60,10 @@ namespace mobject * manipulated and finally rendered within Lumiera's EDL * are MObjects. */ - class MObject : public Buildable + class MObject + : public Buildable, + boost::noncopyable, + boost::equality_comparable< MObject > { protected: typedef lumiera::Time Time; @@ -79,6 +84,8 @@ namespace mobject virtual bool isValid() const =0; virtual Time& getLength() =0; ///< @todo how to deal with the time/length field?? + + virtual bool operator== (const MObject& oo) const =0; }; diff --git a/src/proc/mobject/session/abstractmo.cpp b/src/proc/mobject/session/abstractmo.cpp index e484eee6e..e3ebffd4f 100644 --- a/src/proc/mobject/session/abstractmo.cpp +++ b/src/proc/mobject/session/abstractmo.cpp @@ -28,8 +28,14 @@ namespace mobject namespace session { - /** */ - + /** default/fallback implementation of equality + * using literal object identity (same address) + */ + bool + AbstractMO::operator== (const MObject& oo) const + { + return (this == &oo); + } } // namespace mobject::session diff --git a/src/proc/mobject/session/abstractmo.hpp b/src/proc/mobject/session/abstractmo.hpp index 45942986d..5de61737f 100644 --- a/src/proc/mobject/session/abstractmo.hpp +++ b/src/proc/mobject/session/abstractmo.hpp @@ -49,6 +49,8 @@ namespace mobject DEFINE_PROCESSABLE_BY (builder::BuilderTool); + virtual bool operator== (const MObject& oo) const; + }; diff --git a/src/proc/mobject/session/clip.hpp b/src/proc/mobject/session/clip.hpp index 75cac3ef7..cf15cbd27 100644 --- a/src/proc/mobject/session/clip.hpp +++ b/src/proc/mobject/session/clip.hpp @@ -38,8 +38,8 @@ namespace mobject namespace session { using asset::Media; - typedef shared_ptr PMedia; - typedef shared_ptr PClipAsset; + typedef P PMedia; + typedef P PClipAsset; /** diff --git a/src/proc/mobject/session/defsmanager.cpp b/src/proc/mobject/session/defsmanager.cpp index c84e5116d..b60e38064 100644 --- a/src/proc/mobject/session/defsmanager.cpp +++ b/src/proc/mobject/session/defsmanager.cpp @@ -39,7 +39,7 @@ namespace mobject { namespace session { - using std::tr1::shared_ptr; + using lumiera::P; @@ -52,10 +52,10 @@ namespace mobject template - shared_ptr + P DefsManager::search (const Query& capabilities) { - shared_ptr res; + P res; QueryHandler& typeHandler = ConfigRules::instance(); for (DefsRegistry::Iter i = defsRegistry->candidates(capabilities); res = *i ; ++i ) @@ -69,10 +69,10 @@ namespace mobject template - shared_ptr + P DefsManager::create (const Query& capabilities) { - shared_ptr res; + P res; QueryHandler& typeHandler = ConfigRules::instance(); typeHandler.resolve (res, capabilities); if (res) @@ -83,9 +83,9 @@ namespace mobject template bool - DefsManager::define (const shared_ptr& defaultObj, const Query& capabilities) + DefsManager::define (const P& defaultObj, const Query& capabilities) { - shared_ptr candidate (defaultObj); + P candidate (defaultObj); QueryHandler& typeHandler = ConfigRules::instance(); typeHandler.resolve (candidate, capabilities); if (!candidate) @@ -97,17 +97,17 @@ namespace mobject template bool - DefsManager::forget (const shared_ptr& defaultObj) + DefsManager::forget (const P& defaultObj) { return defsRegistry->forget (defaultObj); } template - shared_ptr + P DefsManager::operator() (const Query& capabilities) { - shared_ptr res (search (capabilities)); + P res (search (capabilities)); if (res) return res; else diff --git a/src/proc/mobject/session/defsmanager.hpp b/src/proc/mobject/session/defsmanager.hpp index 753b452d2..eb2ca1502 100644 --- a/src/proc/mobject/session/defsmanager.hpp +++ b/src/proc/mobject/session/defsmanager.hpp @@ -25,11 +25,11 @@ #define MOBJECT_SESSION_DEFSMANAGER_H +#include "common/p.hpp" #include "common/query.hpp" #include #include -#include @@ -37,7 +37,7 @@ namespace mobject { namespace session { - using std::tr1::shared_ptr; + using lumiera::P; using boost::scoped_ptr; class DefsRegistry; @@ -69,14 +69,14 @@ namespace mobject * is considered \e misconfiguration. */ template - shared_ptr operator() (const lumiera::Query&); + P operator() (const lumiera::Query&); /** search through the registered defaults, never create anything. * @return object fulfilling the query, \c empty ptr if not found. */ template - shared_ptr search (const lumiera::Query&); + P search (const lumiera::Query&); /** retrieve an object fulfilling the query and register it as default. * The resolution is delegated to the ConfigQuery system (which may cause @@ -84,7 +84,7 @@ namespace mobject * @return object fulfilling the query, \c empty ptr if no solution. */ template - shared_ptr create (const lumiera::Query&); + P create (const lumiera::Query&); /** register the given object as default, after ensuring it fulfills the * query. The latter may cause some properties of the object to be set, @@ -93,13 +93,13 @@ namespace mobject * @note only a weak ref to the object is stored */ template - bool define (const shared_ptr&, const lumiera::Query&); + bool define (const P&, const lumiera::Query&); /** remove the defaults registration of the given object, if there was such * @return false if nothing has been changed because the object wasn't registered */ template - bool forget (const shared_ptr&); + bool forget (const P&); // Q: can we have something along the line of...? diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index 4aca10868..7d7589110 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -28,6 +28,7 @@ #include "common/multithread.hpp" #include "common/query.hpp" #include "common/util.hpp" +#include "common/p.hpp" #include #include @@ -41,10 +42,10 @@ namespace mobject { namespace session { - using std::tr1::shared_ptr; - using std::tr1::weak_ptr; - using lumiera::Thread; + using lumiera::P; using lumiera::Query; + using lumiera::Thread; + using std::tr1::weak_ptr; using std::string; using boost::format; @@ -60,7 +61,7 @@ namespace mobject /** we maintain an independent defaults registry * for every participating kind of objects */ - typedef std::vector > Table; + typedef std::vector< P > Table; uint maxSlots (0); ///< number of different registered Types @@ -76,7 +77,7 @@ namespace mobject Query query; weak_ptr objRef; - Record (const Query& q, const shared_ptr& obj) + Record (const Query& q, const P& obj) : degree (lumiera::query::countPraed (q)), query (q), objRef (obj) @@ -85,15 +86,15 @@ namespace mobject struct Search ///< Functor searching for a specific object { - Search (const shared_ptr& obj) + Search (const P& obj) : obj_(obj) { } - const shared_ptr& obj_; + const P& obj_; bool operator() (const Record& rec) { - shared_ptr storedObj (rec.objRef.lock()); + P storedObj (rec.objRef.lock()); return storedObj && (storedObj == obj_); } }; @@ -110,7 +111,7 @@ namespace mobject }; operator string () const { return str (dumpRecord % degree % query % dumpObj()); } - string dumpObj () const { shared_ptr o (objRef.lock()); return o? string(*o):"dead"; } + string dumpObj () const { P o (objRef.lock()); return o? string(*o):"dead"; } }; /** every new kind of object (Type) creates a new @@ -186,7 +187,7 @@ namespace mobject typedef typename Slot::Registry::iterator II; II p,i,e; - shared_ptr next, ptr; + P next, ptr; Iter (II from, II to) ///< just ennumerates the given range : p(from), i(from), e(to) @@ -201,7 +202,7 @@ namespace mobject operator++ (); // init to first element (or to null if emty) } - shared_ptr findNext () throw() + P findNext () throw() { while (!next) { @@ -214,9 +215,9 @@ namespace mobject public: - shared_ptr operator* () { return ptr; } - bool hasNext () { return next || findNext(); } - Iter operator++ (int) { Iter tmp=*this; operator++(); return tmp; } + P operator* () { return ptr; } + bool hasNext () { return next || findNext(); } + Iter operator++ (int) { Iter tmp=*this; operator++(); return tmp; } Iter& operator++ () { ptr=findNext(); @@ -237,7 +238,7 @@ namespace mobject template Iter candidates (const Query& query) { - shared_ptr dummy; + P dummy; Record entry (query, dummy); typedef typename Slot::Registry Registry; Registry& registry = Slot::access(table_); @@ -261,7 +262,7 @@ namespace mobject * case, also the param obj shared-ptr is rebound! */ template - bool put (shared_ptr& obj, const Query& query) + bool put (P& obj, const Query& query) { Record entry (query, obj); typedef typename Slot::Registry Registry; @@ -272,7 +273,7 @@ namespace mobject if ( pos!=registry.end() && pos->query == query) { - shared_ptr storedObj (pos->objRef.lock()); + P storedObj (pos->objRef.lock()); if (storedObj) return (storedObj == obj); else @@ -290,7 +291,7 @@ namespace mobject * @return false if the object wasn't registered at all. */ template - bool forget (const shared_ptr& obj) + bool forget (const P& obj) { typedef typename Slot::Registry Registry; typedef typename Record::Search SearchFunc; diff --git a/src/proc/mobject/session/mobjectfactory.hpp b/src/proc/mobject/session/mobjectfactory.hpp index cff884ca7..427385194 100644 --- a/src/proc/mobject/session/mobjectfactory.hpp +++ b/src/proc/mobject/session/mobjectfactory.hpp @@ -45,7 +45,7 @@ namespace mobject class Track; class Effect; - typedef shared_ptr PTrackAsset; + typedef P PTrackAsset; class MObjectFactory diff --git a/src/proc/mobject/session/track.hpp b/src/proc/mobject/session/track.hpp index 2b8f090b9..2b9801273 100644 --- a/src/proc/mobject/session/track.hpp +++ b/src/proc/mobject/session/track.hpp @@ -34,11 +34,13 @@ namespace mobject { namespace session { + using lumiera::P; + class Track; typedef asset::Track TrackAsset; - typedef shared_ptr PTrack; - typedef shared_ptr PTrackAsset; + typedef P PTrack; + typedef P PTrackAsset; /** diff --git a/tests/components/proc/mobject/session/defsregistryimpltest.cpp b/tests/components/proc/mobject/session/defsregistryimpltest.cpp index 0aae90b3a..e605880a1 100644 --- a/tests/components/proc/mobject/session/defsregistryimpltest.cpp +++ b/tests/components/proc/mobject/session/defsregistryimpltest.cpp @@ -27,6 +27,7 @@ #include "proc/mobject/session/defsregistry.hpp" #include "common/factory.hpp" #include "common/query.hpp" +#include "common/p.hpp" #include "../common/query/querydiagnostics.hpp" @@ -35,11 +36,11 @@ #include #include +using lumiera::P; using lumiera::Query; using lumiera::query::test::garbage_query; using util::isnil; -using std::tr1::shared_ptr; using boost::scoped_ptr; using boost::format; using std::string; @@ -75,7 +76,9 @@ namespace mobject { static string name; string instanceID; - operator string () const { return instanceID; } + operator string () const { return instanceID; } + bool operator== (const Dummy& odu) const { return this == &odu; } + Dummy () : instanceID (newID (name)) {} }; @@ -99,8 +102,8 @@ namespace mobject { scoped_ptr reg_; - typedef shared_ptr > O; - typedef shared_ptr > P; + typedef P > O; + typedef P > P; typedef Query > Q13; typedef Query > Q23; From d9e6adfe020797e4ae3a5e3b859816883b9d8f22 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 23 Apr 2008 05:48:45 +0200 Subject: [PATCH 02/11] make Placement implement the interface Buildable. WIP: works, but isnt what I intended. Rather need a specialisation to be able to define functions treat (Placement) based on the concrete TY.... --- src/common/visitor.hpp | 2 +- src/common/visitorpolicies.hpp | 2 +- src/proc/mobject/explicitplacement.hpp | 3 ++ src/proc/mobject/placement.hpp | 4 +- src/proc/mobject/session/locatingpin.hpp | 5 +- .../proc/mobject/builder/buildertooltest.cpp | 47 +++++++++++++++---- 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/common/visitor.hpp b/src/common/visitor.hpp index 837ddf713..3d22f11e6 100644 --- a/src/common/visitor.hpp +++ b/src/common/visitor.hpp @@ -105,7 +105,7 @@ namespace lumiera typedef RET ReturnType; ///< Tool function invocation return type typedef Tool ToolBase; ///< for templating the Tag and Dispatcher - virtual ~Tool () { }; ///< use RTTI for all visiting tools + virtual ~Tool () { }; ///< use RTTI for all visiting tools /** allows discovery of the concrete Tool type when dispatching a * visitor call. Can be implemented by inheriting from ToolTag */ diff --git a/src/common/visitorpolicies.hpp b/src/common/visitorpolicies.hpp index e59a2f5b2..829dd5546 100644 --- a/src/common/visitorpolicies.hpp +++ b/src/common/visitorpolicies.hpp @@ -23,7 +23,7 @@ /** @file visitorpolicies.hpp ** Policies usable for configuring the lumiera::visitor::Tool for different kinds of error handling. - ** @see buildertool.hpp for another flavor (calling and catch-all-function) + ** @see buildertool.hpp for another flavour (calling an catch-all-function there) ** */ diff --git a/src/proc/mobject/explicitplacement.hpp b/src/proc/mobject/explicitplacement.hpp index 388a0ac82..cb5e424b5 100644 --- a/src/proc/mobject/explicitplacement.hpp +++ b/src/proc/mobject/explicitplacement.hpp @@ -60,6 +60,9 @@ namespace mobject { return *this; } + + /** */ /////////////////////////////////////////////////////////////TODO: wip-wip... + DEFINE_PROCESSABLE_BY (builder::BuilderTool); protected: /* @todo ichthyo considers a much more elegant implementation utilizing a subclass diff --git a/src/proc/mobject/placement.hpp b/src/proc/mobject/placement.hpp index c5d56169d..7bd3a9ce4 100644 --- a/src/proc/mobject/placement.hpp +++ b/src/proc/mobject/placement.hpp @@ -82,7 +82,7 @@ namespace mobject * be within the Session/EDL */ template - class Placement : protected shared_ptr + class Placement : public Buildable, protected shared_ptr { protected: typedef lumiera::Time Time; @@ -105,7 +105,7 @@ namespace mobject virtual ~Placement() {}; /** */ /////////////////////////////////////////////////////////////TODO: totmachen? - // DEFINE_PROCESSABLE_BY (builder::BuilderTool); + DEFINE_PROCESSABLE_BY (builder::BuilderTool); /** interface for defining the kind of placement diff --git a/src/proc/mobject/session/locatingpin.hpp b/src/proc/mobject/session/locatingpin.hpp index f978d4f4e..ecd82ab30 100644 --- a/src/proc/mobject/session/locatingpin.hpp +++ b/src/proc/mobject/session/locatingpin.hpp @@ -111,9 +111,12 @@ namespace mobject virtual LocatingPin* clone () const; virtual ~LocatingPin() {}; - protected: +// protected: LocatingPin () {}; +//TODO (for working out the buildable interface; ctor should be protected) +protected: + friend class Placement; /** diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index 8a445ef18..584b41ea0 100644 --- a/tests/components/proc/mobject/builder/buildertooltest.cpp +++ b/tests/components/proc/mobject/builder/buildertooltest.cpp @@ -1,5 +1,5 @@ /* - BuilderTool(Test) - specialized form of the acyclic visitor + BuilderTool(Test) - specialized visitor used within the builder for processing Placements Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -27,6 +27,9 @@ #include "proc/asset/media.hpp" #include "proc/mobject/session/clip.hpp" +#include "proc/mobject/explicitplacement.hpp" //////////TODO + + #include using std::string; using std::cout; @@ -48,15 +51,31 @@ namespace mobject public: DummyMO() { }; virtual bool isValid() const { return true;} - DEFINE_PROCESSABLE_BY (BuilderTool); +// DEFINE_PROCESSABLE_BY (BuilderTool); + + static void killDummy (AbstractMO* dum) { delete (DummyMO*)dum; } }; - - class TestTool - : public Applicable::List> + + + //////////////////////////////////////////////////////TODO: wip-wip + + class DummyPlacement : public Placement { public: - void treat (Clip& c) { cout << "media is: "<< str(c.getMedia()) <<"\n"; } - void treat (AbstractMO&){ cout << "unspecific MO.\n"; } + DummyPlacement() + : Placement::Placement(*new DummyMO, &DummyMO::killDummy) + { } + }; + //////////////////////////////////////////////////////TODO: wip-wip + + + + class TestTool + : public Applicable, Placement >::List> + { + public: + void treat (Placement& pC) { cout << "media is: "<< str(pC->getMedia()) <<"\n"; } + void treat (Placement&){ cout << "unspecific MO.\n"; } void onUnknown (Buildable&){ cout << "catch-all-function called.\n"; } }; @@ -88,10 +107,10 @@ namespace mobject TestTool t1; BuilderTool& tool (t1); - DummyMO dumm; + DummyPlacement dumm; PMO clip = asset::Media::create("test-1", asset::VIDEO)->createClip(); - clip->apply (tool); + clip.apply (tool); dumm.apply (tool); } }; @@ -105,5 +124,15 @@ namespace mobject } // namespace test } // namespace builder + + //////////////////////////////////////////////////////TODO: wip-wip + template<> + ExplicitPlacement + Placement::resolve () const + { + UNIMPLEMENTED ("just a test"); + } + //////////////////////////////////////////////////////TODO: wip-wip + } // namespace mobject From 03047e6d176f4b24525c2eeb8d11fcae32c4fb18 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 26 Apr 2008 05:38:19 +0200 Subject: [PATCH 03/11] WIP maybe a solution for getting the correct wrapper within Builder Tools. works, but problem is: you need to know the exact type of the wrapper, e.g. Placement or shared_ptr --- src/proc/mobject/session/clip.hpp | 1 + src/tool/try.cpp | 11 ++ .../proc/mobject/builder/buildertooltest.cpp | 112 ++++++++++++++++-- 3 files changed, 114 insertions(+), 10 deletions(-) diff --git a/src/proc/mobject/session/clip.hpp b/src/proc/mobject/session/clip.hpp index cf15cbd27..7f1fe23c0 100644 --- a/src/proc/mobject/session/clip.hpp +++ b/src/proc/mobject/session/clip.hpp @@ -66,6 +66,7 @@ namespace mobject and the unlink() function of the asset should take it into account when breaking circular references. */ +public: ////////////////////////////////////TODO: temporarily for buildertooltest const Media & mediaDef_; const asset::Clip & clipDef_; diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 90bdfadf8..da5ca97c0 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -8,25 +8,36 @@ // 1/08 - working out a static initialisation problem for Visitor (Tag creation) // 1/08 - check 64bit longs // 4/08 - comparison operators on shared_ptr +// 4/08 - conversions on the value_type used for boost::any #include #include #include +#include using std::string; using std::cout; +using boost::any; +using boost::any_cast; +struct B {}; +struct D : B {}; int main (int argc, char* argv[]) { NOBUG_INIT; + D d; + D* pD =&d; + + any aD (pD); + any_cast (aD); cout << "\ngulp\n"; diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index 584b41ea0..995325025 100644 --- a/tests/components/proc/mobject/builder/buildertooltest.cpp +++ b/tests/components/proc/mobject/builder/buildertooltest.cpp @@ -29,7 +29,7 @@ #include "proc/mobject/explicitplacement.hpp" //////////TODO - +#include #include using std::string; using std::cout; @@ -45,8 +45,62 @@ namespace mobject using session::Clip; using session::AbstractMO; +/////////////////////////////////////////////////////TODO: move to buildertool.hpp + using boost::any; + using boost::any_cast; + +// Problem +/* + - brauche Laufzeittyp des Zielobjektes + - an wen wird die Dispatcher-Tabelle gebunden? + - falls an den Wrapper: Gefahr, zur Laufzeit nicht die Tabelle zu bekommen, in die das Trampolin registriert wurde + - falls an das Zielobjekt: wie gebe ich Referenz und konkreten Typ des Wrappers an den Funktionsaufruf im Tool? + - vieleicht einen allgemeinen Argument-Adapter nutzen? + - Zielobjekt oder Wrapper als Argumenttyp? +*/ - class DummyMO : public AbstractMO + class BuTuul + : public lumiera::visitor::Tool + { + any currentArgument_; + + public: + + void rememberWrapper (any argument) + { + currentArgument_ = argument; + } + + template + WRA& getCurrentArgumentWrapper () + { + WRA* argument = any_cast (currentArgument_); + ASSERT (argument); + return *argument; + } + }; + + + + + template + < class TOOLImpl, // concrete BuilderTool implementation + class TYPELIST // list of all concrete Buildables to be treated + > + class Appli + : public lumiera::visitor::Applicable + { } + ; + + using lumiera::typelist::Types; // convienience for the users of "Applicable" + + class BDable : public lumiera::visitor::Visitable + { + + }; +/////////////////////////////////////////////////////TODO: move to buildertool.hpp + + class DummyMO : public AbstractMO, public BDable { public: DummyMO() { }; @@ -54,8 +108,34 @@ namespace mobject // DEFINE_PROCESSABLE_BY (BuilderTool); static void killDummy (AbstractMO* dum) { delete (DummyMO*)dum; } + + virtual void + apply (BuTuul& tool) + { + return BDable::dispatchOp (*this, tool); + } + }; + class Clop : public Clip, public BDable + { + Clop (Clip& base) : Clip (base.clipDef_, base.mediaDef_) {} + + virtual void + apply (BuTuul& tool) + { + return BDable::dispatchOp (*this, tool); + } + }; + + + template + inline BDable::ReturnType + apply (BuTuul& tool, TAR& wrappedTargetObj) + { + tool.rememberWrapper(any (&wrappedTargetObj)); + wrappedTargetObj->apply (tool); + } //////////////////////////////////////////////////////TODO: wip-wip @@ -71,12 +151,23 @@ namespace mobject class TestTool - : public Applicable, Placement >::List> + : public Appli::List> { public: - void treat (Placement& pC) { cout << "media is: "<< str(pC->getMedia()) <<"\n"; } - void treat (Placement&){ cout << "unspecific MO.\n"; } - void onUnknown (Buildable&){ cout << "catch-all-function called.\n"; } + void treat (Clip& c) + { + Placement& pC = getCurrentArgumentWrapper >(); + cout << "media is: "<< str(pC->getMedia()) <<"\n"; + } + void treat (AbstractMO&) + { + Placement& placement = getCurrentArgumentWrapper(); + cout << "unspecific MO; Placement(adr) " << &placement <<"\n"; + } + void onUnknown (Buildable&) + { + cout << "catch-all-function called.\n"; + } }; @@ -105,13 +196,14 @@ namespace mobject { TestTool t1; - BuilderTool& tool (t1); + BuTuul& tool (t1); DummyPlacement dumm; - PMO clip = asset::Media::create("test-1", asset::VIDEO)->createClip(); + Placement clip = asset::Media::create("test-1", asset::VIDEO)->createClip(); - clip.apply (tool); - dumm.apply (tool); + apply (tool, clip); + cout << "Placement(adr) " << &dumm <<"\n"; + apply (tool, dumm); } }; From 85b0029166097ccf2317c13746ad813dfad11007 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 11 May 2008 15:44:24 +0200 Subject: [PATCH 04/11] WIP: how to get at the actual wrapper (here Placement) from within BuilderTool. Nasty problem ... draft solution using boost::variant. To be reconsidered later... --- .../proc/mobject/builder/buildertooltest.cpp | 130 +++++++++++++++--- 1 file changed, 110 insertions(+), 20 deletions(-) diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index 995325025..f9adc1308 100644 --- a/tests/components/proc/mobject/builder/buildertooltest.cpp +++ b/tests/components/proc/mobject/builder/buildertooltest.cpp @@ -27,9 +27,12 @@ #include "proc/asset/media.hpp" #include "proc/mobject/session/clip.hpp" +#include "proc/asset.hpp" +#include "common/p.hpp" +#include "proc/mobject/placement.hpp" #include "proc/mobject/explicitplacement.hpp" //////////TODO -#include +#include #include using std::string; using std::cout; @@ -37,6 +40,10 @@ using std::cout; namespace mobject { + + DEFINE_SPECIALIZED_PLACEMENT (session::AbstractMO); + + namespace builder { namespace test @@ -46,8 +53,8 @@ namespace mobject using session::AbstractMO; /////////////////////////////////////////////////////TODO: move to buildertool.hpp - using boost::any; - using boost::any_cast; + + using lumiera::P; // Problem /* @@ -58,29 +65,106 @@ namespace mobject - vieleicht einen allgemeinen Argument-Adapter nutzen? - Zielobjekt oder Wrapper als Argumenttyp? */ + struct Nothing {}; + /** + * helper to treat various sorts of smart-ptrs uniformly. + * Implemented as a variant-type value object, it is preconfigured + * with the possible hierarchy-base classes used within this application. + * Thus, when passing in an arbitrary smart-ptr, the best fitting smart-ptr + * type pointing to the corresponding base class is selected for internal storage. + * Later on, stored values can be retrieved either utilitzing static or dynamic casts; + * error reporting is similar to the bahaviour of dynamic_cast: when retrieving + * a pointer, in case of mismatch NULL is returned. + */ + class WrapperPtr // NONCOPYABLE!! + { + + template + struct accessor; + + template class WRA, class TAR> + struct accessor< WRA > + : public boost::static_visitor*> + { + template WRA* operator() (X&) { return 0; } + template WRA* operator() (WRA& stored) { return static_cast*> (stored); } + }; + + + private: + /** storage: buffer holding either and "empty" marker, + * or one of the configured pointer to wrapper types */ + boost::variant*, P*> holder_; + + public: + void reset () { holder_ = Nothing(); } + + template + WrapperPtr& + operator= (WRA* src) ///< store a ptr to the given wrapper, after casting to base + { + if (src) holder_ = src; + else holder_ = Nothing(); + return *this; + } + + template + WRA* + get () ///< retrieve ptr and try to downcast to type WRA + { + accessor acc; + WRA* res = boost::apply_visitor(acc, this->holder_); + return res; + } + }; + + class BuTuul : public lumiera::visitor::Tool { - any currentArgument_; + WrapperPtr currentWrapper_; public: - void rememberWrapper (any argument) + template class WRA, class TAR> + void rememberWrapper (WRA* argument) { - currentArgument_ = argument; + currentWrapper_ = argument; } - template - WRA& getCurrentArgumentWrapper () + void forgetWrapper () { - WRA* argument = any_cast (currentArgument_); - ASSERT (argument); - return *argument; + currentWrapper_.reset(); + } + + + template + Placement& + getPlacement () + { + Placement* pPlacement = currentWrapper_.get >(); + return *pPlacement; + } + ExplicitPlacement + getExplicitPlacement () + { + return getPlacement().resolve(); + } + template + P + getPtr () + { + P* pP = currentWrapper_.get >(); + return *pP; } }; + + + + template @@ -107,7 +191,7 @@ namespace mobject virtual bool isValid() const { return true;} // DEFINE_PROCESSABLE_BY (BuilderTool); - static void killDummy (AbstractMO* dum) { delete (DummyMO*)dum; } + static void killDummy (MObject* dum) { delete (DummyMO*)dum; } virtual void apply (BuTuul& tool) @@ -129,12 +213,15 @@ namespace mobject }; - template + template inline BDable::ReturnType - apply (BuTuul& tool, TAR& wrappedTargetObj) + apply (BuTuul& tool, WRA& wrappedTargetObj) { - tool.rememberWrapper(any (&wrappedTargetObj)); + WRA* ptr = &wrappedTargetObj; + (*ptr)->isValid(); + tool.rememberWrapper(ptr); wrappedTargetObj->apply (tool); + tool.forgetWrapper(); } //////////////////////////////////////////////////////TODO: wip-wip @@ -156,12 +243,12 @@ namespace mobject public: void treat (Clip& c) { - Placement& pC = getCurrentArgumentWrapper >(); + Placement& pC = getPlacement(); cout << "media is: "<< str(pC->getMedia()) <<"\n"; } void treat (AbstractMO&) { - Placement& placement = getCurrentArgumentWrapper(); + Placement& placement = getPlacement(); cout << "unspecific MO; Placement(adr) " << &placement <<"\n"; } void onUnknown (Buildable&) @@ -199,11 +286,14 @@ namespace mobject BuTuul& tool (t1); DummyPlacement dumm; + Placement& dummy(dumm); + Placement clip = asset::Media::create("test-1", asset::VIDEO)->createClip(); + apply (tool, clip); cout << "Placement(adr) " << &dumm <<"\n"; - apply (tool, dumm); + apply (tool, dummy); } }; @@ -218,13 +308,13 @@ namespace mobject } // namespace builder //////////////////////////////////////////////////////TODO: wip-wip - template<> +/* template<> ExplicitPlacement Placement::resolve () const { UNIMPLEMENTED ("just a test"); } - //////////////////////////////////////////////////////TODO: wip-wip +*/ //////////////////////////////////////////////////////TODO: wip-wip } // namespace mobject From 88fc2f6099cc1be48de1ab94d079f5e2ff41a486 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 17 May 2008 04:34:46 +0200 Subject: [PATCH 05/11] WIP reworked to replace boost::variant by a custom solution Not a big simplification, but at least the actual codepath is shorter, while it's not so general as boost::variant (and not threadsafe!) --- src/common/typelistutil.hpp | 2 +- src/tool/try.cpp | 212 +++++++++- .../proc/mobject/builder/buildertooltest.cpp | 400 +++++++++++++++++- 3 files changed, 588 insertions(+), 26 deletions(-) diff --git a/src/common/typelistutil.hpp b/src/common/typelistutil.hpp index 4e382df3d..f7a716024 100644 --- a/src/common/typelistutil.hpp +++ b/src/common/typelistutil.hpp @@ -136,7 +136,7 @@ namespace lumiera > { public: - typedef InstantiateChained Next; + typedef InstantiateChained Next; typedef _X_ Unit; }; diff --git a/src/tool/try.cpp b/src/tool/try.cpp index da5ca97c0..85a3286a0 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -9,35 +9,235 @@ // 1/08 - check 64bit longs // 4/08 - comparison operators on shared_ptr // 4/08 - conversions on the value_type used for boost::any +// 5/08 - how to guard a downcasting access, so it is compiled in only if the involved types are convertible #include #include #include -#include +#include +#include +#include +#include +#include +#include using std::string; using std::cout; +using std::ostream; + + + using boost::remove_pointer; + using boost::remove_reference; + using boost::is_convertible; + using boost::is_polymorphic; + using boost::is_base_of; + using boost::enable_if; + + template + struct can_cast : boost::false_type {}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + + template + struct has_RTTI + { + typedef typename remove_pointer< + typename remove_reference::type>::type TPlain; + + enum { value = is_polymorphic::value }; + }; + + template + struct use_dynamic_downcast + { + enum { value = can_cast::value + && has_RTTI::value + && has_RTTI::value + }; + }; + + template + struct use_static_downcast + { + enum { value = can_cast::value + && ( !has_RTTI::value + || !has_RTTI::value + ) + }; + }; + + template + struct use_conversion + { + enum { value = is_convertible::value + && !( use_static_downcast::value + ||use_dynamic_downcast::value + ) + }; + }; + + + template + struct EmptyVal + { + static X create() + { + cout << " NULL() " << __PRETTY_FUNCTION__ <<"\n"; + return X(); + } + }; + template + struct EmptyVal + { + static X*& create() + { + cout << " NULL & " << __PRETTY_FUNCTION__ <<"\n"; + static X* null(0); + return null; + } + }; + + + template + struct NullAccessor + { + typedef RET Ret; + + static RET access (...) { return ifEmpty(); } + static RET ifEmpty () { return EmptyVal::create(); } + }; + + template + struct AccessCasted : NullAccessor + { + using NullAccessor::access; + + template + static typename enable_if< use_dynamic_downcast, TAR>::type + access (ELM& elem) + { + cout << " dynamic " << __PRETTY_FUNCTION__ <<"\n"; + return dynamic_cast (elem); + } + + template + static typename enable_if< use_static_downcast, TAR>::type + access (ELM& elem) + { + cout << " static " << __PRETTY_FUNCTION__ <<"\n"; + return static_cast (elem); + } + + template + static typename enable_if< use_conversion, TAR>::type + access (ELM& elem) + { + cout << " convert " << __PRETTY_FUNCTION__ <<"\n"; + return elem; + } + + }; + -using boost::any; -using boost::any_cast; struct B {}; struct D : B {}; -int main (int argc, char* argv[]) +struct E : D + { + virtual ~E() {}; + }; +struct F : E {}; + + +ostream& operator<< (ostream& s, const B& b) { return s << "B{} adr="<<&b; } +ostream& operator<< (ostream& s, const D& d) { return s << "D{} adr="<<&d; } +ostream& operator<< (ostream& s, const E& e) { return s << "E{} adr="<<&e; } +ostream& operator<< (ostream& s, const F& f) { return s << "F{} adr="<<&f; } + +int +main (int argc, char* argv[]) { NOBUG_INIT; D d; D* pD =&d; + B* pB =pD; + D& rD = *pD; + B& rB = *pB; + + D*& rpD = pD; + B*& rpB = pB; + + F f; + E& rE = f; + E* pE = &f; + D* pDE = pE; - any aD (pD); - any_cast (aD); + cout << "is_base_of = " << is_base_of::value << "\n"; + cout << "is_base_of = " << is_base_of::value << "\n"; + cout << "is_base_of = " << is_base_of::value << "\n"; + + cout << "can_cast = " << can_cast::value << "\n"; + cout << "can_cast = " << can_cast::value << "\n"; + cout << "can_cast = " << can_cast::value << "\n"; + cout << "can_cast = " << can_cast::value << "\n"; + cout << "can_cast = " << can_cast::value << "\n"; + cout << "can_cast = " << can_cast::value << "\n"; + cout << "can_cast = " << can_cast::value << "\n"; + + cout << "can_cast = " << can_cast::value << "\n"; + cout << "can_cast = " << can_cast::value << "\n"; + + cout << "has_RTTI = " << has_RTTI::value << "\n"; + cout << "has_RTTI = " << has_RTTI::value << "\n"; + cout << "has_RTTI = " << has_RTTI::value << "\n"; + + + cout << "use_dynamic_downcast = " << use_dynamic_downcast::value << "\n"; + cout << "use_static_downcast = " << use_static_downcast::value << "\n"; + cout << "use_conversion = " << use_conversion::value << "\n"; + + cout << "use_static_downcast = " << use_static_downcast::value << "\n"; + cout << "use_static_downcast = " << use_static_downcast::value << "\n"; + cout << "use_dynamic_downcast = " << use_dynamic_downcast::value << "\n"; + + + cout << "Access(D as D&) --->" << AccessCasted::access(d) << "\n"; + cout << "Access(D& as D&) --->" << AccessCasted::access(rD) << "\n"; + cout << "Access(B& as D&) --->" << AccessCasted::access(rB) << "\n"; + cout << "Access(D* as D*) --->" << AccessCasted::access(pD) << "\n"; + cout << "Access(B* as D*) --->" << AccessCasted::access(pB) << "\n"; + cout << "Access(D*& as D*&) --->" << AccessCasted::access(rpD) << "\n"; + cout << "Access(B*& as D*&) --->" << AccessCasted::access(rpB) << "\n"; + + cout << "Access(D as B&) --->" << AccessCasted::access(d) << "\n"; + cout << "Access(D& as B&) --->" << AccessCasted::access(rD) << "\n"; + cout << "Access(B& as B&) --->" << AccessCasted::access(rB) << "\n"; + cout << "Access(D* as B*) --->" << AccessCasted::access(pD) << "\n"; + cout << "Access(B* as B*) --->" << AccessCasted::access(pB) << "\n"; + cout << "Access(D*& as B*&) --->" << AccessCasted::access(rpD) << "\n"; + cout << "Access(B*& as B*&) --->" << AccessCasted::access(rpB) << "\n"; + + cout << "Access(D as E&) --->" << AccessCasted::access(d) << "\n"; + cout << "Access(E& as F&) --->" << AccessCasted::access(rE) << "\n"; + cout << "Access(D(E)* as E*) --->" << AccessCasted::access(pDE) << "\n"; + cout << "Access(D(E)* as F*) --->" << AccessCasted::access(pDE) << "\n"; + cout << "Access(E* as F*) --->" << AccessCasted::access(pE) << "\n"; + cout << "\ngulp\n"; diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index f9adc1308..6495bb795 100644 --- a/tests/components/proc/mobject/builder/buildertooltest.cpp +++ b/tests/components/proc/mobject/builder/buildertooltest.cpp @@ -32,7 +32,15 @@ #include "proc/mobject/placement.hpp" #include "proc/mobject/explicitplacement.hpp" //////////TODO -#include +#include "common/typelistutil.hpp" + +#include +#include +#include +#include +#include + +//#include #include using std::string; using std::cout; @@ -65,8 +73,359 @@ namespace mobject - vieleicht einen allgemeinen Argument-Adapter nutzen? - Zielobjekt oder Wrapper als Argumenttyp? */ - struct Nothing {}; + + using boost::enable_if; + using boost::is_convertible; + + using lumiera::typelist::Types; + using lumiera::typelist::Node; + using lumiera::typelist::NullType; +// using lumiera::typelist::count; +// using lumiera::typelist::maxSize; + using lumiera::typelist::InstantiateChained; + + template + < class TYPES // List of Types + , template class _X_ // your-template-goes-here + , class BASE = NullType // Base class at end of chain + , uint i = 0 // incremented on each instantiaton + > + class InstantiateWithIndex; + + + template< template class _X_ + , class BASE + , uint i + > + class InstantiateWithIndex + : public BASE + { + public: + typedef BASE Unit; + typedef NullType Next; + enum{ COUNT = i }; + }; + + template + < class TY, typename TYPES + , template class _X_ + , class BASE + , uint i + > + class InstantiateWithIndex, _X_, BASE, i> + : public _X_< TY + , InstantiateWithIndex + , i + > + { + public: + typedef InstantiateWithIndex Next; + typedef _X_ Unit; + enum{ COUNT = Next::COUNT }; + }; + + template + struct count; + template<> + struct count + { + enum{ value = 0 }; + }; + template + struct count > + { + enum{ value = 1 + count::value }; + }; + + template + struct maxSize; + template<> + struct maxSize + { + enum{ value = 0 }; + }; + template + struct maxSize > + { + enum{ nextval = maxSize::value + , thisval = sizeof(TY) + , value = nextval > thisval? nextval:thisval + }; + }; + + + + template + struct Holder + { + T content; + + Holder (T const& src) : T(src) {} + + template + TAR get () { return static_cast (content); } + }; + + typedef Types < Placement* + , P* + > ::List + WrapperTypes; + + template + struct EmptyVal + { + static X create() + { + return X(); + } + }; + template + struct EmptyVal + { + static X*& create() + { + static X* null(0); + return null; + } + }; + + + const uint TYPECNT = count::value; + const size_t SIZE = maxSize::value; + + struct Buffer + { + char buffer_[SIZE]; + uint which; + + Buffer() : which(TYPECNT) {} + + void* + put (void) + { + deleteCurrent(); + return 0; + } + + void + deleteCurrent (); // depends on the Deleter, see below + }; + + template + struct Storage : BASE + { + T& + put (T const& toStore) + { + BASE::deleteCurrent(); // remove old content, if any + + T& storedObj = *new(BASE::buffer_) T (toStore); + this->which = idx; // remember the actual type selected + return storedObj; + } + + using BASE::put; + }; + + + + template + struct CaseSelect + { + typedef typename FUNCTOR::Ret Ret; + typedef Ret (*Func)(Buffer&); + + Func table_[TYPECNT]; + + CaseSelect () + { + for (uint i=0; i + static Ret + trampoline (Buffer& storage) + { + T& content = reinterpret_cast (storage.buffer_); + return FUNCTOR::access (content); + } + + template + void + create_thunk (uint idx) + { + Func thunk = &trampoline; + table_[idx] = thunk; + } + + Ret + invoke (Buffer& storage) + { + if (TYPECNT <= storage.which) + return FUNCTOR::ifEmpty (); + else + return (*table_[storage.which]) (storage); + } + }; + + + template< class T, class BASE, uint i > + struct CasePrepare + : BASE + { + CasePrepare () : BASE() + { +// BASE::table_[i] = &(BASE::template trampoline); + BASE::template create_thunk(i); + } + }; + + + template + typename FUNCTOR::Ret + access (Buffer& buf) + { + typedef InstantiateWithIndex< WrapperTypes + , CasePrepare + , CaseSelect + > + Accessor; + static Accessor select_case; + return select_case.invoke(buf); + } + + + struct Deleter + { + typedef void Ret; + + template + static void access (T& elem) { elem.~T(); } + + static void ifEmpty () { } + }; + + + void + Buffer::deleteCurrent () + { + access(*this); // remove old content, if any + which = TYPECNT; // mark as empty + } + + + + + + + using boost::remove_pointer; + using boost::remove_reference; + using boost::is_convertible; + using boost::is_polymorphic; + using boost::is_base_of; + using boost::enable_if; + + template + struct can_cast : boost::false_type {}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + + template + struct has_RTTI + { + typedef typename remove_pointer< + typename remove_reference::type>::type TPlain; + + enum { value = is_polymorphic::value }; + }; + + template + struct use_dynamic_downcast + { + enum { value = can_cast::value + && has_RTTI::value + && has_RTTI::value + }; + }; + + template + struct use_static_downcast + { + enum { value = can_cast::value + && ( !has_RTTI::value + || !has_RTTI::value + ) + }; + }; + + template + struct use_conversion + { + enum { value = is_convertible::value + && !( use_static_downcast::value + ||use_dynamic_downcast::value + ) + }; + }; + + + + + + template + struct NullAccessor + { + typedef RET Ret; + + static RET access (...) { return ifEmpty(); } + static RET ifEmpty () { return EmptyVal::create(); } + }; + + template + struct AccessCasted : NullAccessor + { + using NullAccessor::access; + + template + static typename enable_if< use_dynamic_downcast, TAR>::type + access (ELM& elem) + { + return dynamic_cast (elem); + } + + template + static typename enable_if< use_static_downcast, TAR>::type + access (ELM& elem) + { + return static_cast (elem); + } + + template + static typename enable_if< use_conversion, TAR>::type + access (ELM& elem) + { + return elem; + } + + }; + + + + + + + + /** * helper to treat various sorts of smart-ptrs uniformly. * Implemented as a variant-type value object, it is preconfigured @@ -80,32 +439,33 @@ namespace mobject class WrapperPtr // NONCOPYABLE!! { - template - struct accessor; - - template class WRA, class TAR> - struct accessor< WRA > - : public boost::static_visitor*> - { - template WRA* operator() (X&) { return 0; } - template WRA* operator() (WRA& stored) { return static_cast*> (stored); } - }; private: + typedef InstantiateWithIndex< WrapperTypes + , Storage + , Buffer + > + VariantHolder; + /** storage: buffer holding either and "empty" marker, * or one of the configured pointer to wrapper types */ - boost::variant*, P*> holder_; + VariantHolder holder_; public: - void reset () { holder_ = Nothing(); } + void + reset () + { + access (holder_); + holder_.which = TYPECNT; + } template WrapperPtr& operator= (WRA* src) ///< store a ptr to the given wrapper, after casting to base { - if (src) holder_ = src; - else holder_ = Nothing(); + if (src) holder_.put (src); + else reset(); return *this; } @@ -113,8 +473,8 @@ namespace mobject WRA* get () ///< retrieve ptr and try to downcast to type WRA { - accessor acc; - WRA* res = boost::apply_visitor(acc, this->holder_); + typedef AccessCasted AccessWraP; + WRA* res = access(this->holder_); return res; } }; @@ -244,7 +604,8 @@ namespace mobject void treat (Clip& c) { Placement& pC = getPlacement(); - cout << "media is: "<< str(pC->getMedia()) <<"\n"; + cout << "media is: "<< str(pC->getMedia()) <<"\n" + << "Placement(adr) " << &pC <<"\n"; } void treat (AbstractMO&) { @@ -291,6 +652,7 @@ namespace mobject Placement clip = asset::Media::create("test-1", asset::VIDEO)->createClip(); + cout << "Placement(adr) " << &clip <<"\n"; apply (tool, clip); cout << "Placement(adr) " << &dumm <<"\n"; apply (tool, dummy); From 662678f8d14ce3e68e7ba75117dec4281798221c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 18 May 2008 05:08:11 +0200 Subject: [PATCH 06/11] WIP refactored in preparation of splitting into several lib modules --- .../proc/mobject/builder/buildertooltest.cpp | 711 +++++++++--------- 1 file changed, 372 insertions(+), 339 deletions(-) diff --git a/tests/components/proc/mobject/builder/buildertooltest.cpp b/tests/components/proc/mobject/builder/buildertooltest.cpp index 6495bb795..7a427f51c 100644 --- a/tests/components/proc/mobject/builder/buildertooltest.cpp +++ b/tests/components/proc/mobject/builder/buildertooltest.cpp @@ -34,55 +34,137 @@ #include "common/typelistutil.hpp" +#include #include #include #include #include #include -//#include #include using std::string; using std::cout; -namespace mobject - { +namespace util { + using boost::remove_pointer; + using boost::remove_reference; + using boost::is_convertible; + using boost::is_polymorphic; + using boost::is_base_of; + using boost::enable_if; - DEFINE_SPECIALIZED_PLACEMENT (session::AbstractMO); + template + struct can_cast : boost::false_type {}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + template + struct can_cast { enum { value = is_base_of::value };}; - namespace builder + template + struct has_RTTI { - namespace test - { - - using session::Clip; - using session::AbstractMO; - -/////////////////////////////////////////////////////TODO: move to buildertool.hpp - - using lumiera::P; - -// Problem -/* - - brauche Laufzeittyp des Zielobjektes - - an wen wird die Dispatcher-Tabelle gebunden? - - falls an den Wrapper: Gefahr, zur Laufzeit nicht die Tabelle zu bekommen, in die das Trampolin registriert wurde - - falls an das Zielobjekt: wie gebe ich Referenz und konkreten Typ des Wrappers an den Funktionsaufruf im Tool? - - vieleicht einen allgemeinen Argument-Adapter nutzen? - - Zielobjekt oder Wrapper als Argumenttyp? -*/ - - using boost::enable_if; - using boost::is_convertible; + typedef typename remove_pointer< + typename remove_reference::type>::type TPlain; - using lumiera::typelist::Types; - using lumiera::typelist::Node; - using lumiera::typelist::NullType; -// using lumiera::typelist::count; -// using lumiera::typelist::maxSize; - using lumiera::typelist::InstantiateChained; + enum { value = is_polymorphic::value }; + }; + + template + struct use_dynamic_downcast + { + enum { value = can_cast::value + && has_RTTI::value + && has_RTTI::value + }; + }; + + template + struct use_static_downcast + { + enum { value = can_cast::value + && ( !has_RTTI::value + || !has_RTTI::value + ) + }; + }; + + template + struct use_conversion + { + enum { value = is_convertible::value + && !( use_static_downcast::value + ||use_dynamic_downcast::value + ) + }; + }; + + + + template + struct EmptyVal + { + static X create() { return X(); } + }; + template + struct EmptyVal + { + static X*& create() { static X* nullP(0); return nullP; } + }; + + + + + + template + struct NullAccessor + { + typedef RET Ret; + + static RET access (...) { return ifEmpty(); } + static RET ifEmpty () { return EmptyVal::create(); } + }; + + template + struct AccessCasted : NullAccessor + { + using NullAccessor::access; + + template + static typename enable_if< use_dynamic_downcast, TAR>::type + access (ELM& elem) + { + return dynamic_cast (elem); + } + + template + static typename enable_if< use_static_downcast, TAR>::type + access (ELM& elem) + { + return static_cast (elem); + } + + template + static typename enable_if< use_conversion, TAR>::type + access (ELM& elem) + { + return elem; + } + }; + + +} + +namespace lumiera { + + namespace typelist { + template < class TYPES // List of Types @@ -124,7 +206,7 @@ namespace mobject typedef _X_ Unit; enum{ COUNT = Next::COUNT }; }; - + template struct count; template<> @@ -153,279 +235,270 @@ namespace mobject , value = nextval > thisval? nextval:thisval }; }; + + } // namespace typelist - - - template + + + + + namespace variant { + + using lumiera::typelist::count; + using lumiera::typelist::maxSize; + using lumiera::typelist::InstantiateWithIndex; + + /** + * internal helper used to build a variant storage wrapper. + * Parametrized with a collection of types, it provides functionality + * to copy a value of one of these types into an internal buffer, while + * remembering which of these types was used to place this copy. + * The value can be later on extracted using a visitation like mechanism, + * which takes a functor object and invokes a function \c access(T&) with + * the type matching the current value in storage + */ + template struct Holder { - T content; - Holder (T const& src) : T(src) {} + enum { TYPECNT = count::value + , SIZE = maxSize::value + }; - template - TAR get () { return static_cast (content); } + + /** Storage to hold the actual value */ + struct Buffer + { + char buffer_[SIZE]; + uint which; + + Buffer() : which(TYPECNT) {} + + void* + put (void) + { + deleteCurrent(); + return 0; + } + + void + deleteCurrent (); // depends on the Deleter, see below + }; + + template + struct PlacementAdapter : BASE + { + T& + put (T const& toStore) + { + BASE::deleteCurrent(); // remove old content, if any + + T& storedObj = *new(BASE::buffer_) T (toStore); + this->which = idx; // remember the actual type selected + return storedObj; + } + + using BASE::put; + }; + + typedef InstantiateWithIndex< TYPES + , PlacementAdapter + , Buffer + > + Storage; + + + + /** provide a dispatcher table based visitation mechanism */ + template + struct CaseSelect + { + typedef typename FUNCTOR::Ret Ret; + typedef Ret (*Func)(Buffer&); + + Func table_[TYPECNT]; + + CaseSelect () + { + for (uint i=0; i + static Ret + trampoline (Buffer& storage) + { + T& content = reinterpret_cast (storage.buffer_); + return FUNCTOR::access (content); + } + + Ret + invoke (Buffer& storage) + { + if (TYPECNT <= storage.which) + return FUNCTOR::ifEmpty (); + else + return (*table_[storage.which]) (storage); + } + }; + + + template< class T, class BASE, uint i > + struct CasePrepare + : BASE + { + CasePrepare () : BASE() + { + BASE::table_[i] = &BASE::template trampoline; + } + }; + + + template + static typename FUNCTOR::Ret + access (Buffer& buf) + { + typedef InstantiateWithIndex< TYPES + , CasePrepare + , CaseSelect + > + Accessor; + static Accessor select_case; + return select_case.invoke(buf); + } + + + struct Deleter + { + typedef void Ret; + + template + static void access (T& elem) { elem.~T(); } + + static void ifEmpty () { } + }; }; + + + template + void + Holder::Buffer::deleteCurrent () + { + access(*this); // remove old content, if any + which = TYPECNT; // mark as empty + } + + } // namespace variant + + + + + + + + + + + + /** + * A variant wrapper (typesafe union) capable of holding a value of any + * of a bounded collection of types. The value is stored in a local buffer + * directly within the object and may be accessed by a typesafe visitation. + * + * \par + * This utility class is similar to boost::variant and indeed was implemented + * (5/08) in an effort to replace the latter in a draft solution for the problem + * of typesafe access to the correct wrapper class from within some builder tool. + * Well -- after finisihng this "exercise" I must admit that it is not really + * much more simple than what boost::variant does internally. At least we are + * pulling in fewer headers and the actual code path is shorter compared with + * boost::variant, at the price of beeing not so generic, not caring for + * alignment issues within the buffer and being not threadsafe + * + * @param TYPES collection of possible types to be stored in this variant object + * @param Access policy how to access the stored value + */ + template< typename TYPES + , template class Access = util::AccessCasted + > + class Variant + : boost::noncopyable + { + + typedef variant::Holder Holder; + typedef typename Holder::Deleter Deleter; + + + /** storage: buffer holding either and "empty" marker, + * or one of the configured pointer to wrapper types */ + typename Holder::Storage holder_; + + + public: + void reset () { holder_.deleteCurrent();} + + /** store a copy of the given argument within the + * variant holder buffer, thereby typically casting + * or converting the given source type to the best + * suited (base) type (out of the collection of possible + * types for this Variant instance) + */ + template + Variant& + operator= (SRC src) + { + if (src) holder_.put (src); // see Holder::PlacementAdaptor::put + else reset(); + return *this; + } + + /** retrieve current content of the variant, + * trying to cast or convert it to the given type. + * Actually, the function \c access(T&) on the + * Access-policy (template param) is invoked with the + * type currently stored in the holder buffer. + * May return NULL if conversion fails. + */ + template + TAR + get () + { + typedef Access Extractor; + return Holder::template access (this->holder_); + } + }; + +} // namespace lumiera + + + +namespace mobject + { + + DEFINE_SPECIALIZED_PLACEMENT (session::AbstractMO); + + + namespace builder + { + namespace test + { + + using session::Clip; + using session::AbstractMO; + +/////////////////////////////////////////////////////TODO: move to buildertool.hpp + + using lumiera::P; + using lumiera::typelist::Types; + + + typedef Types < Placement* , P* > ::List WrapperTypes; - - template - struct EmptyVal - { - static X create() - { - return X(); - } - }; - template - struct EmptyVal - { - static X*& create() - { - static X* null(0); - return null; - } - }; - - const uint TYPECNT = count::value; - const size_t SIZE = maxSize::value; - - struct Buffer - { - char buffer_[SIZE]; - uint which; - - Buffer() : which(TYPECNT) {} - - void* - put (void) - { - deleteCurrent(); - return 0; - } - - void - deleteCurrent (); // depends on the Deleter, see below - }; - - template - struct Storage : BASE - { - T& - put (T const& toStore) - { - BASE::deleteCurrent(); // remove old content, if any - - T& storedObj = *new(BASE::buffer_) T (toStore); - this->which = idx; // remember the actual type selected - return storedObj; - } - - using BASE::put; - }; - - - - template - struct CaseSelect - { - typedef typename FUNCTOR::Ret Ret; - typedef Ret (*Func)(Buffer&); - - Func table_[TYPECNT]; - - CaseSelect () - { - for (uint i=0; i - static Ret - trampoline (Buffer& storage) - { - T& content = reinterpret_cast (storage.buffer_); - return FUNCTOR::access (content); - } - - template - void - create_thunk (uint idx) - { - Func thunk = &trampoline; - table_[idx] = thunk; - } - - Ret - invoke (Buffer& storage) - { - if (TYPECNT <= storage.which) - return FUNCTOR::ifEmpty (); - else - return (*table_[storage.which]) (storage); - } - }; - - - template< class T, class BASE, uint i > - struct CasePrepare - : BASE - { - CasePrepare () : BASE() - { -// BASE::table_[i] = &(BASE::template trampoline); - BASE::template create_thunk(i); - } - }; - - - template - typename FUNCTOR::Ret - access (Buffer& buf) - { - typedef InstantiateWithIndex< WrapperTypes - , CasePrepare - , CaseSelect - > - Accessor; - static Accessor select_case; - return select_case.invoke(buf); - } - - - struct Deleter - { - typedef void Ret; - - template - static void access (T& elem) { elem.~T(); } - - static void ifEmpty () { } - }; - - - void - Buffer::deleteCurrent () - { - access(*this); // remove old content, if any - which = TYPECNT; // mark as empty - } - - - - - - - using boost::remove_pointer; - using boost::remove_reference; - using boost::is_convertible; - using boost::is_polymorphic; - using boost::is_base_of; - using boost::enable_if; - - template - struct can_cast : boost::false_type {}; - - template - struct can_cast { enum { value = is_base_of::value };}; - - template - struct can_cast { enum { value = is_base_of::value };}; - - template - struct can_cast { enum { value = is_base_of::value };}; - - - template - struct has_RTTI - { - typedef typename remove_pointer< - typename remove_reference::type>::type TPlain; - - enum { value = is_polymorphic::value }; - }; - - template - struct use_dynamic_downcast - { - enum { value = can_cast::value - && has_RTTI::value - && has_RTTI::value - }; - }; - - template - struct use_static_downcast - { - enum { value = can_cast::value - && ( !has_RTTI::value - || !has_RTTI::value - ) - }; - }; - - template - struct use_conversion - { - enum { value = is_convertible::value - && !( use_static_downcast::value - ||use_dynamic_downcast::value - ) - }; - }; - - - - - - template - struct NullAccessor - { - typedef RET Ret; - - static RET access (...) { return ifEmpty(); } - static RET ifEmpty () { return EmptyVal::create(); } - }; - - template - struct AccessCasted : NullAccessor - { - using NullAccessor::access; - - template - static typename enable_if< use_dynamic_downcast, TAR>::type - access (ELM& elem) - { - return dynamic_cast (elem); - } - - template - static typename enable_if< use_static_downcast, TAR>::type - access (ELM& elem) - { - return static_cast (elem); - } - - template - static typename enable_if< use_conversion, TAR>::type - access (ELM& elem) - { - return elem; - } - - }; - - - - - - - - /** * helper to treat various sorts of smart-ptrs uniformly. * Implemented as a variant-type value object, it is preconfigured @@ -436,48 +509,8 @@ namespace mobject * error reporting is similar to the bahaviour of dynamic_cast: when retrieving * a pointer, in case of mismatch NULL is returned. */ - class WrapperPtr // NONCOPYABLE!! - { - - - - private: - typedef InstantiateWithIndex< WrapperTypes - , Storage - , Buffer - > - VariantHolder; - - /** storage: buffer holding either and "empty" marker, - * or one of the configured pointer to wrapper types */ - VariantHolder holder_; - - public: - void - reset () - { - access (holder_); - holder_.which = TYPECNT; - } - - template - WrapperPtr& - operator= (WRA* src) ///< store a ptr to the given wrapper, after casting to base - { - if (src) holder_.put (src); - else reset(); - return *this; - } - - template - WRA* - get () ///< retrieve ptr and try to downcast to type WRA - { - typedef AccessCasted AccessWraP; - WRA* res = access(this->holder_); - return res; - } - }; + typedef lumiera::Variant WrapperPtr; + class BuTuul @@ -503,7 +536,7 @@ namespace mobject Placement& getPlacement () { - Placement* pPlacement = currentWrapper_.get >(); + Placement* pPlacement = currentWrapper_.get*>(); return *pPlacement; } ExplicitPlacement @@ -515,7 +548,7 @@ namespace mobject P getPtr () { - P* pP = currentWrapper_.get >(); + P* pP = currentWrapper_.get*>(); return *pP; } }; From 86162ad314d291f7b190fa17a64b2dc584255067 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 19 May 2008 08:38:13 +0200 Subject: [PATCH 07/11] moved new code into library modules and adapted BuilderTool to use it. BuilderTool_test now passed. --- src/common/accesscasted.hpp | 168 +++++ src/common/typelistutil.hpp | 93 ++- src/common/variant.hpp | 276 +++++++ src/common/wrapperptr.hpp | 63 ++ .../builder/applicablebuildertargettypes.hpp | 70 ++ src/proc/mobject/builder/buildertool.hpp | 134 +++- src/proc/mobject/builder/nodecreatertool.cpp | 7 +- src/proc/mobject/builder/nodecreatertool.hpp | 23 +- src/proc/mobject/builder/segmentationtool.cpp | 6 +- src/proc/mobject/builder/segmentationtool.hpp | 22 +- src/proc/mobject/explicitplacement.hpp | 18 +- src/proc/mobject/mobject.hpp | 5 +- src/proc/mobject/placement.cpp | 32 +- src/proc/mobject/placement.hpp | 21 +- src/proc/mobject/session/clip.hpp | 4 +- src/proc/mobject/session/effect.hpp | 2 +- src/proc/mobject/session/meta.hpp | 2 +- src/proc/mobject/session/track.hpp | 2 +- tests/54builder.tests | 8 +- .../proc/mobject/builder/buildertooltest.cpp | 706 +++--------------- 20 files changed, 953 insertions(+), 709 deletions(-) create mode 100644 src/common/accesscasted.hpp create mode 100644 src/common/variant.hpp create mode 100644 src/common/wrapperptr.hpp create mode 100644 src/proc/mobject/builder/applicablebuildertargettypes.hpp diff --git a/src/common/accesscasted.hpp b/src/common/accesscasted.hpp new file mode 100644 index 000000000..58f2e2dbc --- /dev/null +++ b/src/common/accesscasted.hpp @@ -0,0 +1,168 @@ +/* + ACCESSCASTED.hpp - util template to access a value using conversion or cast as appropriate + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 accesscasted.hpp + ** Helper for accessing a value employing either conversion or downcast + ** depending on the relation of the source type (type of the original value) + ** and the target type (type we need within the usage context). + ** When instantiating AcessCasted, we get a template static function + ** \c AcessCasted::access(SRC& elm), but the actual implementation + ** is choosed using boost::type_traits. If no viable implementatino can be + ** selected, \c EmptyVal::create() is invoked instead, which by default + ** creates a NULL value or similar by using the no-argument ctor of the + ** type TAR. Alternatively, you may define an specialisation fo EmptyVal, + ** e.g. throwing an exception instead of creating a NULL value. + ** + ** @see lumiera::WrapperPtr usage example to access a variant record + ** + */ + + +#ifndef UTIL_ACCESSCASTED_H +#define UTIL_ACCESSCASTED_H + +#include +#include +#include +#include +#include +#include + + + +namespace util { + using boost::remove_pointer; + using boost::remove_reference; + using boost::is_convertible; + using boost::is_polymorphic; + using boost::is_base_of; + using boost::enable_if; + + + template + struct can_cast : boost::false_type {}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + template + struct can_cast { enum { value = is_base_of::value };}; + + + template + struct has_RTTI + { + typedef typename remove_pointer< + typename remove_reference::type>::type TPlain; + + enum { value = is_polymorphic::value }; + }; + + template + struct use_dynamic_downcast + { + enum { value = can_cast::value + && has_RTTI::value + && has_RTTI::value + }; + }; + + template + struct use_static_downcast + { + enum { value = can_cast::value + && ( !has_RTTI::value + || !has_RTTI::value + ) + }; + }; + + template + struct use_conversion + { + enum { value = is_convertible::value + && !( use_static_downcast::value + ||use_dynamic_downcast::value + ) + }; + }; + + + + template + struct EmptyVal + { + static X create() { return X(); } + }; + template + struct EmptyVal + { + static X*& create() { static X* nullP(0); return nullP; } + }; + + + + + + template + struct NullAccessor + { + typedef RET Ret; + + static RET access (...) { return ifEmpty(); } + static RET ifEmpty () { return EmptyVal::create(); } + }; + + template + struct AccessCasted : NullAccessor + { + using NullAccessor::access; + + template + static typename enable_if< use_dynamic_downcast, TAR>::type + access (ELM& elem) + { + return dynamic_cast (elem); + } + + template + static typename enable_if< use_static_downcast, TAR>::type + access (ELM& elem) + { + return static_cast (elem); + } + + template + static typename enable_if< use_conversion, TAR>::type + access (ELM& elem) + { + return elem; + } + }; + + +} // namespace util +#endif diff --git a/src/common/typelistutil.hpp b/src/common/typelistutil.hpp index f7a716024..3fb6a531e 100644 --- a/src/common/typelistutil.hpp +++ b/src/common/typelistutil.hpp @@ -40,7 +40,10 @@ This code is heavily inspired by /** @file typelistutil.hpp ** Helpers for working with lumiera::typelist::Types (i.e. lists-of-types). - ** + ** The main purpose is to build interfaces and polymorphic implementations + ** (using virtual functions) based on templated Types or Collections of types, + ** which is not possible without Template Metaprogrmming. + ** ** @see lumiera::query::ConfigRules usage example ** @see typelist.hpp ** @@ -141,6 +144,94 @@ namespace lumiera }; + + /** + * A Variation of InstantiateChained providing an incremented + * Index value template parameter. This index can e.g. be used + * to store pointers in a dispatcher table in the Base class. + * Similar to InstantiateChained, this template builds a linear + * chain of inheritance. The user-provided template, which is + * to be instantiated for all types in the Typelist, now has to + * accept an additional third parameter (uint i). + */ + template + < class TYPES // List of Types + , template class _X_ // your-template-goes-here + , class BASE = NullType // Base class at end of chain + , uint i = 0 // incremented on each instantiaton + > + class InstantiateWithIndex; + + + template< template class _X_ + , class BASE + , uint i + > + class InstantiateWithIndex + : public BASE + { + public: + typedef BASE Unit; + typedef NullType Next; + enum{ COUNT = i }; + }; + + + template + < class TY, typename TYPES + , template class _X_ + , class BASE + , uint i + > + class InstantiateWithIndex, _X_, BASE, i> + : public _X_< TY + , InstantiateWithIndex + , i + > + { + public: + typedef InstantiateWithIndex Next; + typedef _X_ Unit; + enum{ COUNT = Next::COUNT }; + }; + + + /** + * Metafunction counting the number of Types in the collection + */ + template + struct count; + template<> + struct count + { + enum{ value = 0 }; + }; + template + struct count > + { + enum{ value = 1 + count::value }; + }; + + /** + * Metafunction " max( sizeof(T) ) for T in TYPES " + */ + template + struct maxSize; + template<> + struct maxSize + { + enum{ value = 0 }; + }; + template + struct maxSize > + { + enum{ thisval = sizeof(TY) + , nextval = maxSize::value + , value = nextval > thisval? nextval:thisval + }; + }; + + } // namespace typelist } // namespace lumiera diff --git a/src/common/variant.hpp b/src/common/variant.hpp new file mode 100644 index 000000000..561b7149c --- /dev/null +++ b/src/common/variant.hpp @@ -0,0 +1,276 @@ +/* + VARIANT.hpp - simple variant wrapper (typesafe union) + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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 variant.hpp + ** This file defines a simple alternative to boost::variant. + ** It pulls in fewer headers and has a shorter code path, but also + ** doesn't deal with alignement issues and is not threadsafe. + ** + ** Values can be stored using \c operator= . In order to access the value + ** stored in lumiera::Variant, you additionally need to define a "functor" + **
  • with a typedef "Ret" for the return type
  • + **
  • providing a static Ret access(ELM&) function + ** for each of the types used in the Variant
  • + **
+ ** + ** @see wrapperptr.hpp usage example + */ + + +#ifndef LUMIERA_VARIANT_H +#define LUMIERA_VARIANT_H + + +#include "common/typelistutil.hpp" + +#include + + + +namespace lumiera { + + namespace variant { + + using lumiera::typelist::count; + using lumiera::typelist::maxSize; + using lumiera::typelist::InstantiateWithIndex; + + /** + * internal helper used to build a variant storage wrapper. + * Parametrized with a collection of types, it provides functionality + * to copy a value of one of these types into an internal buffer, while + * remembering which of these types was used to place this copy. + * The value can be later on extracted using a visitation like mechanism, + * which takes a functor class and invokes a function \c access(T&) with + * the type matching the current value in storage + */ + template + struct Holder + { + + enum { TYPECNT = count::value + , SIZE = maxSize::value + }; + + + /** Storage to hold the actual value */ + struct Buffer + { + char buffer_[SIZE]; + uint which_; + + Buffer() : which_(TYPECNT) {} + + void* + put (void) + { + deleteCurrent(); + return 0; + } + + void + deleteCurrent (); // depends on the Deleter, see below + }; + + template + struct PlacementAdapter : BASE + { + T& + put (T const& toStore) + { + BASE::deleteCurrent(); // remove old content, if any + + T& storedObj = *new(BASE::buffer_) T (toStore); + BASE::which_ = idx; // remember the actual type selected + return storedObj; + } + + using BASE::put; + }; + + typedef InstantiateWithIndex< TYPES + , PlacementAdapter + , Buffer + > + Storage; + + + + /** provide a dispatcher table based visitation mechanism */ + template + struct CaseSelect + { + typedef typename FUNCTOR::Ret Ret; + typedef Ret (*Func)(Buffer&); + + Func table_[TYPECNT]; + + CaseSelect () + { + for (uint i=0; i + static Ret + trampoline (Buffer& storage) + { + T& content = reinterpret_cast (storage.buffer_); + return FUNCTOR::access (content); + } + + Ret + invoke (Buffer& storage) + { + if (TYPECNT <= storage.which_) + return FUNCTOR::ifEmpty (); + else + return (*table_[storage.which_]) (storage); + } + }; + + + template< class T, class BASE, uint i > + struct CasePrepare + : BASE + { + CasePrepare () : BASE() + { + BASE::table_[i] = &BASE::template trampoline; + } + }; + + + template + static typename FUNCTOR::Ret + access (Buffer& buf) + { + typedef InstantiateWithIndex< TYPES + , CasePrepare + , CaseSelect + > + Accessor; + static Accessor select_case; + return select_case.invoke(buf); + } + + + struct Deleter + { + typedef void Ret; + + template + static void access (T& elem) { elem.~T(); } + + static void ifEmpty () { } + }; + }; + + + template + inline void + Holder::Buffer::deleteCurrent () + { + access(*this); // remove old content, if any + which_ = TYPECNT; // mark as empty + } + + } // namespace variant + + + + + + + + + /** + * A variant wrapper (typesafe union) capable of holding a value of any + * of a bounded collection of types. The value is stored in a local buffer + * directly within the object and may be accessed by a typesafe visitation. + * + * \par + * This utility class is similar to boost::variant and indeed was implemented + * (5/08) in an effort to replace the latter in a draft solution for the problem + * of typesafe access to the correct wrapper class from within some builder tool. + * Well -- after finisihng this "exercise" I must admit that it is not really + * much more simple than what boost::variant does internally. At least we are + * pulling in fewer headers and the actual code path is shorter compared with + * boost::variant, at the price of beeing not so generic, not caring for + * alignment issues within the buffer and being not threadsafe + * + * @param TYPES collection of possible types to be stored in this variant object + * @param Access policy how to access the stored value + */ + template< typename TYPES + , template class Access + > + class Variant + : boost::noncopyable + { + + typedef variant::Holder Holder; + typedef typename Holder::Deleter Deleter; + + + /** storage: buffer holding either and "empty" marker, + * or one of the configured pointer to wrapper types */ + typename Holder::Storage holder_; + + + public: + void reset () { holder_.deleteCurrent();} + + /** store a copy of the given argument within the + * variant holder buffer, thereby typically casting + * or converting the given source type to the best + * suited (base) type (out of the collection of possible + * types for this Variant instance) + */ + template + Variant& + operator= (SRC src) + { + if (src) holder_.put (src); // see Holder::PlacementAdaptor::put + else reset(); + return *this; + } + + /** retrieve current content of the variant, + * trying to cast or convert it to the given type. + * Actually, the function \c access(T&) on the + * Access-policy (template param) is invoked with the + * type currently stored in the holder buffer. + * May return NULL if conversion fails. + */ + template + TAR + get () + { + typedef Access Extractor; + return Holder::template access (this->holder_); + } + }; + +} // namespace lumiera +#endif diff --git a/src/common/wrapperptr.hpp b/src/common/wrapperptr.hpp new file mode 100644 index 000000000..e17963eff --- /dev/null +++ b/src/common/wrapperptr.hpp @@ -0,0 +1,63 @@ +/* + WRAPPERPTR.hpp - variant record able to hold a pointer to some smart-ptr/wrapper types, providing conversions + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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. + +*/ + + + +#ifndef LUMIERA_WRAPPERPTR_H +#define LUMIERA_WRAPPERPTR_H + + +#include "common/variant.hpp" +#include "common/accesscasted.hpp" + +#include "common/typelist.hpp" +#include "proc/mobject/placement.hpp" +#include "common/p.hpp" + + +namespace asset { class Asset; } +namespace mobject { class MObject; } + +namespace lumiera { + + + typedef typelist::Types < mobject::Placement* + , P* + > ::List + WrapperTypes; + + /** + * helper to treat various sorts of smart-ptrs uniformly. + * Implemented as a variant-type value object, it is preconfigured + * with the possible hierarchy-base classes used within this application. + * Thus, when passing in an arbitrary smart-ptr, the best fitting smart-ptr + * type pointing to the corresponding base class is selected for internal storage. + * Later on, stored values can be retrieved either utilitzing static or dynamic casts; + * error reporting is similar to the bahaviour of dynamic_cast: when retrieving + * a pointer, in case of mismatch NULL is returned. + */ + typedef lumiera::Variant WrapperPtr; + + + +} // namespace lumiera +#endif diff --git a/src/proc/mobject/builder/applicablebuildertargettypes.hpp b/src/proc/mobject/builder/applicablebuildertargettypes.hpp new file mode 100644 index 000000000..e51fa1251 --- /dev/null +++ b/src/proc/mobject/builder/applicablebuildertargettypes.hpp @@ -0,0 +1,70 @@ +/* + ApplicableBuilderTargetTypes - definitinon header specifying all types treated by builder tools + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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. + +*/ + + +#ifndef MOBJECT_BUILDER_APPLICABLEBUILDERTARGETTYPES_H +#define MOBJECT_BUILDER_APPLICABLEBUILDERTARGETTYPES_H + +#include "proc/mobject/builder/buildertool.hpp" + + + +namespace mobject { + namespace session { + + class Clip; + class Effect; + class AbstractMO; + template class Auto; + // Forward declarations sufficient here... + // actual definitions necessary only in the + // implementation file (*cpp) of the builder tool. + } + + namespace builder { + + typedef Types< session::Clip, + session::Effect, + session::AbstractMO + > ::List + BuilderTargetTypes; + + + /** + * Marker used to declare some visiting Tool class to be actually a + * mobject::builder::BuilderTool and to possibly accept and treat the + * common selection of types to be handled by any such builder tool. + * The actual BuilderTool impl should inherit from this template by + * feeding back its type (CRTP), this causes a dispatcher table entry + * be generated for this concrete BuilderTool implementation. + */ + template + struct ApplicableBuilderTargetTypes + : Applicable + { }; + + + + } // namespace mobject::builder + +} // namespace mobject +#endif diff --git a/src/proc/mobject/builder/buildertool.hpp b/src/proc/mobject/builder/buildertool.hpp index 0985567d1..1801bc2ef 100644 --- a/src/proc/mobject/builder/buildertool.hpp +++ b/src/proc/mobject/builder/buildertool.hpp @@ -20,20 +20,54 @@ */ +/** @file buildertool.hpp + ** Visiting-tool mechanism configured specifically for the Builder. + ** The Builder creates the render nodes network by applying several Builder Tools + ** to the objects found in the Session, EDL and Fixture. These BuilderTool instances + ** contain the details of the builder implementation. + ** + ** As the objects to be treated are normally handled by smart-ptrs, BuilderTool provides + ** a special facility for dealing with these wrapped objects. There are some liabilities. + **
  • each concrete Buildable subtype to be treated specifically needs to + ** declare \c DEFINE_PROCESSABLE_BY(BuilderTool)
  • + **
  • at the same time, the concrete BuilderTool subclass has to declare + ** being Applicable to this concrete Buildable subtype. The recommended way + ** of ensuring this, is to add an entry to applicablebuildertargettypes.hpp + ** and then derive the concrete BuilderTool subclass from + ** ApplicableBuilderTargetTypes
  • + **
  • when accessing the wrapper from within a \c treat() function, a suitable + ** concrete wrapper type has to be specified. If the wrapper type used for + ** invoking the BuilderTool (function \c apply(BuilderTool&l, WrappedObject&) ) + ** can not be converted to this type requested from within the call, an + ** assertion failure (or segmentation fault in a release build) will result.
  • + **
+ ** + ** @see visitor.hpp + ** @see applicablebuildertargettypes.hpp + ** @see buildertooltest.hpp + ** @see nodecreatertool.hpp + */ + + #ifndef MOBJECT_BUILDER_TOOL_H #define MOBJECT_BUILDER_TOOL_H #include "common/visitor.hpp" +#include "common/wrapperptr.hpp" + +#include "common/p.hpp" +#include "proc/mobject/placement.hpp" +#include "proc/mobject/explicitplacement.hpp" +namespace mobject { -namespace mobject - { class Buildable; - namespace builder - { + namespace builder { + + using lumiera::P; /** * Policy invoking an catch-all function for processing @@ -49,19 +83,79 @@ namespace mobject virtual RET onUnknown (Buildable& target) = 0; }; - - + + /** * Base class of all BuilderTools, used according to the visitor pattern: * each Tool contains the concrete implementation for one task to be done * to the various MObject classes. The concrete builder tool implementation * should not diretcly inherit from this base interface, but rather through * an instantiation of the "Applicable" template parametrized with all - * concrete Buildable classes, for which it wants calls to be dispatched. + * concrete Buildable classes, for which it wants calls to be dispatched. + * \par + * In addition to lumiera::visitor::Tool, BuilderTool adds support for dealing + * with objects normally handled by means of smart-pointers or similar + * wrappers, most notably mobject::Placement. The visitaion is initiated + * by calling the stand-alone function \c (BuilderTool& tool, WRA& wrappedTargetObj) , + * which forwards to the visitation mechanism supported by the type contained + * in the wrapper, but stores away a pointer to the wrapped object, which can + * be retrieved in a typesafe manner from within the \c treat(ConcreteType&) + * function. + * @note retrieving the wrapper is not threadsafe and not reentrant, + * as we simply store a pointer within the BuilderTool instance. */ - typedef lumiera::visitor::Tool BuilderTool; + class BuilderTool + : public lumiera::visitor::Tool + { + lumiera::WrapperPtr currentWrapper_; + + public: + + template class WRA, class TAR> + void rememberWrapper (WRA* ptr_toWrappedTarget) + { + currentWrapper_ = ptr_toWrappedTarget; + } + + void forgetWrapper () + { + currentWrapper_.reset(); + } + + + protected: /* == interface for accessing the wrapper from within tool application == */ + + template + Placement& + getPlacement () + { + Placement* pPlacement = currentWrapper_.get*>(); + ENSURE (pPlacement, "wrong target type when invoking %s", __PRETTY_FUNCTION__); + return *pPlacement; + } + + ExplicitPlacement + getExplicitPlacement () + { + return getPlacement().resolve(); + } + + template + lumiera::P + getPtr () + { + P* pP = currentWrapper_.get*>(); + ENSURE (pP, "wrong target type when invoking %s", __PRETTY_FUNCTION__); + return *pP; + } + }; + + /** + * declare the concrete types a BuilderTool may recievee and treat. + * @note it is recommended to use ApplicableBuilderTargetTypes + */ template < class TOOLImpl, // concrete BuilderTool implementation class TYPELIST // list of all concrete Buildables to be treated @@ -71,17 +165,33 @@ namespace mobject { } ; - using lumiera::typelist::Types; // convienience for the users of "Applicable" - + using lumiera::typelist::Types; // convenience for the users of "Applicable" + } // namespace mobject::builder /** - * Marker Interface for classes Visitable by Builder tools. + * Marker Interface for classes visitable by Builder tools. */ class Buildable : public lumiera::visitor::Visitable { }; - + + + + + namespace builder { // to be found by ADL + + template + inline Buildable::ReturnType + apply (BuilderTool& tool, WRA& wrappedTargetObj) + { + tool.rememberWrapper(&wrappedTargetObj); + wrappedTargetObj->apply (tool); // dispatch to suitable treat() function + tool.forgetWrapper(); + } + + } // namespace mobject::builder + } // namespace mobject #endif diff --git a/src/proc/mobject/builder/nodecreatertool.cpp b/src/proc/mobject/builder/nodecreatertool.cpp index 89a35fa46..93362ef25 100644 --- a/src/proc/mobject/builder/nodecreatertool.cpp +++ b/src/proc/mobject/builder/nodecreatertool.cpp @@ -31,11 +31,8 @@ using mobject::session::Clip; using mobject::session::Effect; using mobject::session::Auto; -namespace mobject - { - - namespace builder - { +namespace mobject { + namespace builder { diff --git a/src/proc/mobject/builder/nodecreatertool.hpp b/src/proc/mobject/builder/nodecreatertool.hpp index 26a033d26..e37d0d14f 100644 --- a/src/proc/mobject/builder/nodecreatertool.hpp +++ b/src/proc/mobject/builder/nodecreatertool.hpp @@ -24,23 +24,15 @@ #ifndef MOBJECT_BUILDER_NODECREATERTOOL_H #define MOBJECT_BUILDER_NODECREATERTOOL_H -#include "proc/mobject/builder/buildertool.hpp" + +#include "proc/mobject/builder/applicablebuildertargettypes.hpp" + #include "proc/engine/processor.hpp" -namespace mobject - { - namespace session - { - // Forward declarations - class Effect; - class Clip; - template class Auto; - } - - namespace builder - { +namespace mobject { + namespace builder { @@ -52,11 +44,10 @@ namespace mobject * render engine under construction such as to reflect the properties * of the MObject in the actual render. */ - class NodeCreatorTool : public BuilderTool + class NodeCreatorTool + : public ApplicableBuilderTargetTypes { - //////////////////////////////////////////////TODO: switch to acyclic visitior!!!!!!!!!!!!! - public: virtual void treat (mobject::session::Clip& clip) ; virtual void treat (mobject::session::Effect& effect) ; diff --git a/src/proc/mobject/builder/segmentationtool.cpp b/src/proc/mobject/builder/segmentationtool.cpp index 6f07e7d4f..7b6c29747 100644 --- a/src/proc/mobject/builder/segmentationtool.cpp +++ b/src/proc/mobject/builder/segmentationtool.cpp @@ -31,10 +31,8 @@ using mobject::session::Clip; using mobject::session::Effect; -namespace mobject - { - namespace builder - { +namespace mobject { + namespace builder { diff --git a/src/proc/mobject/builder/segmentationtool.hpp b/src/proc/mobject/builder/segmentationtool.hpp index aa09d80d3..eaf74c1c6 100644 --- a/src/proc/mobject/builder/segmentationtool.hpp +++ b/src/proc/mobject/builder/segmentationtool.hpp @@ -24,26 +24,18 @@ #ifndef MOBJECT_BUILDER_SEGMENTATIONTOOL_H #define MOBJECT_BUILDER_SEGMENTATIONTOOL_H -#include -#include "proc/mobject/builder/buildertool.hpp" +#include "proc/mobject/builder/applicablebuildertargettypes.hpp" + #include "proc/mobject/session/segment.hpp" +#include using std::list; -namespace mobject - { - namespace session - { - // Forward declarations - class Clip; - class Effect; - } - - namespace builder - { +namespace mobject { + namespace builder { /** @@ -53,10 +45,10 @@ namespace mobject * can be represented by automation solely, without the need * to change the node connections. */ - class SegmentationTool : public BuilderTool + class SegmentationTool + : public ApplicableBuilderTargetTypes { - //////////////////////////////////////////////TODO: switch to acyclic visitior!!!!!!!!!!!!! public: void treat (mobject::session::Clip& clip) ; diff --git a/src/proc/mobject/explicitplacement.hpp b/src/proc/mobject/explicitplacement.hpp index cb5e424b5..0e5b20bfd 100644 --- a/src/proc/mobject/explicitplacement.hpp +++ b/src/proc/mobject/explicitplacement.hpp @@ -30,9 +30,9 @@ namespace mobject { - - - + + + /** * Special kind of Placement, where the location of the * MObject has been nailed down to a fixed position. @@ -51,7 +51,7 @@ namespace mobject const Pipe pipe; typedef std::pair SolutionData; //TODO (ichthyo consideres better passing of solution by subclass) - + /** no need to resolve any further, as this ExplicitPlacement * already \e is the result of a resolve()-call. */ @@ -61,9 +61,7 @@ namespace mobject return *this; } - /** */ /////////////////////////////////////////////////////////////TODO: wip-wip... - DEFINE_PROCESSABLE_BY (builder::BuilderTool); - + protected: /* @todo ichthyo considers a much more elegant implementation utilizing a subclass * of FixedLocation, which would serve as Placement::LocatingSolution, and @@ -82,8 +80,8 @@ namespace mobject /** copying prohibited, ExplicitPlacement is effectively const! */ ExplicitPlacement& operator= (const ExplicitPlacement&); }; - - - + + + } // namespace mobject #endif diff --git a/src/proc/mobject/mobject.hpp b/src/proc/mobject/mobject.hpp index 28ab788ed..eb481489f 100644 --- a/src/proc/mobject/mobject.hpp +++ b/src/proc/mobject/mobject.hpp @@ -88,9 +88,10 @@ namespace mobject virtual bool operator== (const MObject& oo) const =0; }; + - - + + typedef Placement PMO; diff --git a/src/proc/mobject/placement.cpp b/src/proc/mobject/placement.cpp index 452528627..58e145e72 100644 --- a/src/proc/mobject/placement.cpp +++ b/src/proc/mobject/placement.cpp @@ -23,25 +23,39 @@ #include "proc/mobject/placement.hpp" #include "proc/mobject/explicitplacement.hpp" +#include "proc/mobject/mobject.hpp" + +#include +using boost::str; namespace mobject { - /** @note we know we need only this single - * specialisation, because we define - * the Placements of more specific - * MObject kinds to be subclasses - * of Placement, so they - * will inherit this function. - */ - template<> + template ExplicitPlacement - Placement::resolve () const + Placement::resolve () const { return ExplicitPlacement (*this, chain.resolve()); } + + /** @note we know we need only this single specialisation, + * because we define the Placements of more specific + * MObject kinds to be subclasses of Placement, + * so they will inherit this function. + */ + template ExplicitPlacement Placement::resolve() const; + + template<> + Placement::operator string () const + { + static boost::format fmt("Placement<%s> %|50T.| use-cnt=%x adr=%x pointee=%x"); + return str(fmt % typeid(*get()).name() % use_count() % this % get() ); + } + + + } // namespace mobject diff --git a/src/proc/mobject/placement.hpp b/src/proc/mobject/placement.hpp index 7bd3a9ce4..df71f0d75 100644 --- a/src/proc/mobject/placement.hpp +++ b/src/proc/mobject/placement.hpp @@ -58,12 +58,10 @@ #define MOBJECT_PLACEMENT_H #include "pre.hpp" -#include "proc/mobject/mobject.hpp" #include "proc/mobject/session/locatingpin.hpp" #include "proc/asset/pipe.hpp" #include -using std::tr1::shared_ptr; namespace mobject @@ -72,6 +70,7 @@ namespace mobject class ExplicitPlacement; + using std::tr1::shared_ptr; @@ -82,9 +81,10 @@ namespace mobject * be within the Session/EDL */ template - class Placement : public Buildable, protected shared_ptr + class Placement : protected shared_ptr { protected: + typedef shared_ptr Base; typedef lumiera::Time Time; typedef asset::shared_ptr Pipe; @@ -99,13 +99,14 @@ namespace mobject operator-> () const { ENSURE (*this); - return shared_ptr::operator-> (); + return Base::operator-> (); } + operator string() const ; + size_t use_count() const { return Base::use_count(); } + virtual ~Placement() {}; - /** */ /////////////////////////////////////////////////////////////TODO: totmachen? - DEFINE_PROCESSABLE_BY (builder::BuilderTool); /** interface for defining the kind of placement @@ -128,20 +129,19 @@ namespace mobject friend class session::MObjectFactory; }; - typedef Placement PMO; /* === defining specialisations to be subclasses === */ -#define DEFINE_SPECIALIZED_PLACEMENT(SUBCLASS) \ +#define DEFINE_SPECIALIZED_PLACEMENT(SUBCLASS, BASE) \ template<> \ - class Placement : public Placement \ + class Placement : public Placement \ { \ protected: \ Placement (SUBCLASS & m, void (*moKiller)(MObject*)) \ - : Placement::Placement (m, moKiller) \ + : Placement::Placement (m, moKiller) \ { }; \ friend class session::MObjectFactory; \ \ @@ -154,7 +154,6 @@ namespace mobject (shared_ptr::operator-> ()); \ } \ }; - // DEFINE_PROCESSABLE_BY (builder::BuilderTool); /* a note to the maintainer: please don't add any fields or methods to * these subclasses which aren't also present in Placement! diff --git a/src/proc/mobject/session/clip.hpp b/src/proc/mobject/session/clip.hpp index 7f1fe23c0..174a86cbf 100644 --- a/src/proc/mobject/session/clip.hpp +++ b/src/proc/mobject/session/clip.hpp @@ -66,7 +66,7 @@ namespace mobject and the unlink() function of the asset should take it into account when breaking circular references. */ -public: ////////////////////////////////////TODO: temporarily for buildertooltest + const Media & mediaDef_; const asset::Clip & clipDef_; @@ -99,7 +99,7 @@ public: ////////////////////////////////////TODO: temporarily for buildertoolt } // namespace mobject::session /** Placement defined to be subclass of Placement */ - DEFINE_SPECIALIZED_PLACEMENT (session::Clip); + DEFINE_SPECIALIZED_PLACEMENT (session::Clip, MObject); } // namespace mobject diff --git a/src/proc/mobject/session/effect.hpp b/src/proc/mobject/session/effect.hpp index 5831098d4..e4cdbee16 100644 --- a/src/proc/mobject/session/effect.hpp +++ b/src/proc/mobject/session/effect.hpp @@ -51,7 +51,7 @@ namespace mobject } // namespace mobject::session /** Placement defined to be subclass of Placement */ - DEFINE_SPECIALIZED_PLACEMENT (session::Effect); + DEFINE_SPECIALIZED_PLACEMENT (session::Effect, MObject); } // namespace mobject #endif diff --git a/src/proc/mobject/session/meta.hpp b/src/proc/mobject/session/meta.hpp index b45d30eb5..db679a904 100644 --- a/src/proc/mobject/session/meta.hpp +++ b/src/proc/mobject/session/meta.hpp @@ -50,7 +50,7 @@ namespace mobject } // namespace mobject::session /** Placement defined to be subclass of Placement */ - DEFINE_SPECIALIZED_PLACEMENT (session::Meta); + DEFINE_SPECIALIZED_PLACEMENT (session::Meta, MObject); } // namespace mobject #endif diff --git a/src/proc/mobject/session/track.hpp b/src/proc/mobject/session/track.hpp index 2b9801273..f8b2c0017 100644 --- a/src/proc/mobject/session/track.hpp +++ b/src/proc/mobject/session/track.hpp @@ -83,7 +83,7 @@ namespace mobject } // namespace mobject::session /** Placement defined to be subclass of Placement */ - DEFINE_SPECIALIZED_PLACEMENT (session::Track); + DEFINE_SPECIALIZED_PLACEMENT (session::Track, session::Meta); } // namespace mobject #endif diff --git a/tests/54builder.tests b/tests/54builder.tests index 3b6825651..3ec3bd325 100644 --- a/tests/54builder.tests +++ b/tests/54builder.tests @@ -4,8 +4,12 @@ TESTING "Component Test Suite: Builder" ./test-components --group=builder TEST "BuilderTool_test" BuilderTool_test < -#include -#include -#include -#include -#include +#include "common/util.hpp" #include + +using util::cStr; using std::string; using std::cout; -namespace util { - using boost::remove_pointer; - using boost::remove_reference; - using boost::is_convertible; - using boost::is_polymorphic; - using boost::is_base_of; - using boost::enable_if; - - template - struct can_cast : boost::false_type {}; - template - struct can_cast { enum { value = is_base_of::value };}; - - template - struct can_cast { enum { value = is_base_of::value };}; - - template - struct can_cast { enum { value = is_base_of::value };}; - - - template - struct has_RTTI - { - typedef typename remove_pointer< - typename remove_reference::type>::type TPlain; - - enum { value = is_polymorphic::value }; - }; - - template - struct use_dynamic_downcast - { - enum { value = can_cast::value - && has_RTTI::value - && has_RTTI::value - }; - }; - - template - struct use_static_downcast - { - enum { value = can_cast::value - && ( !has_RTTI::value - || !has_RTTI::value - ) - }; - }; - - template - struct use_conversion - { - enum { value = is_convertible::value - && !( use_static_downcast::value - ||use_dynamic_downcast::value - ) - }; - }; - - - - template - struct EmptyVal - { - static X create() { return X(); } - }; - template - struct EmptyVal - { - static X*& create() { static X* nullP(0); return nullP; } - }; - - - - - - template - struct NullAccessor - { - typedef RET Ret; - - static RET access (...) { return ifEmpty(); } - static RET ifEmpty () { return EmptyVal::create(); } - }; - - template - struct AccessCasted : NullAccessor - { - using NullAccessor::access; - - template - static typename enable_if< use_dynamic_downcast, TAR>::type - access (ELM& elem) - { - return dynamic_cast (elem); - } - - template - static typename enable_if< use_static_downcast, TAR>::type - access (ELM& elem) - { - return static_cast (elem); - } - - template - static typename enable_if< use_conversion, TAR>::type - access (ELM& elem) - { - return elem; - } - }; - - -} - -namespace lumiera { - - namespace typelist { - - - template - < class TYPES // List of Types - , template class _X_ // your-template-goes-here - , class BASE = NullType // Base class at end of chain - , uint i = 0 // incremented on each instantiaton - > - class InstantiateWithIndex; - - - template< template class _X_ - , class BASE - , uint i - > - class InstantiateWithIndex - : public BASE - { - public: - typedef BASE Unit; - typedef NullType Next; - enum{ COUNT = i }; - }; - - - template - < class TY, typename TYPES - , template class _X_ - , class BASE - , uint i - > - class InstantiateWithIndex, _X_, BASE, i> - : public _X_< TY - , InstantiateWithIndex - , i - > - { - public: - typedef InstantiateWithIndex Next; - typedef _X_ Unit; - enum{ COUNT = Next::COUNT }; - }; - - template - struct count; - template<> - struct count - { - enum{ value = 0 }; - }; - template - struct count > - { - enum{ value = 1 + count::value }; - }; - - template - struct maxSize; - template<> - struct maxSize - { - enum{ value = 0 }; - }; - template - struct maxSize > - { - enum{ nextval = maxSize::value - , thisval = sizeof(TY) - , value = nextval > thisval? nextval:thisval - }; - }; - - } // namespace typelist - - - - - - namespace variant { - - using lumiera::typelist::count; - using lumiera::typelist::maxSize; - using lumiera::typelist::InstantiateWithIndex; - - /** - * internal helper used to build a variant storage wrapper. - * Parametrized with a collection of types, it provides functionality - * to copy a value of one of these types into an internal buffer, while - * remembering which of these types was used to place this copy. - * The value can be later on extracted using a visitation like mechanism, - * which takes a functor object and invokes a function \c access(T&) with - * the type matching the current value in storage - */ - template - struct Holder - { - - enum { TYPECNT = count::value - , SIZE = maxSize::value - }; - - - /** Storage to hold the actual value */ - struct Buffer - { - char buffer_[SIZE]; - uint which; - - Buffer() : which(TYPECNT) {} - - void* - put (void) - { - deleteCurrent(); - return 0; - } - - void - deleteCurrent (); // depends on the Deleter, see below - }; - - template - struct PlacementAdapter : BASE - { - T& - put (T const& toStore) - { - BASE::deleteCurrent(); // remove old content, if any - - T& storedObj = *new(BASE::buffer_) T (toStore); - this->which = idx; // remember the actual type selected - return storedObj; - } - - using BASE::put; - }; - - typedef InstantiateWithIndex< TYPES - , PlacementAdapter - , Buffer - > - Storage; - - - - /** provide a dispatcher table based visitation mechanism */ - template - struct CaseSelect - { - typedef typename FUNCTOR::Ret Ret; - typedef Ret (*Func)(Buffer&); - - Func table_[TYPECNT]; - - CaseSelect () - { - for (uint i=0; i - static Ret - trampoline (Buffer& storage) - { - T& content = reinterpret_cast (storage.buffer_); - return FUNCTOR::access (content); - } - - Ret - invoke (Buffer& storage) - { - if (TYPECNT <= storage.which) - return FUNCTOR::ifEmpty (); - else - return (*table_[storage.which]) (storage); - } - }; - - - template< class T, class BASE, uint i > - struct CasePrepare - : BASE - { - CasePrepare () : BASE() - { - BASE::table_[i] = &BASE::template trampoline; - } - }; - - - template - static typename FUNCTOR::Ret - access (Buffer& buf) - { - typedef InstantiateWithIndex< TYPES - , CasePrepare - , CaseSelect - > - Accessor; - static Accessor select_case; - return select_case.invoke(buf); - } - - - struct Deleter - { - typedef void Ret; - - template - static void access (T& elem) { elem.~T(); } - - static void ifEmpty () { } - }; - }; - - - template - void - Holder::Buffer::deleteCurrent () - { - access(*this); // remove old content, if any - which = TYPECNT; // mark as empty - } - - } // namespace variant - - - - - - - - - - - - - /** - * A variant wrapper (typesafe union) capable of holding a value of any - * of a bounded collection of types. The value is stored in a local buffer - * directly within the object and may be accessed by a typesafe visitation. - * - * \par - * This utility class is similar to boost::variant and indeed was implemented - * (5/08) in an effort to replace the latter in a draft solution for the problem - * of typesafe access to the correct wrapper class from within some builder tool. - * Well -- after finisihng this "exercise" I must admit that it is not really - * much more simple than what boost::variant does internally. At least we are - * pulling in fewer headers and the actual code path is shorter compared with - * boost::variant, at the price of beeing not so generic, not caring for - * alignment issues within the buffer and being not threadsafe - * - * @param TYPES collection of possible types to be stored in this variant object - * @param Access policy how to access the stored value - */ - template< typename TYPES - , template class Access = util::AccessCasted - > - class Variant - : boost::noncopyable - { - - typedef variant::Holder Holder; - typedef typename Holder::Deleter Deleter; - - - /** storage: buffer holding either and "empty" marker, - * or one of the configured pointer to wrapper types */ - typename Holder::Storage holder_; - - - public: - void reset () { holder_.deleteCurrent();} - - /** store a copy of the given argument within the - * variant holder buffer, thereby typically casting - * or converting the given source type to the best - * suited (base) type (out of the collection of possible - * types for this Variant instance) - */ - template - Variant& - operator= (SRC src) - { - if (src) holder_.put (src); // see Holder::PlacementAdaptor::put - else reset(); - return *this; - } - - /** retrieve current content of the variant, - * trying to cast or convert it to the given type. - * Actually, the function \c access(T&) on the - * Access-policy (template param) is invoked with the - * type currently stored in the holder buffer. - * May return NULL if conversion fails. - */ - template - TAR - get () - { - typedef Access Extractor; - return Holder::template access (this->holder_); - } - }; - -} // namespace lumiera - - - -namespace mobject - { - - DEFINE_SPECIALIZED_PLACEMENT (session::AbstractMO); - - - namespace builder - { - namespace test - { +namespace mobject { + namespace builder { + namespace test { using session::Clip; using session::AbstractMO; - -/////////////////////////////////////////////////////TODO: move to buildertool.hpp - - using lumiera::P; - using lumiera::typelist::Types; - - - - typedef Types < Placement* - , P* - > ::List - WrapperTypes; - - /** - * helper to treat various sorts of smart-ptrs uniformly. - * Implemented as a variant-type value object, it is preconfigured - * with the possible hierarchy-base classes used within this application. - * Thus, when passing in an arbitrary smart-ptr, the best fitting smart-ptr - * type pointing to the corresponding base class is selected for internal storage. - * Later on, stored values can be retrieved either utilitzing static or dynamic casts; - * error reporting is similar to the bahaviour of dynamic_cast: when retrieving - * a pointer, in case of mismatch NULL is returned. - */ - typedef lumiera::Variant WrapperPtr; - - - - class BuTuul - : public lumiera::visitor::Tool - { - WrapperPtr currentWrapper_; - - public: - - template class WRA, class TAR> - void rememberWrapper (WRA* argument) - { - currentWrapper_ = argument; - } - - void forgetWrapper () - { - currentWrapper_.reset(); - } - template - Placement& - getPlacement () - { - Placement* pPlacement = currentWrapper_.get*>(); - return *pPlacement; - } - ExplicitPlacement - getExplicitPlacement () - { - return getPlacement().resolve(); - } - template - P - getPtr () - { - P* pP = currentWrapper_.get*>(); - return *pP; - } - }; - - - - - - - - template - < class TOOLImpl, // concrete BuilderTool implementation - class TYPELIST // list of all concrete Buildables to be treated - > - class Appli - : public lumiera::visitor::Applicable - { } - ; - - using lumiera::typelist::Types; // convienience for the users of "Applicable" - - class BDable : public lumiera::visitor::Visitable - { - - }; -/////////////////////////////////////////////////////TODO: move to buildertool.hpp - - class DummyMO : public AbstractMO, public BDable + /** + * Test MObject subclass, which, contrary to any real MObject, + * can be created directly without involving MObjectFactory. + */ + class TestMO : public AbstractMO { public: - DummyMO() { }; - virtual bool isValid() const { return true;} -// DEFINE_PROCESSABLE_BY (BuilderTool); + TestMO() { }; - static void killDummy (MObject* dum) { delete (DummyMO*)dum; } - - virtual void - apply (BuTuul& tool) - { - return BDable::dispatchOp (*this, tool); - } + DEFINE_PROCESSABLE_BY (BuilderTool); + virtual bool isValid() const { return true;} + static void killDummy (MObject* dum) { delete (TestMO*)dum; } }; - class Clop : public Clip, public BDable - { - Clop (Clip& base) : Clip (base.clipDef_, base.mediaDef_) {} - - virtual void - apply (BuTuul& tool) - { - return BDable::dispatchOp (*this, tool); - } + /** + * Subclass-1 is \em not defined "processable", + * thus will always be handled as TestMO... + */ + class TestSubMO1 : public TestMO + { }; + + /** + * Subclass-2 \em is defined "processable", + * but we omit the necessary "applicable" definition in TestTool, + * resulting in an invocation of the error (catch-all) function... + */ + class TestSubMO2 : public TestMO + { + DEFINE_PROCESSABLE_BY (BuilderTool); }; - template - inline BDable::ReturnType - apply (BuTuul& tool, WRA& wrappedTargetObj) - { - WRA* ptr = &wrappedTargetObj; - (*ptr)->isValid(); - tool.rememberWrapper(ptr); - wrappedTargetObj->apply (tool); - tool.forgetWrapper(); - } - - //////////////////////////////////////////////////////TODO: wip-wip - - class DummyPlacement : public Placement + template + class TestPlacement : public Placement { public: - DummyPlacement() - : Placement::Placement(*new DummyMO, &DummyMO::killDummy) + TestPlacement(TestMO& testObject) + : Placement::Placement(testObject, &TestMO::killDummy) { } }; - //////////////////////////////////////////////////////TODO: wip-wip - + /** + * BuilderTool implementation for checking the invokation of the correct + * \c treat() function and for accessing the original Placement from + * within this invocation. It is declared to be applicable to Clip + * and TestMO objects (wrapped into any acceptable shared-ptr). + * Intentionally, we omit to declare it applicable to TestSubMO2 instances. + * In reality this would be a case of misconfiguration, because TestSubMO2 + * is defined to be processable and consequently has an \apply() function, + * which, due to this ommission can't find a dispatcher entry when invoked, + * so it will call the \c onUnknown(Buildable&) instead + */ class TestTool - : public Appli::List> + : public Applicable::List> { public: + string log_; + void treat (Clip& c) { Placement& pC = getPlacement(); - cout << "media is: "<< str(pC->getMedia()) <<"\n" - << "Placement(adr) " << &pC <<"\n"; + cout << "Clip on media : "<< str(pC->getMedia()) <<"\n"; + log_ = string (pC); } void treat (AbstractMO&) { - Placement& placement = getPlacement(); - cout << "unspecific MO; Placement(adr) " << &placement <<"\n"; + cout << "treat (AbstractMO&);\n"; + log_ = string (getPlacement()); } - void onUnknown (Buildable&) + void onUnknown (Buildable& bxx) { - cout << "catch-all-function called.\n"; + cout << "catch-all-function called...\n"; + log_ = string (getPlacement()); } }; - - - /******************************************************************* - * @test the generic visitor pattern specialized for treating - * MObjects in the builder. Because the generic visitor - * implementation is already covered by - * \link VisitingTool_test, it is sufficient to test - * the specialisation to the builder. - * \par + + + + + /************************************************************************************* + * @test the generic visitor pattern specialized for treating MObjects in the builder. * Besides using existing MObject types (at the moment session::Clip), - * we create inline a yet-unknown new MObject subclass. When passing - * such to any BuilderTool subclass, the compiler enforces the definition - * of a catch-all function, which is called, when there is no other - * applicable \c treat(MO&) function. Note further, whithin the specific - * treat-functions we get direct references, whithout interfering with - * Placements and memory management. (Is this a good idea? it allows - * client code to bypass the Placement (Handle). At least, I think - * it is not really a problem...) + * we create a yet-unknown new MObject subclass. When passing such to any + * BuilderTool subclass, the compiler enforces the definition of a catch-all + * function, which is called, when there is no other applicable \c treat(MO&) + * function. Note further, whithin the specific treat-functions we get direct + * references, whithout interfering with Placements and memory management. + * But from within the \c treat() function, we may access the wrapper object + * (i.e. shared_ptr, or lumiera::P or Placement) used when invoking the + * BuilderTool by using the protected interface on BuilderTool. + * + * @see VisitingTool_test for checking general visitor functionality */ class BuilderTool_test : public Test { @@ -677,18 +150,27 @@ namespace mobject { TestTool t1; - BuTuul& tool (t1); + BuilderTool& tool = t1; - DummyPlacement dumm; - Placement& dummy(dumm); - Placement clip = asset::Media::create("test-1", asset::VIDEO)->createClip(); + TestPlacement test1(*new TestSubMO1); + TestPlacement test2(*new TestSubMO2); - cout << "Placement(adr) " << &clip <<"\n"; + cout << "apply (tool, clip);\n"; apply (tool, clip); - cout << "Placement(adr) " << &dumm <<"\n"; - apply (tool, dummy); + INFO (test, "got Wrapper = %s", t1.log_.c_str()); + ASSERT (t1.log_ == string(clip)); + + cout << "apply (tool, test1);\n"; + apply (tool, test1); + INFO (test, "got Wrapper = %s", t1.log_.c_str()); + ASSERT (t1.log_ == string(test1)); + + cout << "apply (tool, test2);\n"; + apply (tool, test2); + INFO (test, "got Wrapper = %s", t1.log_.c_str()); + ASSERT (t1.log_ == string(test2)); } }; @@ -702,14 +184,4 @@ namespace mobject } // namespace builder - //////////////////////////////////////////////////////TODO: wip-wip -/* template<> - ExplicitPlacement - Placement::resolve () const - { - UNIMPLEMENTED ("just a test"); - } -*/ //////////////////////////////////////////////////////TODO: wip-wip - - } // namespace mobject From 686e47220d60227fd89d5d96f5eab597a25e2c8d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 21 May 2008 05:30:07 +0200 Subject: [PATCH 08/11] written down basics of the builder's internal operation --- wiki/renderengine.html | 69 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 7e922bde6..b076f0d9b 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -578,8 +578,9 @@ The first step towards an solution is to isolate the problem; obviously we //nee [img[how to implement Automation|uml/fig129669.png]] -
+
Starting out from the concepts of Objects, Placement to Tracks, render Pipes and connection properties (&rarr; see [[here|TrackPipeEDL]]) within the EDL, we can identify the elementary operations occuring within the Builder. Overall, the Builder is organized as application of //visiting tools// to a collection of objects, so finally we have to consider some object kind appearing in the working function of the given builder tool, which holds at this moment some //context//. The job now is to organize this context such as to create a predictable build process from this //event driven// approach.
+&rarr;see also: BuilderPrimitives for the elementary situations used to cary out the building operations
 
 !Builder working Situations
 # any ''Clip'' (which at this point has been reduced already to a part of a simple elementary media stream &rarr; see [[Fixture]])
@@ -728,7 +729,7 @@ config.macros.timeline.handler = function(place,macroName,params,wikifier,paramS
 }
 //}}}
-
+
All decisions on //how // the RenderProcess has to be carried out are concentrated in this rather complicated Builder Subsystem. The benefit of this approach is, besides decoupling of subsystems, to keep the actual performance-intensive video processing code as simple and transparent as possible. The price, in terms of increased complexity &mdash; to pay in the Builder &mdash; can be handled by making the Build Process generic to a large degree. Using a Design By Contract approach we can decompose the various decisions into small decision modules without having to trace the actual workings of the Build Process as a whole.
 
 [>img[Outline of the Build Process|uml/fig129413.png]]
@@ -737,8 +738,10 @@ The building itself will be broken down into several small tool application step
 * for each segment: generate a ProcNode for each found MObject and wire them accordingly
 Note, //we still have to work out how exactly building, rendering and playback work// together with the backend-design. The build process as such doesn't overly depend on these decisions. It is easy to reconfigure this process. For example, it would be possible as well to build for each frame separately (as Cinelerra2 does), or to build one segment covering the whole timeline (and handle everything via [[Automation]]
 
+&rarr;see also: [[Builder Overview|Builder]]
 &rarr;see also: BasicBuildingOperations
 &rarr;see also: BuilderStructures
+&rarr;see also: BuilderMechanics
 &rarr;see also: PlanningBuildFixture
 &rarr;see also: PlanningSegementationTool
 &rarr;see also: PlanningNodeCreatorTool
@@ -746,7 +749,7 @@ Note, //we still have to work out how exactly building, rendering and playback w
 [img[Colaborations in the Build Process|uml/fig128517.png]]
 
-
+
The Builder takes some MObject/[[Placement]] information (called Timeline) and generates out of this a Render Engine configuration able to render this Objects. It does all decisions and retrieves the current configuration of all objects and plugins, so the Render Engine can just process them stright forward.
 
 The Builder is the central part of the [[Builder Pattern|http://en.wikipedia.org/wiki/Builder_pattern]]
@@ -754,12 +757,44 @@ The Builder is the central part of the [[Builder Pattern|http://en.wikipedia.org
 As the builder has to create a render node network implementing most of the features and wiring possible with the various MObject kinds and placement types, it is a rather complicated piece of software. In order to keep it manageable, it is broken down into several specialized sub components:
 * clients access builder functionality via the BuilderFacade
 * the [[Proc-Layer-Controller|Controller]] initiates the BuildProcess and does the overall coordination of scheduling edit operations, rebuilding the fixture and triggering the Builder
-* to carry out the building, we use several tools (SegmentationTool, NodeCreaterTool,...), which are supplied by the [[tool factory|BuilderToolFactory]]
-* the actual building (i.e. the application of those tools to the timeline) is done by the [[Assembler|BuilderAssembler]], which is basically a collection of functions (but has a small amount of global configuration state)
+* to carry out the building, we use several primary tools (SegmentationTool, NodeCreaterTool,...),  together with a BuilderToolKit to be supplied by the [[tool factory|BuilderToolFactory]]
+* //operating the Builder// can be viewed at from two different angles, either emphasizing the [[basic building operations|BasicBuildingOperations]] employed to assemble the render node network, or focussing rather at the [[mechanics|BuilderMechanics]] of cooperating parts while processing.
+* besides, we can identify a small set of elementary situations we call [[builder primitives|BuilderPrimitives]], to be covered by the mentioned BuilderToolKit; by virtue of [[processing patterns|ProcPatt]] they form an [[interface to the rule based configuration|BuilderRulesInterface]].
+* the actual building (i.e. the application of tools to the timeline) is done by the [[Assembler|BuilderAssembler]], which is basically a collection of functions (but has a small amount of global configuration state)
 * any non-trivial wiring of render nodes, tracks, pipes and automation is done by the services of the [[connection manager|ConManager]]
 
-
+
+
The cooperation of several components creates a context of operation for the primary builder working tool, the [[node creator|PlanningNodeCreatorTool]]:
+* the BuilderToolFactory acts as the "builder for the builder tools", i.e. we can assume to be able to retrive all needed primary tools and elementary tools from this factory, completely configured and ready to use.
+* the [[Assembler|BuilderAssembler]] has the ability to consume objects from the high level model and feed them to the node creator (which translates into a dispatch of individual operations suited to the objects to be treated). This involves some sort of scheduling or ordering of the operaions, which is the only means to direct the overall process such as to create a sensible and usable result. //This is an fundamental design decision:// the actual working tools have no hard wired knowledge of the "right process", which makes the whole Builder highly configurable ("open").
+* the [[connection manager|ConManager]] on the contrary is a passive service provider. Fed with [[wiring requests|WiringRequest]], he can determine if a desired connection is possible, and what steps to take to implement it; the latter recursively creates further building requests to satisfy by the assembler, and possibly new wiring requests.
+
+!!pattern of operation
+The working pattern of this builder mechanics can be described as triggering, enqueuing, priorizing, recursing and exhausting. Without the priorizing part, it would be a depth-first call graph without any context state, forcing us to have all cross reference information available at every node or element to be treated. We prefer to avoid this overhead by ordering the operations into several phases and within these phases into correlated entities with the help of a ''weighting function'' and scheduling with a ''priority queue''
+
+
+
While assembling and building up the render engines node network, a small number of primitive building situations is encountered repeatedly. The BuilderToolKit provides a "fitting tool" for each of these situations, typically involving parametrisation and the application of a [[processing pattern|ProcPatt]].
+
+!List of elementary building situations
+!!!inserting an Effect or Plugin
+* participating: a Pipe and an Effect
+* result: Effect appended at the pipe's exit node
+
+!!!building a source connection
+* participating: source port of a clip, media access point, processing pattern
+* result: processing pattern has been //executed//, resulting in a chain of nodes from the source reader to the clip source port
+
+!!!wiring a general connection
+* participating: already verified connection request, providing a Pipe and an exit node; a processing pattern and a Placement
+* result: summation node prepended to the port of the pipe, processing pattern has been //executed// for building the connection from the exit node to the pipe's port, ParamProvider has been setup in accordance to the Placement.
+
+!!!attaching a transition 
+* participating: N pipe's exit nodes, transition
+* result: transition has been attached with the pipe's exit nodes, new wiring requests created attached to the transition's exit node(s)
+
+
+
* the MObjects implement //Buildable//
 * each Buildable can "receive" a Tool object and apply it
 * the different Tool objects are iterated/mapped onto the list of MObjects in the [[Timeline]]
@@ -769,10 +804,26 @@ As the builder has to create a render node network implementing most of the feat
 ** a given Tool instance can carry state while being iterated, so we don't need any global (or object-global) variables to hold the result of the build process
 
 This programming technique is often referred to as [["double dispatch" or "visitor"|VisitorUse]]. We use a specialized library implementation of this pattern &mdash; heavily inspired by the [[Loki library|http://loki-lib.sourceforge.net/]]. We use this approach not only for the builder, but also for carrying out operations on the objects in the EDL in a typesafe manner. 
-For the actual building operations see BasicBuildingOperations {{red{TODO:flesh out the actual Operations}}}
-    
-
+It is the low level foundation of the actual [[building operations|BasicBuildingOperations]] necessary to create render nodes starting from the given high level model. 
 [img[Entities cooperating in the Builder|uml/fig129285.png]]
+
+!Colaborations
+
+While building, the application of such a visiting tool (especially the [[NodeCreatorTool|PlanningNodeCreatorTool]]) is embedded into an execution context formed by the BuilderToolFactory providing our BuilderToolKit, the [[Assembler|BuilderAssembler]] and [[connection manager|ConManager]]. The colaboration of these parts can be seen as the [[mechanics of the builder|BuilderMechanics]] &mdash; sort of the //outward view//, contrary to the //invard aspects// visible when focussing on how the nodes are put together.
+    
+[img[Colaborations in the Build Process|uml/fig128517.png]]
+
+
+
+
+
Besides the primary working tool within the builder (namely the [[Node Creator Tool|PlanningNodeCreatorTool]]), on a lower level, we encounder several [[elementary building situations|BuilderPrimitives]] &mdash; and for each of these elementary situations we can retrieve a suitable "fitting tool". The palette of these fitting tools is called the ''tool kit'' of the builder. It is subject to configuration by rules.
+
+!! {{red{open questions}}}
+* how to address these fitting tools
+* how to type them
+* how to parametrize them
+
+&rarr;see also: BuilderPrimitives for the elementary corresponding to these fitting tools
 
From f3d078f93b1cdfacfdf789da650f2a733f2cc9db Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 23 May 2008 07:05:35 +0200 Subject: [PATCH 09/11] add drawing to explain the builder primitives --- doc/devel/draw/builder-primitives.svg | 2115 +++++++++++++++++++++++++ wiki/draw/builder-primitives1.png | Bin 0 -> 4777 bytes wiki/draw/builder-primitives2.png | Bin 0 -> 7087 bytes wiki/draw/builder-primitives3.png | Bin 0 -> 8197 bytes wiki/draw/builder-primitives4.png | Bin 0 -> 13071 bytes wiki/renderengine.html | 29 +- 6 files changed, 2136 insertions(+), 8 deletions(-) create mode 100644 doc/devel/draw/builder-primitives.svg create mode 100644 wiki/draw/builder-primitives1.png create mode 100644 wiki/draw/builder-primitives2.png create mode 100644 wiki/draw/builder-primitives3.png create mode 100644 wiki/draw/builder-primitives4.png diff --git a/doc/devel/draw/builder-primitives.svg b/doc/devel/draw/builder-primitives.svg new file mode 100644 index 000000000..aa1ae5801 --- /dev/null +++ b/doc/devel/draw/builder-primitives.svg @@ -0,0 +1,2115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + style="overflow:visible"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + Transition + + + + + + + + + + + + + + + x + + + + + + + Effect + + + + + + + + + + + + + + + + + + + x + + + + + + + + + + + Effect + + + + + + + + + + + + + + + x + + + + + + + + x + + + + + + + + + + + + + + + + + + + x + + + + + + + Transition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clip + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clip + + + + + + + + + + + + + + + + + + + + + + + Conv + + + + + + + Cam + + + + + + + + + + + + + + + + x + + + + + + + + + + + x + + + + + + + + + Con-Req + + + + + + + + + + Placement + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x + + + + + + + + ∑ + + + + + + + + + + + + + + + + + + + + Conv + + + + + + + + + + + + + Proj + + + + + + + + + + + + + + + + + + x + + + + + diff --git a/wiki/draw/builder-primitives1.png b/wiki/draw/builder-primitives1.png new file mode 100644 index 0000000000000000000000000000000000000000..986aa4227074a0e48673d1e9d3416062bfd850b7 GIT binary patch literal 4777 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 literal 0 HcmV?d00001 diff --git a/wiki/draw/builder-primitives3.png b/wiki/draw/builder-primitives3.png new file mode 100644 index 0000000000000000000000000000000000000000..f80b1f5c69a1624bbe254b195180f11dfac669d6 GIT binary patch literal 8197 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 M$|=kKkTLr7f7;m5IRF3v literal 0 HcmV?d00001 diff --git a/wiki/draw/builder-primitives4.png b/wiki/draw/builder-primitives4.png new file mode 100644 index 0000000000000000000000000000000000000000..6d60ddf2cc054d6a30d4bb4ab967d05c9bf3df0a GIT binary patch literal 13071 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 literal 0 HcmV?d00001 diff --git a/wiki/renderengine.html b/wiki/renderengine.html index b076f0d9b..93cf42061 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -773,25 +773,38 @@ As the builder has to create a render node network implementing most of the feat !!pattern of operation The working pattern of this builder mechanics can be described as triggering, enqueuing, priorizing, recursing and exhausting. Without the priorizing part, it would be a depth-first call graph without any context state, forcing us to have all cross reference information available at every node or element to be treated. We prefer to avoid this overhead by ordering the operations into several phases and within these phases into correlated entities with the help of a ''weighting function'' and scheduling with a ''priority queue''
-
-
While assembling and building up the render engines node network, a small number of primitive building situations is encountered repeatedly. The BuilderToolKit provides a "fitting tool" for each of these situations, typically involving parametrisation and the application of a [[processing pattern|ProcPatt]].
+
+
While assembling and building up the render engines node network, a small number of primitive building situations is encountered repeatedly. The BuilderToolKit provides a "[[mould|BuilderMould]]" for each of these situations, typically involving parametrisation and the application of a [[processing pattern|ProcPatt]].
 
 !List of elementary building situations
 !!!inserting an Effect or Plugin
+[>img[draw/builder-primitives1.png]]
 * participating: a Pipe and an Effect
 * result: Effect appended at the pipe's exit node
 
+@@clear(right):display(block):@@
+
+
+!!!attaching a transition 
+[>img[draw/builder-primitives2.png]]
+* participating: N pipe's exit nodes, transition
+* result: transition has been attached with the pipe's exit nodes, new wiring requests created attached to the transition's exit node(s)
+
+@@clear(right):display(block):@@
+
 !!!building a source connection
+[>img[draw/builder-primitives3.png]]
 * participating: source port of a clip, media access point, processing pattern
 * result: processing pattern has been //executed//, resulting in a chain of nodes from the source reader to the clip source port
 
+@@clear(right):display(block):@@
+
 !!!wiring a general connection
 * participating: already verified connection request, providing a Pipe and an exit node; a processing pattern and a Placement
 * result: summation node prepended to the port of the pipe, processing pattern has been //executed// for building the connection from the exit node to the pipe's port, ParamProvider has been setup in accordance to the Placement.
+[>img[draw/builder-primitives4.png]]
 
-!!!attaching a transition 
-* participating: N pipe's exit nodes, transition
-* result: transition has been attached with the pipe's exit nodes, new wiring requests created attached to the transition's exit node(s)
+@@clear(right):display(block):@@
 
@@ -815,11 +828,11 @@ While building, the application of such a visiting tool (especially the [[NodeCr
-
-
Besides the primary working tool within the builder (namely the [[Node Creator Tool|PlanningNodeCreatorTool]]), on a lower level, we encounder several [[elementary building situations|BuilderPrimitives]] &mdash; and for each of these elementary situations we can retrieve a suitable "fitting tool". The palette of these fitting tools is called the ''tool kit'' of the builder. It is subject to configuration by rules.
+
+
Besides the primary working tool within the builder (namely the [[Node Creator Tool|PlanningNodeCreatorTool]]), on a lower level, we encounder several [[elementary building situations|BuilderPrimitives]] &mdash; and for each of these elementary situations we can retrieve a suitable "fitting tool" or [[mould|BuilderMould]]. The palette of these moulds is called the ''tool kit'' of the builder. It is subject to configuration by rules.
 
 !! {{red{open questions}}}
-* how to address these fitting tools
+* how to address these moulds
 * how to type them
 * how to parametrize them
 

From 93908cf29ff8f983f38d45ae37b1e244503e56ec Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Sun, 25 May 2008 06:18:58 +0200
Subject: [PATCH 10/11] further planning of implementation details

---
 wiki/renderengine.html | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 93cf42061..94a0886b7 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -773,35 +773,51 @@ As the builder has to create a render node network implementing most of the feat
 !!pattern of operation
 The working pattern of this builder mechanics can be described as triggering, enqueuing, priorizing, recursing and exhausting. Without the priorizing part, it would be a depth-first call graph without any context state, forcing us to have all cross reference information available at every node or element to be treated. We prefer to avoid this overhead by ordering the operations into several phases and within these phases into correlated entities with the help of a ''weighting function'' and scheduling with a ''priority queue''
-
+
While assembling and building up the render engines node network, a small number of primitive building situations is encountered repeatedly. The BuilderToolKit provides a "[[mould|BuilderMould]]" for each of these situations, typically involving parametrisation and the application of a [[processing pattern|ProcPatt]].
 
+The ''Lifecycle'' of such a mould starts out by arming it with the object references involved into the next building step. After conducting this building step, the resulting render nodes can be found &mdash; depending on the situation &mdash; attached either to the same mould, or to another kind of mould, but in any case ready to included in the next building step. Thus, //effectively//&nbsp; the moulds are //used to handle the nodes being built,// due to the fact that the low-level model (nodes to be built) and the high-level model (objects directing what is to be built) are //never connected directly.//
+
 !List of elementary building situations
 !!!inserting an Effect or Plugin
 [>img[draw/builder-primitives1.png]]
+The __~PipeMould__ is used to chain up the effects attached to a clip (=local pipe) or global pipe (=bus)
 * participating: a Pipe and an Effect
+* point of reference: current exit node of the pipe
 * result: Effect appended at the pipe's exit node
+* returns: ~PipeMould holding onto the new exit node
 
 @@clear(right):display(block):@@
 
 
 !!!attaching a transition 
 [>img[draw/builder-primitives2.png]]
+After having completed N pipe's node chains, a __~CombiningMould__ can be used to join them into a [transition|TransitionsHandling]
 * participating: N pipe's exit nodes, transition
+* point of reference: N exit nodes corresponding to (completed) pipes
 * result: transition has been attached with the pipe's exit nodes, new wiring requests created attached to the transition's exit node(s)
+* returns: ~WiringMould, connected with the created wiring request
+Using this mould implicitly "closes" the involved pipes, which means that we give up any reference to the exit node and can't build any further effect attached to this pipes. Generally speaking, "exit node" isn't a special kind of node, rather it's a node we are currently holding on. Similarly, there is nothing directly correlated to a pipe within the render nodes network after we are done with building the part of the network corresponding to the pipe, which is used as a blueprint for building, but isn't an entity in the resulting low-level model.
+Actually, there is {{red{planned}}} a more general (and complicated) kind of transition, which can be inserted into N data connections without joining them together into one single output, as the standard transitions do. The ~CombiningMould can handle this case too by just returning N wiring moulds as a result.
 
 @@clear(right):display(block):@@
 
 !!!building a source connection
 [>img[draw/builder-primitives3.png]]
+The __~SourceChainMould__ is used as a starting point for any further building, as it results in a local pipe (=clip) rooted at the clip source port. This reflects the fact that the source readers (media access points) are the leaf nodes in the graph
 * participating: source port of a clip, media access point, processing pattern
+* point of reference: //none//
 * result: processing pattern has been //executed//, resulting in a chain of nodes from the source reader to the clip source port
+* returns: ~PipeMould holding onto the new exit node (of a yet-empty pipe)
 
 @@clear(right):display(block):@@
 
 !!!wiring a general connection
+Any wiring (outside the chain of effects within a pipe) is always done from exit nodes to the port of another pipe, requiring an [[wiring request|WiringRequest]] already checked and deemed resolvable. Within the __~WiringMould__ the actual wiring is conducted, possibly adding a summation node (called "overlayer" in case of video) and typically a fader element (the specific setup to be used is subject to configuration by processing patterns)
 * participating: already verified connection request, providing a Pipe and an exit node; a processing pattern and a Placement
+* points of reference: exit node and (optionally) starting point of a pipe's chain (in case there are already other connections)
 * result: summation node prepended to the port of the pipe, processing pattern has been //executed// for building the connection from the exit node to the pipe's port, ParamProvider has been setup in accordance to the Placement.
+* returns: ~PipeMould holding onto the destination pipe's exit node, ~WiringMould holding onto the port side of the same pipe, i.e. the destination where further connections will insert summation nodes. {{red{TODO how to handle the //empty//-case?}}}
 [>img[draw/builder-primitives4.png]]
 
 @@clear(right):display(block):@@
@@ -828,7 +844,7 @@ While building, the application of such a visiting tool (especially the [[NodeCr
 
 
-
+
Besides the primary working tool within the builder (namely the [[Node Creator Tool|PlanningNodeCreatorTool]]), on a lower level, we encounder several [[elementary building situations|BuilderPrimitives]] &mdash; and for each of these elementary situations we can retrieve a suitable "fitting tool" or [[mould|BuilderMould]]. The palette of these moulds is called the ''tool kit'' of the builder. It is subject to configuration by rules.
 
 !! {{red{open questions}}}
@@ -836,7 +852,7 @@ While building, the application of such a visiting tool (especially the [[NodeCr
 * how to type them
 * how to parametrize them
 
-&rarr;see also: BuilderPrimitives for the elementary corresponding to these fitting tools
+&rarr;see also: BuilderPrimitives for the elementary working situations corresponding to these fitting tools
 
@@ -2379,12 +2395,12 @@ The GUI can connect the viewer(s) to some pipe (and moreover can use [[probe poi
-
+
!Identification
 Pipes are distinct objects and can be identified by their asset ~IDs. Besides, as for all [[structural assets|StructAsset]] there are extended query capabilities, including a symbolic pipe-id and a media (stream) type id. Any pipe can accept and deliver exactly one media stream kind (which may be inherently structured though, e.g. spatial sound systems or stereoscopic video)
 
 !creating pipes
-Pipe assets are created automatically by being used and referred. The [[Session]] holds a collection of global pipes, and further pipes can be created by using a new pipe reference in some placement. Moreover, every clip has an (implicit) [[source port|ClipSourcePort]], which will appear as pipe asset when first used (referred) while [[building|BuildProcess]]. Note that creating a new pipe implies using a [[processing pattern|ProcPatt]], which will be queried from the [[Defaults Manager|DefaultsManagement]] (resulting in the use of some preconfigured pattern or maybe the creation of a new ProcPatt object if necessary)
+Pipe assets are created automatically by being used and referred. The [[Session]] holds a collection of global pipes ({{red{todo: implementation missing as of 5/08}}}), and further pipes can be created by using a new pipe reference in some placement. Moreover, every clip has an (implicit) [[source port|ClipSourcePort]], which will appear as pipe asset when first used (referred) while [[building|BuildProcess]]. Note that creating a new pipe implies using a [[processing pattern|ProcPatt]], which will be queried from the [[Defaults Manager|DefaultsManagement]] (resulting in the use of some preconfigured pattern or maybe the creation of a new ProcPatt object if necessary)
 
 !removal
 Deleting a Pipe is an advanced operation, because it includes finding and "detaching" all references, otherwise the pipe will leap back into existence immediately. Thus, global pipe entries in the Session and pipe references in [[locating pins|LocatingPin]] within any placement have to be removed, while clips using a given source port will be disabled. {{red{todo: implementation deferred}}}
@@ -2577,13 +2593,12 @@ Besides, they provide an important __inward interface__ for the [[ProcNode]]s, w
 
 
-
+
The Render Engine is the part of the application doing the actual video calculations. Its operations are guided by the Objects and Parameters edited by the user in [[the EDL|EDL]] and it retrieves the raw audio and video data from the [[Data backend|backend.html]]. Because the inner workings of the Render Engine are closely related to the structures used in the EDL, this design covers [[the aspect of objects placed into the EDL|MObjects]] as well.
 <<<
 ''Status'': started out as design draft in summer '07, Ichthyo is now in the middle of a implementing the foundations and main structures in C++
 * basic AssetManager working
 * currently impmenenting the Builder (&rarr;[[more|PlanningNodeCreatorTool]])
-* intermittently working out how to deal with &rarr; [[ConfigQueries / default objects|ConfigQueryIntegration]])
 <<<
 
 !Summary

From 2e58b02b8a1cc84a7f9769f46a6fab9ff9d79ecd Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 26 May 2008 07:28:10 +0200
Subject: [PATCH 11/11] write down implementation specs and further planned
 details

---
 src/proc/mobject/builder/assembler.cpp        |  6 +-
 src/proc/mobject/builder/assembler.hpp        |  6 +-
 src/proc/mobject/builder/conmanager.cpp       |  7 +-
 src/proc/mobject/builder/conmanager.hpp       |  6 +-
 src/proc/mobject/builder/mould.cpp            | 37 ++++++++
 src/proc/mobject/builder/mould.hpp            | 85 ++++++++++++++++++
 ...odecreatertool.cpp => nodecreatortool.cpp} |  4 +-
 ...odecreatertool.hpp => nodecreatortool.hpp} |  6 +-
 src/proc/mobject/builder/operationpoint.hpp   | 52 +++++++++++
 src/proc/mobject/builder/toolfactory.cpp      | 11 +--
 src/proc/mobject/builder/toolfactory.hpp      | 21 +++--
 src/proc/mobject/builder/wiringrequest.hpp    | 54 ++++++++++++
 wiki/renderengine.html                        | 87 +++++++++++++++----
 13 files changed, 327 insertions(+), 55 deletions(-)
 create mode 100644 src/proc/mobject/builder/mould.cpp
 create mode 100644 src/proc/mobject/builder/mould.hpp
 rename src/proc/mobject/builder/{nodecreatertool.cpp => nodecreatortool.cpp} (92%)
 rename src/proc/mobject/builder/{nodecreatertool.hpp => nodecreatortool.hpp} (92%)
 create mode 100644 src/proc/mobject/builder/operationpoint.hpp
 create mode 100644 src/proc/mobject/builder/wiringrequest.hpp

diff --git a/src/proc/mobject/builder/assembler.cpp b/src/proc/mobject/builder/assembler.cpp
index 8542860f3..7bad26aac 100644
--- a/src/proc/mobject/builder/assembler.cpp
+++ b/src/proc/mobject/builder/assembler.cpp
@@ -23,10 +23,8 @@
 
 #include "proc/mobject/builder/assembler.hpp"
 
-namespace mobject
-  {
-  namespace builder
-    {
+namespace mobject {
+  namespace builder {
 
 
 
diff --git a/src/proc/mobject/builder/assembler.hpp b/src/proc/mobject/builder/assembler.hpp
index f0274f171..c4900e2da 100644
--- a/src/proc/mobject/builder/assembler.hpp
+++ b/src/proc/mobject/builder/assembler.hpp
@@ -28,10 +28,8 @@
 
 
 
-namespace mobject
-  {
-  namespace builder
-    {
+namespace mobject {
+  namespace builder {
 
 
     /**
diff --git a/src/proc/mobject/builder/conmanager.cpp b/src/proc/mobject/builder/conmanager.cpp
index e32ecf92b..3ad9e4fce 100644
--- a/src/proc/mobject/builder/conmanager.cpp
+++ b/src/proc/mobject/builder/conmanager.cpp
@@ -23,11 +23,8 @@
 
 #include "proc/mobject/builder/conmanager.hpp"
 
-namespace mobject
-  {
-
-  namespace builder
-    {
+namespace mobject {
+  namespace builder {
 
 
 
diff --git a/src/proc/mobject/builder/conmanager.hpp b/src/proc/mobject/builder/conmanager.hpp
index 3cd75cc6d..2c234afdd 100644
--- a/src/proc/mobject/builder/conmanager.hpp
+++ b/src/proc/mobject/builder/conmanager.hpp
@@ -26,10 +26,8 @@
 
 
 
-namespace mobject
-  {
-  namespace builder
-    {
+namespace mobject {
+  namespace builder {
 
 
     /**
diff --git a/src/proc/mobject/builder/mould.cpp b/src/proc/mobject/builder/mould.cpp
new file mode 100644
index 000000000..1cc98441b
--- /dev/null
+++ b/src/proc/mobject/builder/mould.cpp
@@ -0,0 +1,37 @@
+/*
+  Mould  -  builder tool kit for the basic building situations
+ 
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+ 
+  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.
+ 
+* *****************************************************/
+
+
+#include "proc/mobject/builder/mould.hpp"
+
+namespace mobject {
+  namespace builder {
+
+
+
+    /** */
+
+
+
+  } // namespace mobject::builder
+
+} // namespace mobject
diff --git a/src/proc/mobject/builder/mould.hpp b/src/proc/mobject/builder/mould.hpp
new file mode 100644
index 000000000..6c714157a
--- /dev/null
+++ b/src/proc/mobject/builder/mould.hpp
@@ -0,0 +1,85 @@
+/*
+  MOULD.hpp  -  builder tool kit for the basic building situations
+ 
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+ 
+  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.
+ 
+*/
+
+
+#ifndef MOBJECT_BUILDER_MOULD_H
+#define MOBJECT_BUILDER_MOULD_H
+
+
+#include "proc/asset/procpatt.hpp"
+#include "proc/mobject/builder/operationpoint.hpp"
+
+#include 
+
+namespace mobject {
+  namespace builder {
+  
+    using std::vector;
+    using asset::PProcPatt;
+    using lumiera::Symbol;
+
+
+    /**
+     * Interface: a holder tool used by the builder to
+     * wire up a specific building situation and then to
+     * apply/execute a single building step. Mould is the 
+     * passive part, while usually the ProcPatt is the active
+     * counterpart. By means of the Mould interface, the specifics
+     * of a build situation are abstracted away, thus allowing the
+     * processing pattern to be defined as working on symbolic
+     * locations. The most common location is "current", denoting
+     * the render node just being built.
+     * 
  • PipeMould supports attaching an effect to a pipe
  • + *
  • combining pipes via a transition is done by a CombiningMould
  • + *
  • a SourceChainMould allows to start out from a source reader and build a clip
  • + *
  • wiring general connections is supported by the WiringMould
  • + */ + class Mould + { + public: + vector operate (); + + OperationPoint& getLocation (Symbol locationID); + }; + + + class PipeMould : public Mould + { + }; + + class CombiningMould : public Mould + { + }; + + class SourceChainMould : public Mould + { + }; + + class WiringMould : public Mould + { + }; + + + } // namespace mobject::builder + +} // namespace mobject +#endif diff --git a/src/proc/mobject/builder/nodecreatertool.cpp b/src/proc/mobject/builder/nodecreatortool.cpp similarity index 92% rename from src/proc/mobject/builder/nodecreatertool.cpp rename to src/proc/mobject/builder/nodecreatortool.cpp index 93362ef25..1d0acec05 100644 --- a/src/proc/mobject/builder/nodecreatertool.cpp +++ b/src/proc/mobject/builder/nodecreatortool.cpp @@ -1,5 +1,5 @@ /* - NodeCreaterTool - central Tool implementing the Renderengine building + NodeCreatorTool - central Tool implementing the Renderengine building Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -21,7 +21,7 @@ * *****************************************************/ -#include "proc/mobject/builder/nodecreatertool.hpp" +#include "proc/mobject/builder/nodecreatortool.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/effect.hpp" #include "proc/mobject/session/auto.hpp" diff --git a/src/proc/mobject/builder/nodecreatertool.hpp b/src/proc/mobject/builder/nodecreatortool.hpp similarity index 92% rename from src/proc/mobject/builder/nodecreatertool.hpp rename to src/proc/mobject/builder/nodecreatortool.hpp index e37d0d14f..616700a87 100644 --- a/src/proc/mobject/builder/nodecreatertool.hpp +++ b/src/proc/mobject/builder/nodecreatortool.hpp @@ -1,5 +1,5 @@ /* - NODECREATERTOOL.hpp - central Tool implementing the Renderengine building + NODECREATORTOOL.hpp - central Tool implementing the Renderengine building Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -21,8 +21,8 @@ */ -#ifndef MOBJECT_BUILDER_NODECREATERTOOL_H -#define MOBJECT_BUILDER_NODECREATERTOOL_H +#ifndef MOBJECT_BUILDER_NODECREATORTOOL_H +#define MOBJECT_BUILDER_NODECREATORTOOL_H #include "proc/mobject/builder/applicablebuildertargettypes.hpp" diff --git a/src/proc/mobject/builder/operationpoint.hpp b/src/proc/mobject/builder/operationpoint.hpp new file mode 100644 index 000000000..d48e9ecf6 --- /dev/null +++ b/src/proc/mobject/builder/operationpoint.hpp @@ -0,0 +1,52 @@ +/* + OPERATIONPOINT.hpp - abstraction representing the point where to apply a build instruction + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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. + +*/ + + +#ifndef MOBJECT_BUILDER_OPERATIONPOINT_H +#define MOBJECT_BUILDER_OPERATIONPOINT_H + + + + +namespace mobject { + namespace builder { + + + /** + * A point in the render node network under construction. + * By means of this unspecific reference, a ProcPatt is able + * to deal with this location and to execute a single elementary + * building operation denoted by a BuildInstruct at this point. + * Usually, the actual point is retrieved from a Mould + */ + class OperationPoint + { + public: + + }; + + + + } // namespace mobject::builder + +} // namespace mobject +#endif diff --git a/src/proc/mobject/builder/toolfactory.cpp b/src/proc/mobject/builder/toolfactory.cpp index 8e1b14631..06cbd114c 100644 --- a/src/proc/mobject/builder/toolfactory.cpp +++ b/src/proc/mobject/builder/toolfactory.cpp @@ -23,17 +23,12 @@ #include "proc/mobject/builder/toolfactory.hpp" -namespace mobject - { - namespace builder - { +namespace mobject { + namespace builder { - BuilderTool & - ToolFactory::configure () - { - } + /** */ diff --git a/src/proc/mobject/builder/toolfactory.hpp b/src/proc/mobject/builder/toolfactory.hpp index 2061b4fd2..5d30c9eea 100644 --- a/src/proc/mobject/builder/toolfactory.hpp +++ b/src/proc/mobject/builder/toolfactory.hpp @@ -25,19 +25,28 @@ #define MOBJECT_BUILDER_TOOLFACTORY_H #include "proc/mobject/builder/buildertool.hpp" +#include "proc/asset/pipe.hpp" +#include "proc/mobject/session/clip.hpp" +#include "proc/mobject/builder/wiringrequest.hpp" + +#include +namespace mobject { + namespace builder { -namespace mobject - { - namespace builder - { - + using std::vector; + using asset::PPipe; + using session::PClipMO; class ToolFactory { public: - BuilderTool & configure () ; + PipeMould& provideMould(PPipe const&); + CombiningMould& provideMould(vector&); + SourceChainMould& provideMould(PClipMO const&); + WiringMould& provideMould(WiringRequest const&); + }; diff --git a/src/proc/mobject/builder/wiringrequest.hpp b/src/proc/mobject/builder/wiringrequest.hpp new file mode 100644 index 000000000..ae1992832 --- /dev/null +++ b/src/proc/mobject/builder/wiringrequest.hpp @@ -0,0 +1,54 @@ +/* + WIRINGREQUEST.hpp - intention to send the output of a pipe to another pipe's port + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + 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. + +*/ + + +#ifndef MOBJECT_BUILDER_WIRINGREQUEST_H +#define MOBJECT_BUILDER_WIRINGREQUEST_H + + + + +namespace mobject { + namespace builder { + + + /** + * Request to make a connection. + * This connection may imply some data conversions, + * thus it has to be \link #isInvestigated chcked \endlink + * and may be deemed impossible. Otherwise, it can actually + * be carried out by the help of the WiringMould. + * @see ToolFactory#provideMould(WiringRequest const&); + */ + class WiringRequest + { + public: + bool isInvestigated(); + bool isPossible(); + }; + + + + } // namespace mobject::builder + +} // namespace mobject +#endif diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 94a0886b7..c55d698a2 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -773,10 +773,18 @@ As the builder has to create a render node network implementing most of the feat !!pattern of operation The working pattern of this builder mechanics can be described as triggering, enqueuing, priorizing, recursing and exhausting. Without the priorizing part, it would be a depth-first call graph without any context state, forcing us to have all cross reference information available at every node or element to be treated. We prefer to avoid this overhead by ordering the operations into several phases and within these phases into correlated entities with the help of a ''weighting function'' and scheduling with a ''priority queue''
-
+
+
The [[Builder]] uses different kinds of tools for creating a network of render nodes from a given high-level model. When breaking down this (necessarily complex) process into small manageable chunks, we arrive at [[elementary building situations|BuilderPrimitives]]. For each of these there is a specialized tool. We denote these tools as "moulds" because they are a rather passive holder for the objects to be attached and wired up. They are shaped according to the basic form the connections have to follow for each of these basic situations:
+* attaching an effect to a pipe
+* combining pipes via a transition
+* starting out a pipe from a source reader
+* general connections from the exit node of a pipe to the port of another pipe
+In all those cases, the active part is provided by [[processing patterns|ProcPatt]] &mdash; sort of micro programs executed within the context of a given mould: the processing pattern defines the steps to take (in the standard/basic case this is just "attach"), while the mould holds and provides the location where these steps will operate.
+
+
While assembling and building up the render engines node network, a small number of primitive building situations is encountered repeatedly. The BuilderToolKit provides a "[[mould|BuilderMould]]" for each of these situations, typically involving parametrisation and the application of a [[processing pattern|ProcPatt]].
 
-The ''Lifecycle'' of such a mould starts out by arming it with the object references involved into the next building step. After conducting this building step, the resulting render nodes can be found &mdash; depending on the situation &mdash; attached either to the same mould, or to another kind of mould, but in any case ready to included in the next building step. Thus, //effectively//&nbsp; the moulds are //used to handle the nodes being built,// due to the fact that the low-level model (nodes to be built) and the high-level model (objects directing what is to be built) are //never connected directly.//
+The ''Lifecycle'' of such a mould starts out by arming it with the object references involved into the next building step. After conducting this building step, the resulting render nodes can be found &mdash; depending on the situation &mdash; attached either to the same mould, or to another kind of mould, but in any case ready to be included in the next building step. Thus, //effectively//&nbsp; the moulds are //used to handle the nodes being built,// due to the fact that the low-level model (nodes to be built) and the high-level model (objects directing what is to be built) are //never connected directly.//
 
 !List of elementary building situations
 !!!inserting an Effect or Plugin
@@ -792,20 +800,20 @@ The __~PipeMould__ is used to chain up the effects attached to a clip (=local pi
 
 !!!attaching a transition 
 [>img[draw/builder-primitives2.png]]
-After having completed N pipe's node chains, a __~CombiningMould__ can be used to join them into a [transition|TransitionsHandling]
+After having completed N pipe's node chains, a __~CombiningMould__ can be used to join them into a [[transition|TransitionsHandling]]
 * participating: N pipe's exit nodes, transition
 * point of reference: N exit nodes corresponding to (completed) pipes
 * result: transition has been attached with the pipe's exit nodes, new wiring requests created attached to the transition's exit node(s)
 * returns: ~WiringMould, connected with the created wiring request
-Using this mould implicitly "closes" the involved pipes, which means that we give up any reference to the exit node and can't build any further effect attached to this pipes. Generally speaking, "exit node" isn't a special kind of node, rather it's a node we are currently holding on. Similarly, there is nothing directly correlated to a pipe within the render nodes network after we are done with building the part of the network corresponding to the pipe, which is used as a blueprint for building, but isn't an entity in the resulting low-level model.
+Using this mould implicitly "closes" the involved pipes, which means that we give up any reference to the exit node and can't build any further effect attached to this pipes. Generally speaking, "exit node" isn't a special kind of node, rather it's a node we are currently holding on. Similarly, there is nothing directly correlated to a pipe within the render nodes network after we are done with building the part of the network corresponding to the pipe; the latter serves rather as a blueprint for building, but isn't an entity in the resulting low-level model.
 Actually, there is {{red{planned}}} a more general (and complicated) kind of transition, which can be inserted into N data connections without joining them together into one single output, as the standard transitions do. The ~CombiningMould can handle this case too by just returning N wiring moulds as a result.
 
 @@clear(right):display(block):@@
 
 !!!building a source connection
 [>img[draw/builder-primitives3.png]]
-The __~SourceChainMould__ is used as a starting point for any further building, as it results in a local pipe (=clip) rooted at the clip source port. This reflects the fact that the source readers (media access points) are the leaf nodes in the graph
-* participating: source port of a clip, media access point, processing pattern
+The __~SourceChainMould__ is used as a starting point for any further building, as it results in a local pipe (=clip) rooted at the clip source port. This reflects the fact that the source readers (=media access points) are the //leaf nodes// in the node graph we are about to build.
+* participating: source port of a clip, media access point, [[processing pattern|ProcPatt]]
 * point of reference: //none//
 * result: processing pattern has been //executed//, resulting in a chain of nodes from the source reader to the clip source port
 * returns: ~PipeMould holding onto the new exit node (of a yet-empty pipe)
@@ -816,7 +824,7 @@ The __~SourceChainMould__ is used as a starting point for any further building,
 Any wiring (outside the chain of effects within a pipe) is always done from exit nodes to the port of another pipe, requiring an [[wiring request|WiringRequest]] already checked and deemed resolvable. Within the __~WiringMould__ the actual wiring is conducted, possibly adding a summation node (called "overlayer" in case of video) and typically a fader element (the specific setup to be used is subject to configuration by processing patterns)
 * participating: already verified connection request, providing a Pipe and an exit node; a processing pattern and a Placement
 * points of reference: exit node and (optionally) starting point of a pipe's chain (in case there are already other connections)
-* result: summation node prepended to the port of the pipe, processing pattern has been //executed// for building the connection from the exit node to the pipe's port, ParamProvider has been setup in accordance to the Placement.
+* result: summation node prepended to the port of the pipe, processing pattern has been //executed// for building the connection from the exit node to the pipe's port, ParamProvider has been setup in [[accordance|PlacementDerivedDimension]] to the Placement.
 * returns: ~PipeMould holding onto the destination pipe's exit node, ~WiringMould holding onto the port side of the same pipe, i.e. the destination where further connections will insert summation nodes. {{red{TODO how to handle the //empty//-case?}}}
 [>img[draw/builder-primitives4.png]]
 
@@ -844,14 +852,23 @@ While building, the application of such a visiting tool (especially the [[NodeCr
 
 
-
-
Besides the primary working tool within the builder (namely the [[Node Creator Tool|PlanningNodeCreatorTool]]), on a lower level, we encounder several [[elementary building situations|BuilderPrimitives]] &mdash; and for each of these elementary situations we can retrieve a suitable "fitting tool" or [[mould|BuilderMould]]. The palette of these moulds is called the ''tool kit'' of the builder. It is subject to configuration by rules.
+
+
Besides the primary working tool within the builder (namely the [[Node Creator Tool|PlanningNodeCreatorTool]]), on a lower level, we encounter several [[elementary building situations|BuilderPrimitives]] &mdash; and for each of these elementary situations we can retrieve a suitable "fitting tool" or [[mould|BuilderMould]]. The palette of these moulds is called the ''tool kit'' of the builder. It is subject to configuration by rules.
 
 !! {{red{open questions}}}
 * how to address these moulds
 * how to type them
 * how to parametrize them
 
+!!addressing a mould
+All mould instances are owned and managed by the [[tool factory|BuilderToolFactory]], and can be referred to by their type ({{{PipeMould}}}, {{{CombiningMould}}}, {{{SourceChainMould}}}, {{{WiringMould}}}) and a concrete object instance (of suitable type). The returned mould (instance) acts as a handle to stick together the given object instance (from the high-level model) with the corresponding point in the low-level node network under construction. As consequence of this approach, the tool factory instance holds a snapshot of the current building state, including all the active spots in the build process. As the latter is driven by objects from the high-level model appearing (in a sensible order &rarr; see BuilderMechanics) within the NodeCreatorTool, new moulds will be created and fitted as necessary, and existing moulds will be exhausted when finished, until the render node network is complete.
+
+!!configuring a mould
+As each mould kind is different, it has a {{{prepare(...)}}} function with suitably typed parameters. The rest is intended to be  self-configuring (for example, a ~CombiningMould will detect the actual kind of Transition and select the internal mode of operation), so that it's sufficient to just call {{{operate()}}}
+
+!!sequence of operations
+When {{{operate()}}} doesn't throw, the result is a list of //successor moulds// &mdash; you shouldn't use the original mould after triggering its operation, because it may have been retracted as a result and reused for another purpose by the tool factory. It is not necessary to store these resulting moulds either (as they can be retrieved as described above), but they can be used right away for the next building step if applicable. In the state they are returned from a successful building step (mould operation = execution of a contained [[processing pattern|ProcPatt]]), they are usually already holding a reference to the part of the network just created and need to be configured only with the next high-level object (effect, placement, pipe, processing pattern or similar, depending on the concrete situation) in order to carry out the next step.
+
 &rarr;see also: BuilderPrimitives for the elementary working situations corresponding to these fitting tools
 
@@ -2410,17 +2427,37 @@ there is not much you can do directly with a pipe asset. It is an point of refer Pipes are integrated with the [[management of defaults|DefaultsManagement]]. For example, any pipe uses implicitly some [[processing pattern|ProcPatt]] &mdash; it may default to the empty pattern. This feature enables to apply some standard wiring to the pipes (e.g a fader for audio, similar to the classic mixing consoles). This //is // a global property of the pipe, but &mdash; contrary to the stream type &mdash; this pattern may be switched
-
-
A Placement represents a //relation:// it is always linked to a //Subject// (this being a [[Media Object|MObject]]) and has the meaning to //place// this Subject in some manner, either relatively to other Media Objects, or by some Constraint or simply absolute at (time,track). The latter case is especially important and represented by a special [[Sub-Interface|ExplicitPlacement]]
+
+
A Placement represents a //relation:// it is always linked to a //Subject// (this being a [[Media Object|MObject]]) and has the meaning to //place// this Subject in some manner, either relatively to other Media Objects, or by some Constraint or simply absolute at (time, output). The latter case is especially important for the build process and thus represented by a special [[Sub-Interface ExplicitPlacement|ExplicitPlacement]]. Besides this simple cases, Placements can also express more specific kinds of "locating" an object, like placing a sound source at a pan position or placing a video clip at a given layer (above or below another video clip)
+
+So basically placements represent a query interface: you can allways ask the placement to find out about the position of the related object in terms of (time, output), and &mdash; depending on the specific object and situation &mdash; also about these additional [[placement derived dimensions|PlacementDerivedDimension]] like sound pan or layer order or similar things which also fit into the general concept of "placing" an object.
 
 The fact of being placed in the [[Session|SessionOverview]]/[[EDL]]is constitutive for all sorts of [[MObject]]s, without Placement they make no sense. Thus &mdash; technically &mdash; Placements act as ''smart pointers''. Of course, there are several kinds of Placements and they are templated on the type of MObject they are refering to. Placements can be //aggregated// to increasingly constrain the resulting "location" of the refered ~MObject. See &rarr; [[handling of Placements|PlacementHandling]] for more details
-
+
+
For any [[media object|MObject]] within the session, we can allways at least query the time (reference/start) point and the output destination from the [[Placement]], by which the object is being handled. But the simple act of placing an object in some way, can &mdash; depending on the context &mdash; create additional degrees of freedom. To list some important examples:
+* placing a video clip overlapping with other clips on other tracks creates the possibility for the clip to be above another clip or to be combined in various other ways with the other clips at the same time position
+* placing a mono sound object plugged to a stereophoic output destination creates the freedom to define the pan position
+The Placement interface allows to query for these additional //parameter values derived from the fact of being placed.//
+
+!defining additional dimensions
+probably a LocatingPin but... {{red{TODO any details are yet unknown as of 5/08}}}
+!querying additional dimensions
+basically you resolve the Placement, yielding an ExplicitPlacement... {{red{TODO but any details of how additional dimensions are resolved is still undefined as of 5/08}}}
+
+
[[Placement]]s are at the very core of all [[editing operations|EditingOperations]], because they act as handles (smart pointers) to access the [[media objects|MObject]] to be manipulated. Moreover, Placements are the actual content of the EDL(s) and Fixture and thus are small objects with //value semantics//. Many editing tasks include finding some Placement in the EDL or directly take a ref to some Placement. By acting on the Placement object, we can in some cases change parameters of the way the media object is placed (e.g. adjust an offset), while by dereferencing the Placement object, we access the "real" media object (e.g. for trimming its length). Placements are ''templated'' on the type of the actual ~MObject they refer to, thus defining the interface/methods usable on this object.
 
-Actually, the way each Placement locates its subject is implemented by one or several small LocatingPin objects, where subclasses of LocatingPin implement the various differend methods of placing and resolving the final location. Notably, we can give a ~FixedLocation or we can atach to another ~MObject to get a ~RelativeLocation, etc.
+Actually, the way each Placement locates its subject is implemented by one or several small LocatingPin objects, where subclasses of LocatingPin implement the various different methods of placing and resolving the final location. Notably, we can give a ~FixedLocation or we can atach to another ~MObject to get a ~RelativeLocation, etc. In the typical use case, these ~LocatingPins are added to the Placement, but never retrieved directly. Rather the Placement acts as a ''query interface'' for determining the location of the related object. Here, "location" can be thought of as encompassing multiple dimenstions at the same time. An object can be
+* located at a specific point in time
+* related to and plugged into a specific output or global bus
+* defined to have a position within some [[context-dependant additional dimensions|PlacementDerivedDimension]] like
+** the pan position, either on the stereophoic base, or within a fully periphoic (spatial) sound system
+** the layer order and overlay mode for video (normal, additive, subtractive, masking)
+** the stereoscopic window position (depth parameter) for 3D video
+** channel and parameter selection for MIDI data
 
-Placements have //value semantics,// i.e. we don't stress the identity of a placement object (~MObjects on the other hand //do have// a distinguishable identity): initially, you create a Placement parametrized to some specific kind (fixed, relative,...), but later on, you treat the placement polymorphically and don't care about its kind. The sole purpose of the placement's kind is to select some virtual function implementing the desired behaviour. There is no limitation to one single Placement per ~MObject, indeed we have several different Placements of the same MObject (from a users point of view, this behaves like having clones). Besides, we can ''aggregate'' additional [[LocatingPin]]a to one Placements, resulting in their properties and constraints being combined to yield the actual position of the referred ~MObject.
+Placements have //value semantics,// i.e. we don't stress the identity of a placement object (~MObjects on the other hand //do have// a distinguishable identity): initially, you create a Placement parametrized to some specific kind by adding [[LocatingPin]]s (fixed, relative,...) and possibliy you use a subclass of {{{Placement<MObject>}}} to encode additional type information, say {{{Placement<Clip>}}}, but later on, you treat the placement polymorphically and don't care about its kind. The sole purpose of the placement's kind is to select some virtual function implementing the desired behaviour. There is no limitation to one single Placement per ~MObject, indeed we can have several different Placements of the same MObject (from a users point of view, these behave like being clones). Besides, we can ''aggregate'' additional [[LocatingPin]]s to one Placements, resulting in their properties and constraints being combined to yield the actual position of the referred ~MObject.
 
 !design decisions
 * the actual way of placing is implemented similar to the ''State Pattern'' by small embedded LocatingPin objects.
@@ -2624,17 +2661,29 @@ The system is ''open'' inasmuch every part mirrors the structure of correspondin
 
A data processing node within the Render Engine. Its key feature is the possibility to pull from it one (freely addressable) [[Frame]] of calculated data. Further, each ~ProcNode has the ability to be wired with other nodes and [[Parameter Providers|ParamProvider]]
 
-
-
This special type of [[structural Asset|StructAsset]] represents information how to build some part of the render engine's processing nodes network. It can be thought of as a template or blueprint for construction. Most notably, it is used for creating nodes reading, decoding and delivering source media material to the render network. Each [[media Asset|MediaAsset]] has associated processing patterns describing the codecs and other transformations needed to get at the media data of this asset. (and because media assets are typically compound objects, the referred ~ProcPatt will be compound too). Obviously, the possibilities opened by using processing patterns go far beyond.
+
+
This special type of [[structural Asset|StructAsset]] represents information how to build some part of the render engine's processing nodes network. Processing patterns can be thought of as a blueprint or micro program for construction. Most notably, they are used for creating nodes reading, decoding and delivering source media material to the render network, and they are used for building the output connection via faders, summation or overlay nodes to the global pipes (busses). Each [[media Asset|MediaAsset]] has associated processing patterns describing the codecs and other transformations needed to get at the media data of this asset. (and because media assets are typically compound objects, the referred ~ProcPatt will be compound too). Similarily, for each stream kind, we can retrieve a processing pattern for making output connections. Obviously, the possibilities opened by using processing patterns go far beyond.
 
 Technically, a processing pattern is a list of building instructions, which will be //executed// by the [[Builder]] on the render node network under construction. This implies the possibility to define further instruction kinds when needed in future; at the moment the relevant sorts of instructions are
 * attach the given sequence of nodes to the specified point
 * recursively execute a nested ~ProcPatt
-More specifically, a sequence of nodes is given by a sequence of prototypical effect and codec assets (and from each of them we can create the corresponding render node). And the point to attach these nodes is given by an identifier &mdash; in most cases just "{{{current}}}", denoting the point the builder was just working at, when he treated some MObject which in turn yielded this processing pattern in question.
+More specifically, a sequence of nodes is given by a sequence of prototypical effect and codec assets (and from each of them we can create the corresponding render node). And the point to attach these nodes is given by an identifier &mdash; in most cases just "{{{current}}}", denoting the point the builder is currently working at, when treating some placed ~MObject which in turn yielded this processing pattern in question.
 
-Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming scheme within the asset name field, which directly mirrors its purpose and allows to bind to existing processing pattern instances when needed. The idea is letting all assets in need of a similar processing pattern refer to one shared ~ProcPatt instance. For example, within a MPEG video media asset, at some point there will be a ~ProcPatt labeled "{{{stream(mpeg)}}}". In consequence, all MPEG video will use the same pattern of node wiring. And, of course, this pattern could be changed, either globally, or by binding a single clip to some other processing pattern (for making a punctual exception from the general rule)
+Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming scheme within the asset name field, which directly mirrors its purpose and allows to bind to existing processing pattern instances when needed. {{red{TODO: that's just the general idea, but really it will rather use some sort of tags. Yet undefined as of 5/08}}} The idea is letting all assets in need of a similar processing pattern refer to one shared ~ProcPatt instance. For example, within a MPEG video media asset, at some point there will be a ~ProcPatt labeled "{{{stream(mpeg)}}}". In consequence, all MPEG video will use the same pattern of node wiring. And, of course, this pattern could be changed, either globally, or by binding a single clip to some other processing pattern (for making a punctual exception from the general rule) 
+
+!!defining Processing Patterns
+The basic working set of processing patterns can be expected to be just there (hard wired or default configuration, mechanism for creating an sensible fallback). Besides, the idea is that new processing patterns can be added via rules in the session and then referred to by other rules controlling the build process. Any processing pattern is assembled by adding individual build instructions, or by including another (nested) processing pattern.
+
+!!retrieving a suitable Processing Pattern
+For a given situation, the necessary ProcPatt can be retrieved by issuing a [[configuration query|ConfigQuery]]. This query should include the needed capabilities in predicate form (technically this query is a Prolog goal), but it can leave out informations by just requesting "the default" &rarr; see DefaultsManagement
+
+!!how does this actually work?
+Any processing pattern needs the help of a passive holder tool suited for a specific [[building situation|BuilderPrimitives]]; we call these holder tools [[building moulds|BuilderMould]]. Depending on the situation, the mould has been armed up by the builder with the involved objects to be connected and extended. So, just by issuing the //location ID// defined within the individual build instruction (in most cases simply {{{"current"}}}), the processing pattern can retrieve the actual render object to use for building from the mould it is executed in.
+
+!!errors and misconfiguration
+Viewed as a micro program, the processing patterns are ''weak typed'' &mdash; thus providing the necessary flexibility within an otherwise strong typed system. Consequently, the builder assumes they are configured //the right way// &mdash; and will just bail out when this isn't the case, marking the related part of the high-level model as erroneous.
+&rarr; see BuilderErrorHandling for details
 
-//Note,// nothing has been said about how processing patterns are defined. One can expect for some //default patterns// beeing defined somewhere in the application, any session could overrule these defaults, and when there is no default, we can expect some mechanism deriving sensible fallback patterns //for every specific use case// of processing patterns. See the problem of [[Loading Media]] as an example.