diff --git a/src/proc/mobject/mobject-ref.cpp b/src/proc/mobject/mobject-ref.cpp index e711b26a8..a66250f1a 100644 --- a/src/proc/mobject/mobject-ref.cpp +++ b/src/proc/mobject/mobject-ref.cpp @@ -48,6 +48,7 @@ namespace mobject { LUMIERA_ERROR_DEFINE (INVALID_PLACEMENTREF, "unresolvable placement reference, or of incompatible type"); LUMIERA_ERROR_DEFINE (BOTTOM_PLACEMENTREF, "NIL placement-ID marker encountered."); + LUMIERA_ERROR_DEFINE (BOTTOM_MOBJECTREF, "NIL MObjectRef encountered"); diff --git a/src/proc/mobject/mobject-ref.hpp b/src/proc/mobject/mobject-ref.hpp index 17bb483b6..f91c978f2 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -22,9 +22,30 @@ /** @file mobject-ref.hpp - ** A reference handle pointing at an MObject, attached (placed) to the session. - ** Allows client code to access both the object and the placement, even from a - ** separate Layer or from within plugin code. + ** External MObject/Placement reference. + ** This smart-handle referres to an MObject, attached (placed) into the session. + ** It is a copyable value object, implemented by an LUID (hash) and an shared_ptr. + ** Holding an MObject ref keeps the referred MObject alive, but gives no guarantees + ** regarding the validity of the referred \em Placement within the session. On each + ** access, the placement is re-fetched using the PlacementIndex, which may fail. + ** + ** MObjectRef allows to access the pointee (MObject subclass) by using the dereferentiation + ** operator, and it allows to access the Placement within the session. Moreover, as an + ** convenience shortcut, some of Placement's query operations are directly exposed. + ** + ** !Lifecycle + ** An MObjectRef is always created inactive. It needs to be activated explicitly, + ** providing either a direct (language) ref to an Placement within the session, + ** or an PlacementRef tag, or another MObjecRef. It can be closed (detached). + ** + ** !Type handling + ** Like any smart-ptr MObjectRef is templated on the actual type of the pointee. + ** It can be built or re-assigned from a variety of sources, given the runtime type + ** of the referred pointee is compatible to this template parameter type. This + ** allows flexibly to re-gain a specifically typed context, even based just + ** on a plain LUID. This functionality is implemented by accessing the + ** PlacementIndex within the session, and then by using the RTTI of + ** the fetched Placement's pointee. ** ** @see MObject ** @see Session @@ -39,24 +60,23 @@ #define MOBJECT_MOBJECT_REF_H //#include "pre.hpp" -//#include "proc/mobject/session/locatingpin.hpp" -//#include "proc/asset/pipe.hpp" #include "lib/handle.hpp" +#include "lib/lumitime.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" -//#include ///////////////////////////////////////////TODO: define an C-API representation here, make the header multilingual! namespace mobject { -// using std::tr1::shared_ptr; class MObject; + LUMIERA_ERROR_DECLARE (BOTTOM_MOBJECTREF); ///< NIL MObjectRef encountered + /** * An active (smart-ptr like) external reference * to a specifically placed MObject "instance" within the session. @@ -69,29 +89,43 @@ namespace mobject { class MORef : public lib::Handle { - typedef lib::Handle _Par; + typedef lib::Handle _Handle; + + + PlacementRef pRef_; + using _Handle::smPtr_; - PlacementRef pRef_; ////////////////////////////////////////////////////////////////////TODO: how to create an "inactive" PlacementRef??? - using _Par::smPtr_; public: MO* operator-> () const { - REQUIRE (pRef_ && smPtr_, "Lifecycle-Error: not activated"); + if (!smPtr_) + throw lumiera::error::State("Lifecycle error: MObject ref not activated" + ,LUMIERA_ERROR_BOTTOM_MOBJECTREF); + ENSURE (INSTANCEOF (MO, smPtr_.get())); return smPtr_.operator-> (); } Placement& getPlacement() const { - REQUIRE (pRef_ && smPtr_, "Lifecycle-Error: not activated"); ENSURE (INSTANCEOF (MO, smPtr_.get())); return *pRef_; } + /** resolves the referred placement to an + * ExplicitPlacement and returns the found start time + */ + lumiera::Time const& + getStartTime() + { + return pRef_.resolve().time; + } + + /* === Lifecycle === */ @@ -119,7 +153,7 @@ namespace mobject { * - a plain LUID * @throws error::Invalid when the (directly or indirectly * referred placement isn't known to the session PlacementIndex, - * or when the placement actually found has an incompatible dynamic type + * or when the placement actually found has an incompatible dynamic type */ template MORef& @@ -153,16 +187,46 @@ namespace mobject { return activate (oRef.getPlacement().getID()); } + /** cross assignment. + * @note besides that, we use the default generated copy operations. + * @note STRONG exception safety guarantee + * @throws error::Invalid when the referred placement isn't registered + * within the current session, or if the runtime type of the pointees + * aren't assignment compatible + */ + template + MORef& + operator= (MORef const& oRef) + { + return activate (oRef); + } + /* == diagnostics == */ + bool + isValid() const + { + return pRef_.isValid(); + } + size_t use_count() const { return pRef_.use_count(); } - + + template + bool + isCompatible() const + { + return pRef_ + && (*pRef_).isCompatible(); + } + + + /* == equality comparisons == */ @@ -221,7 +285,6 @@ namespace mobject { } }; - ////////////////TODO currently just fleshing out the API.... typedef MORef MObjectRef; diff --git a/tests/components/proc/mobject/mobject-ref-test.cpp b/tests/components/proc/mobject/mobject-ref-test.cpp index 4c19041f7..b202636eb 100644 --- a/tests/components/proc/mobject/mobject-ref-test.cpp +++ b/tests/components/proc/mobject/mobject-ref-test.cpp @@ -52,7 +52,7 @@ namespace test { using session::SessionServiceMockIndex; using session::PPIdx; - + /*************************************************************************** * @test properties and behaviour of the external reference-mechanism for @@ -116,7 +116,6 @@ namespace test { PlacementRef ref2 (pClip2); -#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! // -----Tests------------------ checkBuildMObjectRef (rP1, &pClip1); checkBuildMObjectRef (rP2, &pClip2); @@ -131,7 +130,6 @@ namespace test { checkLifecycle (rP1,rP2); checkTypeHandling (luid); // -----Tests------------------ -#endif ////////////////////////////////////////////////////////////////////////////////////////TODO lots of things unimplemented.....!!!!! // verify clean state index->remove (pClip1); @@ -168,30 +166,28 @@ namespace test { // access the Placement-API ASSERT (3 == rMO.use_count()); // now rMO shares ownership with the Placement -#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! - ASSERT (0 < rMO.getStartTime()); // (internally, this resolves to an ExplicitPlacement) /////////TICKET #332 + ASSERT (Time(0) < rMO.getStartTime()); // (internally, this resolves to an ExplicitPlacement) /////////TICKET #332 ASSERT ( rMO.isCompatible()); ASSERT ( rMO.isCompatible()); ASSERT (!rMO.isCompatible()); Time start = rMO.getStartTime(); -#endif ////////////////////////////////////////////////////////////////////////////////////////TODO lots of things unimplemented.....!!!!! // re-link to the Placement (note we get the Clip API!) Placement & refP = rMO.getPlacement(); - ASSERT (refP); + ASSERT (refP.isValid()); ASSERT (3 == refP.use_count()); ASSERT (&refP == placementAdr); // actually denotes the address of the original Placement in the "session" cout << string(refP) << endl; ExplicitPlacement exPla = refP.resolve(); -// ASSERT (exPla.time == start); // recovered Placement resolves to the same time as provided by the proxied API + ASSERT (exPla.time == start); // recovered Placement resolves to the same time as provided by the proxied API ASSERT (4 == refP.use_count()); // but now we've indeed created an additional owner (exPla) ASSERT (4 == rMO.use_count()); } void - checkComparison (PMO& p1, PMO& p2) + checkComparison (PMO const& p1, PMO const& p2) { PlacementRef pRef1 (p1); PlacementRef pRef2 (p2); @@ -258,7 +254,7 @@ namespace test { void - checkLifecylce (PMObj const& p1, PMObj const& p2) + checkLifecycle (PMObj const& p1, PMObj const& p2) { ASSERT (2 == p1.use_count()); ASSERT (2 == p2.use_count()); @@ -290,7 +286,7 @@ namespace test { ASSERT (2 == p2.use_count()); VERIFY_ERROR (INVALID_PLACEMENTREF, rMO.getPlacement() ); -// VERIFY_ERROR (INVALID_MOBJECTREF, rMO->getMedia() ); + VERIFY_ERROR (BOTTOM_MOBJECTREF, rMO->getMedia() ); } void @@ -316,9 +312,7 @@ namespace test { // impossible, because Clip isn't a subclass of TestSubMO1: VERIFY_ERROR (INVALID_PLACEMENTREF, rSub1.activate(luid) ); -#if false //////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET 384 !!!!!!!!! -// VERIFY_ERROR (INVALID_PLACEMENTREF, rSub1 = rMObj ); -#endif ////////////////////////////////////////////////////////////////////////////////////////TODO lots of things unimplemented.....!!!!! + VERIFY_ERROR (INVALID_PLACEMENTREF, rSub1 = rMObj ); ASSERT (rMObj->isValid()); ASSERT (rClip->isValid()); @@ -327,12 +321,12 @@ namespace test { // doesn't compile, because the function isn't on MObject API: // rMObj->getMedia(); - // can assign, because the actual type is relevant: rClip.close(); ASSERT (3 == rMObj.use_count()); ASSERT (0 == rClip.use_count()); -// rClip = rMObj; + // can assign, because the actual type checked: + rClip = rMObj; ASSERT (4 == rMObj.use_count()); ASSERT (4 == rClip.use_count());