LUMIERA.clone/src/steam/mobject/placement.hpp
Ichthyostega 20f3252892 Upgrade: down with typename!!
Yet another chainsaw massacre.

One of the most obnoxious annoyances with C++ metaprogramming
is the need to insert `typename` and `template` qualifiers into
most definitions, to help the compiler to cope with the syntax,
which is not context-free.

The recent standards adds several clarifications, so that most
of these qualifiers are redundant now, at least at places where
it is unambiguously clear that only a type can be given.

GCC already supports most of these relaxing rules
(Clang unfortunately lags way behind with support of newer language features...)
2025-07-06 01:19:08 +02:00

286 lines
10 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
PLACEMENT.hpp - Key Abstraction: a way to place and locate a Media Object
Copyright (C)
2008, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** 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. See the file COPYING for further details.
*/
/** @file placement.hpp
** Core abstraction: placement of a media object into session context.
** Placements are at the very core of all editing operations, because they act
** as handles to access the media objects to be manipulated.
** Moreover, Placements are the actual "content" stored within the the Session and Fixture
** data structure and thus are small handle like objects, which can be processed efficiently.
** Many editing tasks include locating some Placement within the Session or directly take
** a reference to a Placement. A Placement represents a _specific way to attach content._
** They may refer to contextual information and relate themselves to other placements.
**
** On the implementation level, placements are *refcounting smart pointers*: By acting
** on the Placement object, we can change parameters of the way the media object is placed
** (e.g. adjust an offset), while by dereferencing the Placement object, we access the media
** object itself. Usually, any MObject is created by a factory and immediately wrapped into a Placement,
** which takes ownership of the MObject.
**
** Besides being a handle, Placements define the logical position where some MObject is
** supposed to be located within the Session or Fixture. The way in which this placing happens
** is controlled and parametrised by a collection (chain) of LocatingPin objects. By adding
** to this chain, the position of the MObject is increasingly constrained. The simplest
** case of such constraining is to add a FixedLocation, thus placing the MObject at one
** absolute position (time, output).
**
** Together, this yields semantics somewhere in between value semantics and reference semantics.
** As any smart-ptr, placements are copyable, but each such copy takes on a _distinct identity._
** Moreover, when added to the Session, a placement acts as if it was an _instance_ of the object
** it points at, with the purpose to bind this instance into the Session with specific placement
** properties. Thus, such a placement-within-session _is_ a distinguishable entity, because
** the settings on the contained LocatingPin chain _do constitute_ the relation properties
** of the MObject "placed" by this placement. To support this rather ref-like semantics, any
** placement has an embedded ID (identity). Building on this ID, it is possible in turn to
** create a smart-ptr like PlacementRef to denote a specific placement found within the Session.
**
** Placements are templated on the type of the actual MObject they refer to, so, sometimes
** e.g. we rather use a Placement<Clip> to be able to use the more specific methods of the
** session::Clip interface. But _please note the following detail:_ this type labelling
** and downcasting is the _only_ difference between these subclasses, besides that,
** they can be replaced literally by one another (slicing acceptable).
**
** @see ExplicitPlacement
** @see LocatingPin interface for controlling the positioning parameters
**
*/
#ifndef MOBJECT_PLACEMENT_H
#define MOBJECT_PLACEMENT_H
#include "lib/error.hpp"
#include "lib/hash-indexed.hpp"
#include "lib/time/timevalue.hpp"
#include "steam/mobject/session/locatingpin.hpp"
#include "steam/asset/pipe.hpp" //////////////TICKET #109 : get rid of this
#include <memory>
namespace steam {
namespace mobject {
namespace session{ class MObjectFactory; }
class MObject;
class ExplicitPlacement;
using std::shared_ptr;
using std::static_pointer_cast;
using lib::HashIndexed;
/**
* A refcounting Handle to an MObject of type MO,
* used to constrain or explicitly specify the location
* where the MObject is supposed to be within the Session/Model.
* Placements are copyable (like values), but may be distinguished
* by their identity (reference semantics), which is based on an
* \link lib::HashIndexed hash-ID \endlink.
*
* Placements are defined to form a hierarchy, thereby mirroring
* the relations between their referents to some degree. This allows
* for building APIs targeted at specific kinds of MObjects, and
* at the same time allows a specific placement to stand-in when
* just a unspecific Placement<MObject> is required.
*
* @param MO the (compile time) type of the referent
* @param B immediate base class of this placement
*/
template<class MO, class B =MObject>
class Placement ;
/**
* this specialisation forms the root of all placements
* and defines all of Placement's actual functionality.
*/
template<>
class Placement<MObject,MObject>
: protected shared_ptr<MObject>
, public HashIndexed<Placement<MObject>, lib::hash::LuidH >
{
protected:
using HashInd = HashIndexed<Placement<MObject>, lib::hash::LuidH>;
using _SmartPtr = shared_ptr<MObject>;
typedef void (*Deleter)(MObject*);
using Time = lib::time::Time;
using Pipe = asset::shared_ptr<asset::Pipe>; ////TICKET #109 : get rid of this
public:
/** smart pointer: accessing the MObject,
* which is subject to placement.
* @note we don't provide operator*
*/
MObject *
operator->() const
{
ENSURE (*this);
return _SmartPtr::operator->();
}
/** run time diagnostics: is the pointee
* of this placement compatible to the given type?
*/
template<class Y>
bool
isCompatible () const
{
return 0 != dynamic_cast<Y*> (get());
}
/** extend shared ownership to the given smart-ptr
* @note never throws
*/
template<class Y>
void
extendOwnershipTo (shared_ptr<Y>& target) const
{
REQUIRE (isCompatible<Y>());
target = static_pointer_cast<Y>(*this);
}
/** free function to detect two placements sharing a pointee */
friend bool
isSharedPointee (Placement const& p1, Placement const& p2)
{
return static_cast<const void*> (p1.get())
== static_cast<const void*> (p2.get());
}
operator string() const ;
size_t use_count() const { return _SmartPtr::use_count(); }
bool isValid() const { return _SmartPtr::use_count(); }
virtual ~Placement() {};
/** interface for defining the kind of placement
* to employ, and for controlling any additional
* constraints and properties.
*/
session::LocatingPin chain;
/** combine and resolve all constraints defined
* by the various LocatingPin (\see #chain) and
* provide the resulting (explicit) placement.
*/
virtual ExplicitPlacement resolve () const;
//////////////////////////TODO (1) could resolve() return a reference? Otherwise placement-ref.hpp needs to include ExplicitPlacement
//////////////////////////TODO (2) does this really need to be virtual. Guess not. It's not abstract and not really polymorphic. But virtual here causes template bloat.
////////////TICKET #439
Placement (Placement const& ref)
: _SmartPtr (ref)
, HashInd() // creating a new ID!
, chain(ref.chain)
{ }
/** @todo 2025-4 what is the semantics of such an assignment?
* Shouldn't placement be treated with reference semantics? //////////////////////////////////TICKET #123 : (from 2009) "Find out about the correct meaning when assigning placements...."
*/
Placement& operator= (Placement const&) = default;
protected:
Placement (MObject & subject, Deleter killer)
: _SmartPtr (&subject, killer) { };
friend class session::MObjectFactory;
/////////////////////////////////////////////////////////TICKET #513
// private:
// /** copy assignment prohibited */
// Placement& operator= (Placement const&);
/////////////////////////////////////////////////////////TICKET #513
};
/**
* any specific placements are supposed to be derived
* from Placement<MObject>, or an intermediary interface,
* in case the second template parameter is used.
* @note please refrain from adding additional functionality
* to these subclasses. Especially, don't add any fields
* to the subclass, as Placements are treated like values
* at times, and thus slicing will happen, which in this
* special case is acceptable.
*/
template<class MO, class B>
class Placement
: public Placement<B>
{
protected:
using _Parent = Placement<B>;
using _Id = _Parent::template Id<MO> const&;
using Deleter = _Parent::Deleter;
using _SmartPtr = _Parent::_SmartPtr;
Placement (MO & mo, Deleter killer)
: _Parent (mo, killer)
{ };
friend class session::MObjectFactory;
public:
MO*
operator-> () const
{
ENSURE (INSTANCEOF (MO, this->get()));
return static_cast<MO*>
(_SmartPtr::operator-> ());
}
_Id
getID () const ///< @note overrides HashIndexed::getID to pass specific type information,
{
return _Parent::template recastID<MO>();
}
};
/** @todo cleanup uses of ref-to-placement. See Ticket #115 */
using PlacementMO = Placement<MObject>;
using PMO = Placement<MObject>;
/* == free functions == */
string
format_PlacementID (PlacementMO const&) ;
/** compare the properties of placement
* @return \c true if all the LocatingPin entries
* in both placements are semantically equivalent.
*/
bool
isSameDef (PlacementMO const&, PlacementMO const&);
}} // namespace steam::mobject
#endif