finish and comment the new time::Control facility
This commit is contained in:
parent
f4d0d23e48
commit
2099ecbcac
5 changed files with 747 additions and 410 deletions
203
src/lib/time/control-impl.hpp
Normal file
203
src/lib/time/control-impl.hpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
CONTROL-IMPL.hpp - time::control implementation building blocks
|
||||
|
||||
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-impl.hpp
|
||||
** Implementation building blocks for time modification and propagation.
|
||||
** 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. This header/include defines two building blocks:
|
||||
** - the actual Mutator to apply the changes to the target entity
|
||||
** - a Propagator to register listeners and forward the changes.
|
||||
**
|
||||
** \par implementation technique
|
||||
**
|
||||
** The Mutator uses functor objects to encapsulate the actual modification
|
||||
** operations. When attaching to a target time entity to be manipulated, these
|
||||
** functor objects will be configured by binding them to the appropriate
|
||||
** implementation function. And picking this actual implementation is done
|
||||
** through a time::mutation::Policy element, using the concrete time entity
|
||||
** types as template parameter. Thus, the actual implementation to be used
|
||||
** is determined by the compiler, through the template specialisations
|
||||
** contained in control-policy.hpp
|
||||
**
|
||||
** @note the header control-policy.hpp with the template specialisations
|
||||
** is included way down, after the class definitions. This is done
|
||||
** so for sake of readability
|
||||
**
|
||||
** @see TimeControl_test
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef LIB_TIME_CONTROL_IMPL_H
|
||||
#define LIB_TIME_CONTROL_IMPL_H
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace time {
|
||||
namespace mutation {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation building block: impose changes to a Time element.
|
||||
* The purpose of the Mutator is to attach a target time entity,
|
||||
* which then will be subject to any received value changes,
|
||||
* offsets and grid nudging. The actual attachment is to be
|
||||
* performed in a subclass, by using the Mutation interface.
|
||||
* When attaching to a target, the Mutator will be outfitted
|
||||
* with a set of suitable functors, incorporating the specific
|
||||
* behaviour for the concrete combination of input changes
|
||||
* ("source values") and target object type. This works by
|
||||
* binding to the appropriate implementation functionality,
|
||||
* guided by a templated policy class. After installing
|
||||
* these functors, these decisions remains opaque and
|
||||
* encapsulated within the functor objects, so the
|
||||
* mutator object doesn't need to carry this
|
||||
* type information on the interface
|
||||
*/
|
||||
template<class TI>
|
||||
class Mutator
|
||||
: public Mutation
|
||||
{
|
||||
typedef function<TI(TI const&)> ValueSetter;
|
||||
typedef function<TI(Offset const&)> Ofsetter;
|
||||
typedef function<TI(int)> Nudger;
|
||||
|
||||
protected:
|
||||
mutable ValueSetter setVal_;
|
||||
mutable Ofsetter offset_;
|
||||
mutable Nudger nudge_;
|
||||
|
||||
void
|
||||
ensure_isArmed() const
|
||||
{
|
||||
if (!setVal_)
|
||||
throw error::State("feeding time/value change "
|
||||
"while not (yet) connected to any target to change"
|
||||
,error::LUMIERA_ERROR_UNCONNECTED);
|
||||
}
|
||||
|
||||
|
||||
template<class TAR>
|
||||
void bind_to (TAR& target) const;
|
||||
|
||||
void unbind();
|
||||
|
||||
// using default construction and copy
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation building block: propagate changes to listeners.
|
||||
* The Propagator manages a set of callback signals, allowing to
|
||||
* propagate notifications for changed Time values.
|
||||
*
|
||||
* There are no specific requirements on the acceptable listeners,
|
||||
* besides exposing a function-call operator to feed the changed
|
||||
* time value to. Both Mutator and Propagator employ one primary
|
||||
* template parameter, which is the type of the time values
|
||||
* to be fed in and propagated.
|
||||
*/
|
||||
template<class TI>
|
||||
class Propagator
|
||||
{
|
||||
typedef function<void(TI const&)> ChangeSignal;
|
||||
typedef std::vector<ChangeSignal> ListenerList;
|
||||
|
||||
ListenerList listeners_;
|
||||
|
||||
public:
|
||||
/** install notification receiver */
|
||||
template<class SIG>
|
||||
void
|
||||
attach (SIG const& toNotify)
|
||||
{
|
||||
ChangeSignal newListener (ref(toNotify));
|
||||
listeners_.push_back (newListener);
|
||||
}
|
||||
|
||||
/** disconnect any observers */
|
||||
void
|
||||
disconnnect()
|
||||
{
|
||||
listeners_.clear();
|
||||
}
|
||||
|
||||
/** publish a change */
|
||||
TI
|
||||
operator() (TI const& changedVal) const
|
||||
{
|
||||
typedef typename ListenerList::const_iterator Iter;
|
||||
Iter p = listeners_.begin();
|
||||
Iter e = listeners_.end();
|
||||
|
||||
for ( ; p!=e; ++p )
|
||||
(*p) (changedVal);
|
||||
return changedVal;
|
||||
}
|
||||
|
||||
// using default construction and copy
|
||||
};
|
||||
|
||||
}}} // lib::time::mutation
|
||||
|
||||
|
||||
/* ===== Definition of actual operations ===== */
|
||||
#include "lib/time/control-policy.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
template<class TI>
|
||||
template<class TAR>
|
||||
void
|
||||
lib::time::mutation::Mutator<TI>::bind_to (TAR& target) const
|
||||
{
|
||||
using lib::time::mutation::Policy;
|
||||
|
||||
setVal_ = Policy<TI,TI, TAR>::buildChangeHandler (target);
|
||||
offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target);
|
||||
nudge_ = Policy<TI,int, TAR>::buildChangeHandler (target);
|
||||
}
|
||||
|
||||
template<class TI>
|
||||
void
|
||||
lib::time::mutation::Mutator<TI>::unbind()
|
||||
{
|
||||
setVal_ = ValueSetter();
|
||||
offset_ = Ofsetter();
|
||||
nudge_ = Nudger();
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
400
src/lib/time/control-policy.hpp
Normal file
400
src/lib/time/control-policy.hpp
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
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 \em nominal value type used on the time::Control interface
|
||||
** - for \c SRC, the actual type of values to impose as \em change
|
||||
** - for \c TAR, the target time value's type, receiving those changes.
|
||||
**
|
||||
** \par 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 \em 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 \em imposed to 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 \em 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 \em 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 \c TI, in accordance
|
||||
** to the \c time::Control<TI> frontend definition used in the concrete usage situation.
|
||||
** As this type \c 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 \c TAR in terms of the interface
|
||||
** type \c TI.
|
||||
**
|
||||
** \par 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_TIMEQUQNT_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/meta/util.hpp"
|
||||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <tr1/functional>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace time {
|
||||
|
||||
namespace mutation {
|
||||
|
||||
using boost::disable_if;
|
||||
using lumiera::typelist::is_sameType;
|
||||
using std::tr1::placeholders::_1;
|
||||
using std::tr1::function;
|
||||
using std::tr1::bind;
|
||||
using std::tr1::ref;
|
||||
|
||||
|
||||
|
||||
namespace { // metaprogramming helpers to pick a suitable implementation branch...
|
||||
|
||||
template<class T>
|
||||
inline bool
|
||||
isDuration()
|
||||
{
|
||||
return is_sameType<T,Duration>::value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline bool
|
||||
isTimeSpan()
|
||||
{
|
||||
return is_sameType<T,TimeSpan>::value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T const&
|
||||
maybeMaterialise (T const& non_grid_aligned_TimeValue)
|
||||
{
|
||||
return non_grid_aligned_TimeValue;
|
||||
}
|
||||
|
||||
#ifdef LIB_TIME_TIMEQUQNT_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_TIMEQUQNT_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
|
||||
: Mutator<TI>
|
||||
, Builder<TI,TAR>
|
||||
{
|
||||
|
||||
template<class SRC>
|
||||
static TI
|
||||
processValueChange (TAR& target, SRC const& change) ///< standard case: plain value change
|
||||
{
|
||||
imposeChange (target, maybeMaterialise(change));
|
||||
return buildChangedValue (maybeMaterialise(target));
|
||||
}
|
||||
|
||||
static TI
|
||||
useLengthAsChange (TAR& target, TimeSpan const& change)
|
||||
{
|
||||
return processValueChange(target, change.duration());
|
||||
}
|
||||
|
||||
static TI
|
||||
mutateLength (TimeSpan& target, Duration const& change)
|
||||
{
|
||||
Mutator<TimeSpan>::imposeChange (target.duration(), change);
|
||||
return Builder<TI,TimeSpan>::buildChangedValue(target);
|
||||
}
|
||||
|
||||
static TimeSpan
|
||||
mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
|
||||
{
|
||||
Mutator<TimeSpan>::imposeChange (target.duration(), change.duration());
|
||||
Mutator<TimeSpan>::imposeChange (target,change.start());
|
||||
return Builder<TimeSpan,TimeSpan>::buildChangedValue(target);
|
||||
}
|
||||
|
||||
static TI
|
||||
dontChange (TAR& target) ///< @note: not touching the target
|
||||
{
|
||||
return 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
|
||||
{
|
||||
static const bool value = is_sameType<T,Duration>::value
|
||||
|| is_sameType<T,Offset>::value
|
||||
|| is_sameType<T,int>::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct canReceiveDuration
|
||||
{
|
||||
static const bool value = is_sameType<T,Duration>::value
|
||||
|| is_sameType<T,TimeSpan>::value;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
@ -40,6 +40,46 @@
|
|||
** of time-like entities -- be it the running time display in a GUI widget, a ruler marker
|
||||
** which can be dragged, a modifiable selection or the animated playhead cursor.
|
||||
**
|
||||
** \par usage scenarios
|
||||
** The time::Control element provides mediating functionality, but doesn't assume or provide
|
||||
** anything special regarding the usage pattern or the lifecycle, beyond the ability to
|
||||
** attach listeners, attach to a (different) target and to detach from all connections.
|
||||
** Especially, no assumptions are made about which side is the server or the client
|
||||
** and who owns the time::Control element.
|
||||
**
|
||||
** Thus an interface might accept a time::Control element \em reference (e.g. the
|
||||
** lumiera::Play::Controller uses this pattern) -- meaning that the client owns the
|
||||
** Control element and might attach listeners, while the implementation (server side)
|
||||
** will attach the Control to mutate an time value entity otherwise not disclosed
|
||||
** (e.g. the playhead position of the playback process). Of course, in this case
|
||||
** the client is responsible for keeping the Control element and all listeners
|
||||
** alive, and to invoke Control#disconnect prior to destroying the element.
|
||||
**
|
||||
** Of course, the reversed usage situation would be possible as well: an interface
|
||||
** exposing a time::Control, thus allowing to attach target and listeners, while the
|
||||
** actual changes will originate somewhere within the service implementation.
|
||||
**
|
||||
** Another usage pattern would be to expose a time::Control \c const&, allowing only to
|
||||
** impose changes, but not to change the target or listener attachments. To the contrary,
|
||||
** when exposing only a time::Mutation \c const& through an interface allows only to
|
||||
** attach new target elements, but not to change listeners or feed any value changes.
|
||||
**
|
||||
** Using time::Control as an implementation building block and just exposing the
|
||||
** change (function) operators or the listener attachment through an forwarding sub
|
||||
** interface is another option.
|
||||
**
|
||||
** @note time::Control is default constructible and freely copyable.
|
||||
**
|
||||
**
|
||||
** \par changing quantised (grid aligned) time entities
|
||||
**
|
||||
** The time::Control element includes the functionality 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_TIMEQUQNT_H header guard needs to be defined, which happens automatically
|
||||
** if lib/time/mutation.hpp is included prior to lib/time/control.hpp.
|
||||
**
|
||||
** \par implementation notes
|
||||
** - the validity of a given combination of change and target is checked immediately,
|
||||
** when connecting to the target. Depending on the situation, the actual changes later
|
||||
|
|
@ -48,406 +88,42 @@
|
|||
** processed within its own call context (function invocation), parallelism is only
|
||||
** a concern with respect to the value finally visible within the target.
|
||||
** - the change notification is processed right away, after applying the change to the
|
||||
** target; in all cases, the effective change value is what will be propagated, \em not
|
||||
** the content of the target after applying the change
|
||||
** target; of course there is a race between applying the value and building the
|
||||
** response value passed on as notification. In all cases, the effective change
|
||||
** notification value is built from the state of the target after applying
|
||||
** the change, which might or might not reflect the change value passed in.
|
||||
**
|
||||
** @todo WIP-WIP-WIP
|
||||
** @todo include support for QuTimeSpan values ////////////////////TICKET #760
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef LIB_TIME_CONTROL_H
|
||||
#define LIB_TIME_CONTROL_H
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/meta/util.hpp"
|
||||
#include "lib/time/mutation.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/time/control-impl.hpp"
|
||||
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <tr1/functional>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace time {
|
||||
|
||||
namespace mutation {
|
||||
|
||||
using boost::enable_if;
|
||||
using boost::disable_if;
|
||||
using lumiera::typelist::is_sameType;
|
||||
using std::tr1::placeholders::_1;
|
||||
using std::tr1::function;
|
||||
using std::tr1::bind;
|
||||
using std::tr1::ref;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation building block: impose changes to a Time element.
|
||||
* The purpose of the Mutator is to attach a target time entity,
|
||||
* which then will be subject to any received value changes,
|
||||
* offsets and grid nudging. The actual attachment is to be
|
||||
* performed in a subclass, by using the Mutation interface.
|
||||
* When attaching to a target, the Mutator will be outfitted
|
||||
* with a set of suitable functors, incorporating the specific
|
||||
* behaviour for the concrete combination of input changes
|
||||
* ("source values") and target object type. This works by
|
||||
* binding to the appropriate implementation functionality,
|
||||
* guided by a templated policy class. After installing
|
||||
* these functors, these decisions remains opaque and
|
||||
* encapsulated within the functor objects, so the
|
||||
* mutator object doesn't need to carry this
|
||||
* type information on the interface
|
||||
*/
|
||||
template<class TI>
|
||||
class Mutator
|
||||
: public Mutation
|
||||
{
|
||||
typedef function<TI(TI const&)> ValueSetter;
|
||||
typedef function<TI(Offset const&)> Ofsetter;
|
||||
typedef function<TI(int)> Nudger;
|
||||
|
||||
protected:
|
||||
mutable ValueSetter setVal_;
|
||||
mutable Ofsetter offset_;
|
||||
mutable Nudger nudge_;
|
||||
|
||||
void
|
||||
ensure_isArmed()
|
||||
{
|
||||
if (!setVal_)
|
||||
throw error::State("feeding time/value change "
|
||||
"while not (yet) connected to any target to change"
|
||||
,error::LUMIERA_ERROR_UNCONNECTED);
|
||||
}
|
||||
|
||||
|
||||
template<class TAR>
|
||||
void bind_to (TAR& target) const;
|
||||
|
||||
void unbind();
|
||||
|
||||
// using default construction and copy
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation building block: propagate changes to listeners.
|
||||
* The Propagator manages a set of callback signals, allowing to
|
||||
* propagate notifications for changed Time values.
|
||||
*
|
||||
* There are no specific requirements on the acceptable listeners,
|
||||
* besides exposing a function-call operator to feed the changed
|
||||
* time value to. Both Mutator and Propagator employ one primary
|
||||
* template parameter, which is the type of the time values
|
||||
* to be fed in and propagated.
|
||||
*/
|
||||
template<class TI>
|
||||
class Propagator
|
||||
{
|
||||
typedef function<void(TI const&)> ChangeSignal;
|
||||
typedef std::vector<ChangeSignal> ListenerList;
|
||||
|
||||
ListenerList listeners_;
|
||||
|
||||
public:
|
||||
/** install notification receiver */
|
||||
template<class SIG>
|
||||
void
|
||||
attach (SIG const& toNotify)
|
||||
{
|
||||
ChangeSignal newListener (ref(toNotify));
|
||||
listeners_.push_back (newListener);
|
||||
}
|
||||
|
||||
/** disconnect any observers */
|
||||
void
|
||||
disconnnect()
|
||||
{
|
||||
listeners_.clear();
|
||||
}
|
||||
|
||||
/** publish a change */
|
||||
TI
|
||||
operator() (TI const& changedVal) const
|
||||
{
|
||||
typedef typename ListenerList::const_iterator Iter;
|
||||
Iter p = listeners_.begin();
|
||||
Iter e = listeners_.end();
|
||||
|
||||
for ( ; p!=e; ++p )
|
||||
(*p) (changedVal);
|
||||
return changedVal;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace { // metaprogramming helpers to pick suitable implementation branch...
|
||||
|
||||
template<class T>
|
||||
inline bool
|
||||
isDuration()
|
||||
{
|
||||
return is_sameType<T,Duration>::value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline bool
|
||||
isTimeSpan()
|
||||
{
|
||||
return is_sameType<T,TimeSpan>::value;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T const&
|
||||
maybeMaterialise (T const& non_grid_aligned_TimeValue)
|
||||
{
|
||||
return non_grid_aligned_TimeValue;
|
||||
}
|
||||
|
||||
#ifdef LIB_TIME_TIMEQUQNT_H
|
||||
inline QuTime
|
||||
maybeMaterialise (QuTime const& alignedTime)
|
||||
{
|
||||
PQuant const& grid(alignedTime);
|
||||
return QuTime(grid->materialise(alignedTime), grid);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
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_TIMEQUQNT_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
|
||||
|
||||
template<class TI, class TAR>
|
||||
struct Link
|
||||
: Mutator<TI>
|
||||
, Builder<TI,TAR>
|
||||
{
|
||||
|
||||
template<class SRC>
|
||||
static TI
|
||||
processValueChange (TAR& target, SRC const& change)
|
||||
{
|
||||
imposeChange (target, maybeMaterialise(change));
|
||||
return buildChangedValue (maybeMaterialise(target));
|
||||
}
|
||||
|
||||
static TI
|
||||
useLengthAsChange (TAR& target, TimeSpan const& change)
|
||||
{
|
||||
return processValueChange(target, change.duration());
|
||||
}
|
||||
|
||||
static TI
|
||||
mutateLength (TimeSpan& target, Duration const& change)
|
||||
{
|
||||
Mutator<TimeSpan>::imposeChange (target.duration(), change);
|
||||
return Builder<TI,TimeSpan>::buildChangedValue(target);
|
||||
}
|
||||
|
||||
static TimeSpan
|
||||
mutateTimeSpan (TimeSpan& target, TimeSpan const& change)
|
||||
{
|
||||
Mutator<TimeSpan>::imposeChange (target.duration(), change.duration());
|
||||
Mutator<TimeSpan>::imposeChange (target,change.start());
|
||||
return Builder<TimeSpan,TimeSpan>::buildChangedValue(target);
|
||||
}
|
||||
|
||||
static TI
|
||||
dontChange (TAR& target)
|
||||
{
|
||||
// note: not touching the target
|
||||
return buildChangedValue(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
|
||||
{
|
||||
static const bool value = is_sameType<T,Duration>::value
|
||||
|| is_sameType<T,Offset>::value
|
||||
|| is_sameType<T,int>::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct canReceiveDuration
|
||||
{
|
||||
static const bool value = is_sameType<T,Duration>::value
|
||||
|| is_sameType<T,TimeSpan>::value;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
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) );
|
||||
}
|
||||
};
|
||||
|
||||
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 );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<class TI>
|
||||
template<class TAR>
|
||||
void
|
||||
Mutator<TI>::bind_to (TAR& target) const
|
||||
{
|
||||
setVal_ = Policy<TI,TI, TAR>::buildChangeHandler (target);
|
||||
offset_ = Policy<TI,Offset,TAR>::buildChangeHandler (target);
|
||||
nudge_ = Policy<TI,int, TAR>::buildChangeHandler (target);
|
||||
}
|
||||
|
||||
template<class TI>
|
||||
void
|
||||
Mutator<TI>::unbind()
|
||||
{
|
||||
setVal_ = ValueSetter();
|
||||
offset_ = Ofsetter();
|
||||
nudge_ = Nudger();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frontend/Interface: controller-element to retrieve
|
||||
* and change running time values
|
||||
* and change running time values. time::Control is
|
||||
* a mediator element, which can be attached to some
|
||||
* time value entities as \em mutation, and at the
|
||||
* same time allows to register listeners. When
|
||||
* configured this way, \em changes may be fed
|
||||
* to the function operator(s). These changes
|
||||
* will be imposed to the connected target
|
||||
* and the result propagated to the listeners.
|
||||
*
|
||||
* @see time::Mutation
|
||||
* @see time::TimeSpan#accept(Mutation const&)
|
||||
* @todo WIP-WIP-WIP
|
||||
*/
|
||||
template<class TI>
|
||||
class Control
|
||||
|
|
@ -460,9 +136,9 @@ namespace time {
|
|||
virtual void change (QuTime&) const;
|
||||
|
||||
public:
|
||||
void operator() (TI const&);
|
||||
void operator() (Offset const&);
|
||||
void operator() (int);
|
||||
void operator() (TI const&) const;
|
||||
void operator() (Offset const&) const;
|
||||
void operator() (int) const;
|
||||
|
||||
|
||||
/** install a callback functor to be invoked as notification
|
||||
|
|
@ -481,27 +157,47 @@ namespace time {
|
|||
|
||||
/* === forward to implementation === */
|
||||
|
||||
/** impose a new value to the connected target.
|
||||
* If applicable, the target will afterwards reflect
|
||||
* that change, and listeners will be notified, passing
|
||||
* the target's new state.
|
||||
* @throw error::State if not connected to a target
|
||||
* @note the actual change in the target also depends
|
||||
* on the concrete target type and the type of
|
||||
* the change. By default, the time value is
|
||||
* changed; this may include grid alignment.
|
||||
*/
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::operator () (TI const& newValue)
|
||||
Control<TI>::operator () (TI const& newValue) const
|
||||
{
|
||||
this->ensure_isArmed();
|
||||
notifyListeners_(
|
||||
this->setVal_(newValue));
|
||||
}
|
||||
|
||||
/** impose an offset to the connected target.
|
||||
* If applicable, the target will be adjusted by the
|
||||
* time offset, and listeners will be notified.
|
||||
* @throw error::State if not connected to a target
|
||||
*/
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::operator () (Offset const& adjustment)
|
||||
Control<TI>::operator () (Offset const& adjustment) const
|
||||
{
|
||||
this->ensure_isArmed();
|
||||
notifyListeners_(
|
||||
this->offset_(adjustment));
|
||||
}
|
||||
|
||||
/** nudge the connected target by the given offset steps,
|
||||
* using either the target's own grid (when quantised),
|
||||
* or a 'natural' nudge grid
|
||||
* @throw error::State if not connected to a target
|
||||
*/
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::operator () (int offset_by_steps)
|
||||
Control<TI>::operator () (int offset_by_steps) const
|
||||
{
|
||||
this->ensure_isArmed();
|
||||
notifyListeners_(
|
||||
|
|
@ -530,6 +226,9 @@ namespace time {
|
|||
notifyListeners_.attach (toNotify);
|
||||
}
|
||||
|
||||
|
||||
/* ==== Implementation of the Mutation interface ==== */
|
||||
|
||||
template<class TI>
|
||||
void
|
||||
Control<TI>::change (Duration& targetDuration) const
|
||||
|
|
|
|||
|
|
@ -688,7 +688,22 @@ return: 0
|
|||
END
|
||||
|
||||
|
||||
PLANNED "Life changing time specifications with feedback" TimeControl_test <<END
|
||||
TEST "Life changing time specifications with feedback" TimeControl_test <<END
|
||||
out: Test-Case. Target=.+time.Duration. <--feed--- .+time.TimeValue.$
|
||||
out: Test-Case. Target=.+time.Duration. <--feed--- .+time.Time.$
|
||||
out: Test-Case. Target=.+time.Duration. <--feed--- .+time.Duration.$
|
||||
out: Test-Case. Target=.+time.Duration. <--feed--- .+time.TimeSpan.$
|
||||
out: Test-Case. Target=.+time.Duration. <--feed--- .+time.QuTime.$
|
||||
out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.TimeValue.$
|
||||
out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.Time.$
|
||||
out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.Duration.$
|
||||
out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.TimeSpan.$
|
||||
out: Test-Case. Target=.+time.TimeSpan. <--feed--- .+time.QuTime.$
|
||||
out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.TimeValue.$
|
||||
out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.Time.$
|
||||
out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.Duration.$
|
||||
out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.TimeSpan.$
|
||||
out: Test-Case. Target=.+time.QuTime. <--feed--- .+time.QuTime.$
|
||||
return: 0
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -26,10 +26,11 @@
|
|||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/time/timequant.hpp"
|
||||
#include "lib/time/control.hpp"
|
||||
#include "proc/asset/meta/time-grid.hpp"
|
||||
|
||||
#include "lib/meta/generator-combinations.hpp"
|
||||
#include "lib/meta/util.hpp"
|
||||
#include "proc/asset/meta/time-grid.hpp"
|
||||
#include "lib/scoped-holder.hpp"
|
||||
#include "lib/meta/util.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
|
@ -57,7 +58,10 @@ namespace test{
|
|||
using lumiera::typelist::InstantiateChainedCombinations;
|
||||
using error::LUMIERA_ERROR_UNCONNECTED;
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
namespace { // Test setup and helpers....
|
||||
|
||||
inline string
|
||||
pop (Arg arg)
|
||||
{
|
||||
|
|
@ -105,21 +109,25 @@ namespace test{
|
|||
return *received_;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}//(End)Test helpers
|
||||
|
||||
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* @test use the time::Control to push a sequence of modifications
|
||||
* to various time entities; in all cases, a suitable change
|
||||
* should be imposed to the target and then a notification signal
|
||||
/***********************************************************************
|
||||
* @test use the time::Control to push a sequence of modifications to
|
||||
* various time entities; in all cases, a suitable change should
|
||||
* be imposed to the target and then a notification signal
|
||||
* should be invoked.
|
||||
* @todo cover the cases.....
|
||||
* - change to a given value
|
||||
* - change by an offset
|
||||
* - change using a grid value
|
||||
* - apply an (grid) increment
|
||||
*
|
||||
* After covering a simple basic case, this test uses
|
||||
* template metaprogramming techniques to build a matrix of all
|
||||
* possible type combinations and then performs a standard test
|
||||
* sequence for each of these type combinations. Within this
|
||||
* test sequence, verification functions are invoked, which
|
||||
* are defined with specialisations to adapt for the various
|
||||
* types to be covered.
|
||||
*/
|
||||
class TimeControl_test : public Test
|
||||
{
|
||||
|
|
@ -176,6 +184,11 @@ namespace test{
|
|||
}
|
||||
|
||||
|
||||
/** @test cover all possible combinations of input change values
|
||||
* and target time value entities to be handled by time::Control.
|
||||
* Each of these cases executes a standard test sequence, which is
|
||||
* defined in TestCase#performTestSequence
|
||||
*/
|
||||
void verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c);
|
||||
};
|
||||
|
||||
|
|
@ -241,7 +254,7 @@ namespace test{
|
|||
return QuTime (org, "test_grid_PAL");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
template<class SRC>
|
||||
struct TestChange
|
||||
|
|
@ -395,17 +408,21 @@ namespace test{
|
|||
|
||||
|
||||
|
||||
template<class TAR, class SRC, class BASE>
|
||||
template< class TAR ///< type of the target time value entity to receive changes
|
||||
, class SRC ///< type of the time value to be imposed as change
|
||||
, class BASE
|
||||
>
|
||||
struct TestCase
|
||||
: BASE
|
||||
{
|
||||
void
|
||||
performTestCases(TimeValue const& org, TimeValue const& c)
|
||||
performTestSequence(TimeValue const& org, TimeValue const& c)
|
||||
{
|
||||
cout << "Test-Case. Target=" << showType<TAR>()
|
||||
<<" <--feed--- " << showType<SRC>()
|
||||
<<endl;
|
||||
<< "\t <--feed--- " << showType<SRC>()
|
||||
<< endl;
|
||||
|
||||
// test subject
|
||||
Control<SRC> controller;
|
||||
|
||||
TAR target = TestTarget<TAR>::build(org);
|
||||
|
|
@ -453,32 +470,35 @@ namespace test{
|
|||
|
||||
|
||||
// tail recursion: further test combinations....
|
||||
BASE::performTestCases(org,c);
|
||||
BASE::performTestSequence(org,c);
|
||||
}
|
||||
};
|
||||
|
||||
struct IterationEnd
|
||||
{
|
||||
void performTestCases(TimeValue const&, TimeValue const&) { }
|
||||
void performTestSequence(TimeValue const&, TimeValue const&) { }
|
||||
};
|
||||
|
||||
}//(End)Implementation Test-case matrix
|
||||
|
||||
|
||||
void
|
||||
TimeControl_test::verifyMatrix_of_MutationCases (TimeValue const& o, TimeValue const& c)
|
||||
TimeControl_test::verifyMatrix_of_MutationCases (TimeValue const& origVal, TimeValue const& change)
|
||||
{
|
||||
typedef Types<Duration,TimeSpan,QuTime> KindsOfTarget;
|
||||
typedef Types<Time,Duration,TimeSpan,QuTime> KindsOfSource;
|
||||
typedef Types<Duration,TimeSpan,QuTime> KindsOfTarget; // time entities to receive value changes
|
||||
typedef Types<TimeValue,Time,Duration,TimeSpan,QuTime> KindsOfSource; // time entities to be used as change values
|
||||
typedef InstantiateChainedCombinations< KindsOfTarget
|
||||
, KindsOfSource
|
||||
, TestCase
|
||||
, TestCase // template to be instantiated for each type
|
||||
, IterationEnd > TestMatrix;
|
||||
|
||||
TestMatrix().performTestCases(o,c);
|
||||
TestMatrix().performTestSequence(origVal, change);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Register this test class... */
|
||||
LAUNCHER (TimeControl_test, "unit common");
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue