This header shall provide only very fundamental metaprogramming helpers, since it is included pervasively
401 lines
13 KiB
C++
401 lines
13 KiB
C++
/*
|
|
CONTROL-POLICY.hpp - detail definition of actual time changing functionality
|
|
|
|
Copyright (C) Lumiera.org
|
|
2011, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
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 control-policy.hpp
|
|
** Definition of special cases when imposing a change onto concrete time values.
|
|
** The time::Control element allows to impose modifications to a connected
|
|
** time value entity and at the same time publish the changes to registered
|
|
** listeners. Due to the various flavours of actual time value entities, this
|
|
** is a complex undertaking, which is implemented here based on policies and
|
|
** template metaprogramming.
|
|
**
|
|
** The header control-impl.hpp defines the building blocks for time::Control
|
|
** and then includes this header here to get the concrete template specialisations
|
|
** for time::mutation::Policy. This policy class is templated by time entity types
|
|
** - for \c TI, the _nominal_ value type used on the time::Control interface
|
|
** - for \c SRC, the actual type of values to impose _as change_
|
|
** - for \c TAR, the target time value's type, receiving those changes.
|
|
**
|
|
** ## mutating a time value entity
|
|
**
|
|
** Actually imposing a change to the attached time value entity involves several
|
|
** steps. Each of these steps might be adapted specifically, in accordance to
|
|
** the concrete time value types involved.
|
|
** - TimeValue, Time
|
|
** - Offset
|
|
** - Duration
|
|
** - TimeSpan
|
|
** - QuTime (grid aligned time value)
|
|
** - QuTimeSpan (planned as of 6/2011)
|
|
**
|
|
** Moreover, the combination of types needs to be taken into account. For example,
|
|
** it doesn't make sense to apply a Duration value as change to a TimeValue, which
|
|
** has no duration (temporal extension). While a TimeSpan might receive a Duration
|
|
** change, but behaves differently when imposing a Time to manipulate the starting
|
|
** point of the time interval given by the TimeSpan.
|
|
**
|
|
** Incoming changes might be of any of the aforementioned types, and in addition,
|
|
** we might receive *nudging*, which means to increment or decrement the target
|
|
** time value in discrete steps. After maybe adapting these incoming change values,
|
|
** they may be actually _imposed_ on the target. In all cases, this is delegated
|
|
** to the time::Mutation base class, which is declared fried to TimeValue and thus
|
|
** has the exceptional ability to manipulate time values, which otherwise are defined
|
|
** to be immutable. Additionally, these protected functions in the time::Mutation
|
|
** baseclass also know how to handle _nudge values_, either by using the native
|
|
** (embedded) time grid of a quantised time value, or by falling back to a standard
|
|
** nudging grid, defined in the session context (TODO as of 6/2011). //////////////////////TICKET #810
|
|
**
|
|
** After (maybe) imposing a change to the target, the _change notification_ value
|
|
** needs to be built. This is the time value entity to be forwarded to registered
|
|
** listeners. This notification value has to be given as the type `TI`, in accordance
|
|
** to the `time::Control<TI>` frontend definition used in the concrete usage situation.
|
|
** As this type `TI` might be different to the actual target type, and again different
|
|
** to the type of the change handed in, in some cases this involves a second conversion
|
|
** step, to represent the current state of the target `TAR` in terms of the interface
|
|
** type `TI`.
|
|
**
|
|
** ## changing quantised (grid aligned) time entities
|
|
**
|
|
** The time::Control element includes the capability to handle grid aligned time values,
|
|
** both as target and as change/notification value. This ability is compiled in conditionally,
|
|
** as including mutation.hpp causes several additional includes, which isn't desirable when
|
|
** it comes just to changing plain time values. Thus, to get these additional specialisations,
|
|
** the LIB_TIME_TIMEQUANT_H header guard needs to be defined, which happens automatically
|
|
** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
|
|
**
|
|
** As a special convention, any \em quantised (grid aligned) types involved in these
|
|
** time changes will be \em materialised, whenever a type conversion happens. Generally
|
|
** speaking, a quantised time value contains an (opaque) raw time value, plus a reference
|
|
** to a time grid definition to apply. In this context \em materialising means actually
|
|
** to apply this time grid to yield a grid aligned value. Thus, when using a quantised
|
|
** value to impose as change (or to receive a change), its grid aligning nature
|
|
** becomes effective, by applying the \em current definition of the grid to
|
|
** create a fixed (materialised) time value, aligned to that current grid.
|
|
**
|
|
** @todo 6/2011 include all the special cases for QuTimeSpan ////////////////////TICKET #760
|
|
**
|
|
** @see TimeControl_test
|
|
**
|
|
*/
|
|
|
|
#ifndef LIB_TIME_CONTROL_POLICY_H
|
|
#define LIB_TIME_CONTROL_POLICY_H
|
|
|
|
#include "lib/time/mutation.hpp"
|
|
#include "lib/time/timevalue.hpp"
|
|
|
|
#include <boost/utility/enable_if.hpp>
|
|
#include <type_traits>
|
|
#include <functional>
|
|
|
|
|
|
namespace lib {
|
|
namespace time {
|
|
|
|
namespace mutation {
|
|
|
|
using boost::disable_if;
|
|
using std::__or_;
|
|
using std::is_same;
|
|
using std::placeholders::_1;
|
|
using std::function;
|
|
using std::bind;
|
|
using std::ref;
|
|
|
|
|
|
|
|
namespace { // metaprogramming helpers to pick a suitable implementation branch...
|
|
|
|
template<class T>
|
|
inline bool
|
|
isDuration()
|
|
{
|
|
return is_same<T, Duration>::value;
|
|
}
|
|
|
|
template<class T>
|
|
inline bool
|
|
isTimeSpan()
|
|
{
|
|
return is_same<T, TimeSpan>::value;
|
|
}
|
|
|
|
template<class T>
|
|
inline T const&
|
|
maybeMaterialise (T const& non_grid_aligned_TimeValue)
|
|
{
|
|
return non_grid_aligned_TimeValue;
|
|
}
|
|
|
|
#ifdef LIB_TIME_TIMEQUANT_H
|
|
inline QuTime
|
|
maybeMaterialise (QuTime const& alignedTime)
|
|
{
|
|
PQuant const& grid(alignedTime);
|
|
return QuTime(grid->materialise(alignedTime), grid);
|
|
}
|
|
#endif //--quantised-time-support
|
|
}
|
|
|
|
|
|
/**
|
|
* Implementation policy: how to build a new
|
|
* notification value of type \c TI, given a
|
|
* target time value entity of type \c TAR
|
|
*/
|
|
template<class TI, class TAR>
|
|
struct Builder
|
|
{
|
|
static TI
|
|
buildChangedValue (TAR const& target)
|
|
{
|
|
return TI(target);
|
|
}
|
|
};
|
|
template<class TAR>
|
|
struct Builder<TimeSpan, TAR>
|
|
{
|
|
static TimeSpan
|
|
buildChangedValue (TAR const& target)
|
|
{
|
|
return TimeSpan (target, Duration::NIL);
|
|
}
|
|
};
|
|
template<>
|
|
struct Builder<TimeSpan, Duration>
|
|
{
|
|
static TimeSpan
|
|
buildChangedValue (Duration const& targetDuration)
|
|
{
|
|
return TimeSpan (Time::ZERO, targetDuration);
|
|
}
|
|
};
|
|
template<>
|
|
struct Builder<TimeSpan, TimeSpan>
|
|
{
|
|
static TimeSpan
|
|
buildChangedValue (TimeSpan const& target)
|
|
{
|
|
return target;
|
|
}
|
|
};
|
|
#ifdef LIB_TIME_TIMEQUANT_H
|
|
template<class TAR>
|
|
struct Builder<QuTime, TAR>
|
|
{
|
|
static QuTime
|
|
buildChangedValue (TAR const& target)
|
|
{
|
|
return QuTime (target
|
|
,getDefaultGridFallback() //////////////////TICKET #810
|
|
);
|
|
}
|
|
};
|
|
template<>
|
|
struct Builder<QuTime, QuTime>
|
|
{
|
|
static QuTime
|
|
buildChangedValue (QuTime const& target)
|
|
{
|
|
return target;
|
|
}
|
|
};
|
|
#endif //--quantised-time-support
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Policy to tie the various detail policies together
|
|
* for providing actual value change operations.
|
|
* The standard case uses the (inherited) time::Mutation
|
|
* base implementation to impose a new value onto the
|
|
* target entity and then uses the Builder policy to
|
|
* create a notification value reflecting this change.
|
|
*/
|
|
template<class TI, class TAR>
|
|
struct Link
|
|
: Mutation
|
|
, Builder<TI,TAR>
|
|
{
|
|
|
|
template<class SRC>
|
|
static TI
|
|
processValueChange (TAR& target, SRC const& change) ///< standard case: plain value change
|
|
{
|
|
imposeChange (target, maybeMaterialise(change));
|
|
return Link::buildChangedValue (maybeMaterialise(target));
|
|
}
|
|
|
|
static TI
|
|
useLengthAsChange (TAR& target, TimeSpan const& change)
|
|
{
|
|
return processValueChange(target, change.duration());
|
|
}
|
|
|
|
static TI
|
|
mutateLength (TimeSpan& target, Duration const& change)
|
|
{
|
|
imposeChange (target.duration(), change);
|
|
return Builder<TI,TimeSpan>::buildChangedValue(target);
|
|
}
|
|
|
|
static TimeSpan
|
|
mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
|
|
{
|
|
imposeChange (target.duration(), change.duration());
|
|
imposeChange (target,change.start());
|
|
return Builder<TimeSpan,TimeSpan>::buildChangedValue(target);
|
|
}
|
|
|
|
static TI
|
|
dontChange (TAR& target) ///< @note: not touching the target
|
|
{
|
|
return Link::buildChangedValue(target);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* Policy how to impose changes onto a connected target time value entity
|
|
* This policy will be parametrised with the concrete time entity types
|
|
* involved in the usage situation of time::Control. The purpose of the
|
|
* policy is to \em bind a functor object to the concrete implementation
|
|
* of the value change applicable for this combination of types.
|
|
* This functor will then be stored within time::Control and
|
|
* invoked for each actual value change.
|
|
* @param TI the nominal (interface) type of the change, propagated to listeners
|
|
* @param SRC the actual type of the change to be imposed
|
|
* @param TAR the actual type of the target entity to receive the changes
|
|
* @note typically either SRC is identical to TI, or it is an
|
|
* time::Offset, or an int for \em nudging the target
|
|
*/
|
|
template<class TI, class SRC, class TAR>
|
|
struct Policy
|
|
{
|
|
static function<TI(SRC const&)>
|
|
buildChangeHandler (TAR& target)
|
|
{
|
|
return bind (Link<TI,TAR>::template processValueChange<SRC>, ref(target), _1 );
|
|
}
|
|
};
|
|
|
|
|
|
// special treatment of Durations as target------------------------------------
|
|
|
|
namespace {
|
|
template<class T>
|
|
struct canMutateDuration
|
|
: __or_< is_same<T,Duration>
|
|
, is_same<T,Offset>
|
|
, is_same<T,int>
|
|
>
|
|
{ };
|
|
|
|
template<class T>
|
|
struct canReceiveDuration
|
|
: __or_< is_same<T,Duration>
|
|
, is_same<T,TimeSpan>
|
|
>
|
|
{ };
|
|
}
|
|
|
|
|
|
/**
|
|
* special case: a Duration target value can't be changed by plain time values.
|
|
* This specialisation is \em not used (\c disable_if ) when the given change (SRC)
|
|
* is applicable to a Duration in a sensible way. We either define explicit
|
|
* specialisations (for TimeSpan) or fall back to the default in these cases.
|
|
*/
|
|
template<class TI, class SRC>
|
|
struct Policy<TI,SRC, typename disable_if< canMutateDuration<SRC>,
|
|
Duration>::type>
|
|
{
|
|
static function<TI(SRC const&)>
|
|
buildChangeHandler (Duration& target)
|
|
{
|
|
return bind (Link<TI,Duration>::dontChange, ref(target) );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* special case: a Duration change value can't be imposed to a plain time value.
|
|
* In these cases, we even propagate a Duration::ZERO to the listeners.
|
|
* As above, there are exceptions to this behaviour, where a Duration change
|
|
* can sensibly be applied.
|
|
*/
|
|
template<class TAR>
|
|
struct Policy<Duration, typename disable_if< canReceiveDuration<TAR>,
|
|
Duration>::type, TAR>
|
|
{
|
|
static function<Duration(Duration const&)>
|
|
buildChangeHandler (TAR&)
|
|
{
|
|
return bind ( ignore_change_and_return_Zero );
|
|
}
|
|
|
|
static Duration
|
|
ignore_change_and_return_Zero()
|
|
{
|
|
return Duration::NIL;
|
|
}
|
|
};
|
|
|
|
template<class TI>
|
|
struct Policy<TI,TimeSpan,Duration>
|
|
{
|
|
static function<TI(TimeSpan const&)>
|
|
buildChangeHandler (Duration& target)
|
|
{
|
|
return bind (Link<TI,Duration>::useLengthAsChange, ref(target), _1 );
|
|
}
|
|
};
|
|
|
|
|
|
// special treatment for TimeSpan values---------------------------------------
|
|
|
|
template<class TI>
|
|
struct Policy<TI,Duration,TimeSpan>
|
|
{
|
|
static function<TI(Duration const&)>
|
|
buildChangeHandler (TimeSpan& target)
|
|
{
|
|
return bind (Link<TI,TimeSpan>::mutateLength, ref(target), _1 );
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct Policy<TimeSpan,TimeSpan,TimeSpan>
|
|
{
|
|
static function<TimeSpan(TimeSpan const&)>
|
|
buildChangeHandler (TimeSpan& target)
|
|
{
|
|
return bind (Link<TimeSpan,TimeSpan>::mutateTimeSpan, ref(target), _1 );
|
|
}
|
|
};
|
|
|
|
|
|
|
|
}}} // namespace lib::time::mutation
|
|
#endif
|