/* PLACEMENT-REF.hpp - generic reference to an individual placement added to the session Copyright (C) Lumiera.org 2009, 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 placement-ref.hpp ** ** @note there is a twist concerning the nominal Placement hierarchy ** as generated by the return type of PlacementRef::operator*(). ** While the original Placement (as added to the session) might been ** defined to mimic a more elaborate hierarchy (e.g. Placement ** inherits from Placement), the Placement returned here in ** these cases will just be a subclass or Placement ** (which in the mentioned example would mean it couldn't be ** passed to a API function expecting a Placement). ** This is uggly, but doesn't seem to bear any danger. ** ** @see Placement ** @see PlacementRef_test ** */ #ifndef MOBJECT_PLACEMENT_REF_H #define MOBJECT_PLACEMENT_REF_H //#include "pre.hpp" //#include "proc/mobject/session/locatingpin.hpp" //#include "proc/asset/pipe.hpp" #include "lib/error.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/explicitplacement.hpp" /////////////TODO this is ugly! Why can't placement::resolve() return a reference?? //#include namespace mobject { // using std::tr1::shared_ptr; class MObject; // see placement-index.cpp Placement & fetch_PlacementIndex(Placement::ID const&) ; bool checkContains_PlacementIndex (Placement::ID const& pID) ; LUMIERA_ERROR_DECLARE (INVALID_PLACEMENTREF); ///< unresolvable placement reference, or of incompatible type /** */ template class PlacementRef { typedef Placement PlacementMO; typedef Placement::ID _ID; ////TODO: could we define const& here?? typedef Placement::Id _Id; _Id id_; public: /** * Creating a PlacementRef from a compatible reference source. * Any source allowing to infer a \em compatible mobject::Placement * is accepted. Here, compatibility is decided based on the run time * type of the pointee, in comparison to the template parameter Y. * In any case, for this ctor to succeed, the provided ref or ID * needs to be resolvable to a placement by the implicit PlacementIndex * facility used by all PlacementRef instances (typically the Session). * @note there is no default ctor, a reference source is mandatory. * @param refID reference resolvable to a placement via Index, especially * - an existing Placement * - just an Placement::ID * - a plain LUID * @throw error::Invalid on incompatible run time type of the resolved ID */ template explicit PlacementRef (Y const& refID) : id_(recast (refID)) { validate(id_); } PlacementRef (PlacementRef const& r) ///< copy ctor : id_(r.id_) { validate(id_); } template PlacementRef (PlacementRef const& r) ///< extended copy ctor, when type X is assignable to MO : id_(recast(r)) { validate(id_); } PlacementRef& operator= (PlacementRef const& r) { validate(r.id_); id_ = r.id_; return *this; } template PlacementRef& operator= (PlacementRef const& r) { validate(recast (r)); id_ = recast(r); return *this; } template PlacementRef& operator= (Y const& refID) { validate (recast (refID)); id_ = recast (refID); return *this; } /* == forwarding smart-ptr operations == */ PlacementMO& operator*() const { return access(id_); } ///< dereferencing fetches referred Placement from Index PlacementMO& operator->() const { return access(id_); } ///< provide access to pointee API by smart-ptr chaining operator string() const { return access(id_).operator string(); } size_t use_count() const { return access(id_).use_count(); } /* == accessing the embedded ID == */ operator _Id const&() const { return id_; } LumieraUid getLUID() const { return id_.get(); } template bool operator== (PlacementRef const& o) const { return id_ == o; } template bool operator!= (PlacementRef const& o) const { return id_ != o; } typedef _Id PlacementRef::*__unspecified_bool_type; /** implicit conversion to "bool" */ operator __unspecified_bool_type() const { return isValid()? &PlacementRef::id_ : 0; } // never throws bool operator! () const { return !isValid(); } // ditto /* == forwarding part of the Placement-API == */ bool isValid() const { if (checkValidity()) try { return access(id_).isValid(); } catch (lumiera::error::Invalid&) {} return false; } ExplicitPlacement resolve() const { return access(id_).resolve();} ////////////////TODO more operations to come.... private: bool checkValidity () const { return checkContains_PlacementIndex(this->id_); } static void validate (_Id const& rId) { PlacementMO& pRef (access (rId)); if (!(pRef.template isCompatible())) throw lumiera::error::Invalid("actual type of the resolved placement is incompatible",LUMIERA_ERROR_INVALID_PLACEMENTREF); ////////////////////////TODO: 1. better message, including type? ////////////////////////TODO: 2. define a separate error-ID for the type mismatch! } static _Id const& recast (_ID const& someID) { return static_cast<_Id const&> (someID); } static _Id const& recast (const LumieraUid luid) { REQUIRE (luid); return reinterpret_cast<_Id const&> (*luid); } static PlacementMO& access (_Id const& placementID) { Placement & pla (fetch_PlacementIndex (placementID)); // may throw REQUIRE (pla.isValid()); ASSERT (pla.isCompatible()); return static_cast (pla); } }; } // namespace mobject #endif