refactoring(#988): switch function-closure to std::tuple

not sure yet if any of this works, because the
technicalities of dealing with variadic types are
quite different to our LISP-style typelist processing.

The good news is that with variadic templates it is
indeed possible, to supply dynamically picked arguments
to another function taking arbitrary arguments.

This all relies on the feature to unpack argument packs,
and, more specifically, about the possiblity to "wrap"
this unpacking around interspersed function call syntax

template<size_t...i>
Xyz
do_something(MyTuple myTuple)
  {
    return Xyz (std::get<i> (myTuple) ... );
  }

Here the '...' will be applied to the i... and then
the whole std::get-construct will be wrapped around
each element. Mind bogging, but very powerful
This commit is contained in:
Fischlurch 2016-01-17 05:36:35 +01:00
parent 3523b897c2
commit fd2d56ca45
5 changed files with 1101 additions and 165 deletions

View file

@ -34,6 +34,37 @@ the same arguments through separate but otherwise identical invocations of +bind
Besides, should boost or the standard library implementors eventually change the
implementation, this workaround will break.
post C++11
^^^^^^^^^^
As of this writing (1/2016), this still remains an unsolved issue. +
The new standard _does indeed require a comparison_, but only a comparison against
`nullptr`, which is equivalent to the `bool` conversion. It seems we need more.
WARNING: what we have here is a really evil hack, and even the notion of ``equality''
is not correct. We admit that real equality can not be achieved with something
like `std::function`, but in spite of that, we do have our own needs.
[red]#TODO# investigate what we _really_ need and come up with a better concept...
Binding Placeholders
~~~~~~~~~~~~~~~~~~~~
The standard allows function objects to be partially closed; this is achieved by
marking the remaining, unbound arguments in the call to `std::bind` with some special
_marker elements_, the ``argument placeholders''. These are predefined within the
standard library as `std::placeholders::_1` and consecutive, while the _type_ of
these objects remains _unspecified_ as by the standard. But unfortunately we need
some augmentation on top of `std::bind` to help with _generic partial application_
of functions, i.e. we need to close systematically a sequence of arguments, both
starting from the front or from the back of the argument list. We need this,
because it is a standard functional programming technique. Consequently
our helper (`function-closure.hpp`) will build placeholders on its own,
and it needs to feed _placeholder types_ to the generated binders.
Thus we rely on the fact, that the gnu standard library implementation
has a templated type `std::_Placeholder<i>`
Size of standard library facilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -49,6 +80,6 @@ simplified as much as possible. As long as the GNU libstdc++ or Boost don't
change their implementation layout, this give us precise and fast size bounds.
When relying on that hack, we should make sure always to place some kind of
STATIC_ASSERT into the corresponding implementation files to ensure the real
`static_assert` into the corresponding implementation files to ensure the real
facilites actually _do fit_ into the guessed storage dimensions.

View file

@ -23,8 +23,8 @@
/** @file function-closure.hpp
** Partial function application and building a complete function closure.
** This is a small addendum to (and thin wrapper for) tr1/functional, supporting
** the case when a function should be closed over (partially or all) arguments,
** This is a addendum to (and thin wrapper for) `<functional>`, supporting
** the case when a function should be _closed_ over (partially or all) arguments,
** where especially the parameter values to close on are provided as a tuple.
** Additionally, we allow for composing (chaining) of two functions.
**
@ -39,7 +39,6 @@
**
** @see control::CommandDef usage example
** @see function.hpp
** @see tuple.hpp
**
*/
@ -48,9 +47,10 @@
#define LIB_META_FUNCTION_CLOSURE_H
#include "lib/meta/function.hpp"
#include "lib/meta/tuple.hpp"
#include "lib/meta/tuple-helper.hpp"
#include <functional>
#include <tuple>
@ -59,13 +59,14 @@ namespace meta{
namespace func{
using std::function;
using std::tuple;
namespace { // helpers for binding and applying a function to an argument tuple
using tuple::element;
using std::get;
/**
* this Helper with repetitive specialisations for up to nine arguments
@ -102,14 +103,14 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f (element<0>(arg));
return f (get<0>(arg));
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg));
return std::bind (f, get<0>(arg));
}
};
@ -121,8 +122,8 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f ( element<0>(arg)
, element<1>(arg)
return f ( get<0>(arg)
, get<1>(arg)
);
}
@ -130,9 +131,9 @@ namespace func{
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg)
, element<1>(arg)
);
return std::bind (f, get<0>(arg)
, get<1>(arg)
);
}
};
@ -144,9 +145,9 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f ( element<0>(arg)
, element<1>(arg)
, element<2>(arg)
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
);
}
@ -154,10 +155,10 @@ namespace func{
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg)
, element<1>(arg)
, element<2>(arg)
);
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
);
}
};
@ -169,10 +170,10 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f ( element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
);
}
@ -180,11 +181,11 @@ namespace func{
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
);
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
);
}
};
@ -196,11 +197,11 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f ( element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
);
}
@ -208,12 +209,12 @@ namespace func{
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
);
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
);
}
};
@ -225,12 +226,12 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f ( element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
, element<5>(arg)
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
);
}
@ -238,13 +239,13 @@ namespace func{
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
, element<5>(arg)
);
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
);
}
};
@ -256,13 +257,13 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f ( element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
, element<5>(arg)
, element<6>(arg)
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
);
}
@ -270,14 +271,14 @@ namespace func{
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
, element<5>(arg)
, element<6>(arg)
);
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
);
}
};
@ -289,14 +290,14 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f ( element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
, element<5>(arg)
, element<6>(arg)
, element<7>(arg)
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
);
}
@ -304,15 +305,15 @@ namespace func{
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
, element<5>(arg)
, element<6>(arg)
, element<7>(arg)
);
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
);
}
};
@ -324,15 +325,15 @@ namespace func{
static RET
invoke (FUN& f, TUP & arg)
{
return f ( element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
, element<5>(arg)
, element<6>(arg)
, element<7>(arg)
, element<8>(arg)
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
, get<8>(arg)
);
}
@ -340,16 +341,16 @@ namespace func{
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, element<0>(arg)
, element<1>(arg)
, element<2>(arg)
, element<3>(arg)
, element<4>(arg)
, element<5>(arg)
, element<6>(arg)
, element<7>(arg)
, element<8>(arg)
);
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
, get<8>(arg)
);
}
};
@ -360,36 +361,130 @@ namespace func{
/* ===== Helpers for partial function application ===== */
using std::_Placeholder; // what is the "official" way to import them?
/** @note relying on the implementation type
* since we need to _build_ placeholders */
using std::_Placeholder;
/**
* Build a list of tr1 function argument placeholder types.
* Build a list of standard function argument placeholder types.
* For each of the elements of the provided reference list,
* a Placeholder is added, numbers counting up starting with 1 (!)
*/
template<typename TYPES, uint i=1>
template<typename TYPES, size_t i=1>
struct PlaceholderTuple
: PlaceholderTuple<typename TYPES::List>
{ };
template<typename X, typename TAIL, uint i>
template<typename X, typename TAIL, size_t i>
struct PlaceholderTuple<Node<X,TAIL>, i>
{
using TailPlaceholders = typename PlaceholderTuple<TAIL,i+1>::List;
using List = Node<_Placeholder<i>, TailPlaceholders>;
using Type = Tuple<typename Types<List>::Seq>;
};
template<uint i>
template<size_t i>
struct PlaceholderTuple<NullType, i>
{
using List = NullType;
using Type = Tuple<Types<>>;
};
using std::tuple_element;
using std::tuple_size;
using std::get;
/**
* Builder for a tuple instance, where only some ctor parameters
* are supplied, while the remaining arguments will be default constructed.
* The use case for this is creating of a binder, where some arguments
* shall be passed through (and thus be stored in the resulting closure),
* while other arguments are just marked as "Placeholder" with `std::_Placeholder<i>`.
* These placeholder marker terms just need to be default constructed, and will
* then be stored into the desired positions. Later on, when actually invoking
* such a partially closed function, only the arguments marked with placeholders
* need to be supplied, while the other arguments will use the values "baked"
* into the closure.
* @tparam TYPES sequence of the types in the full target tuple. Some oft these
* will be default constructed, some will be initialised from the ARGS
* @tparam ARGS sequence of the argument types, which will actually be initialised
* @tparam start position within TYPES, at which the sequence of init-arguments starts;
* all other positions will just be default initialised
*/
template<typename TYPES, typename ARGS, size_t start>
struct PartInitTuple;
template<typename...TYPES, typename...ARGS, size_t start>
struct PartInitTuple<Types<TYPES...>, Types<ARGS...>, start>
{
using TargetTuple = Tuple<Types<TYPES...>>;
using ArgTuple = Tuple<Types<ARGS...>>;
using TupleIdx = typename BuildIndexSeq<tuple_size<TargetTuple>::value>::Ascending;
static constexpr size_t len = tuple_size<ArgTuple>();
template<size_t idx>
using DestType = typename tuple_element<idx, TargetTuple>::type;
template<size_t idx>
static constexpr bool
shall_pick ()
{
return (start <= idx) and (idx < start+len);
}
template<size_t idx, bool doPick>
struct IndexMapper;
template<size_t idx>
struct IndexMapper<idx, true>
{
DestType<idx>
operator() (ArgTuple initArgs)
{
return get<idx-start> (initArgs);
}
};
template<size_t idx>
struct IndexMapper<idx, false>
{
DestType<idx>
operator() (ArgTuple)
{
return DestType<idx>();
}
};
template<class>
struct TupleBuilder;
template<size_t...i>
struct TupleBuilder<IndexSeq<i...>>
{
static TargetTuple
unpackIntoCtor (ArgTuple initArgs)
{
return TargetTuple ((IndexMapper<i, shall_pick<i>()>()(initArgs))...);
}
};
static TargetTuple
create (ArgTuple initArgs)
{
return TupleBuilder<TupleIdx>::unpackIntoCtor (initArgs);
}
};
} // (END) impl-namespace
@ -398,7 +493,7 @@ namespace func{
/**
* Closure-creating template.
* @note taking functor objects \em and parameters per reference
* @note we take functor objects \em and parameters by reference
*/
template<typename SIG>
class TupleApplicator
@ -433,7 +528,7 @@ namespace func{
* This is a small usage example or spin-off,
* having almost the same effect than invoking tr1::bind.
* The notable difference is that the function arguments for
* creating the closure are passed in as one compound tuple.
* creating the closure are passed in as one tuple compound.
*/
template<typename SIG>
class FunctionClosure
@ -467,6 +562,13 @@ namespace func{
* either from the front (left) or aligned to the end
* of the function argument list. Result is a "reduced" function,
* expecting only the remaining "un-closed" arguments at invocation.
* @tparam SIG signature of the function to be closed, either as
* function reference type or `std::function` object
* @tparam VAL type sequence describing the tuple of values
* used for closing arguments
* @note the construction of this helper template does not verify or
* match types to to the signature. In case of mismatch, you'll get
* a compilation failure from `std::bind` (which can be confusing)
*/
template<typename SIG, typename VAL>
class PApply
@ -475,6 +577,7 @@ namespace func{
typedef typename _Fun<SIG>::Ret Ret;
typedef typename Args::List ArgsList;
typedef typename VAL::List ValList;
typedef typename Types<ValList>::Seq ValTypes;
enum { ARG_CNT = count<ArgsList>::value
, VAL_CNT = count<ValList> ::value
@ -491,13 +594,13 @@ namespace func{
// build a list, where each of the *remaining* arguments is replaced by a placeholder marker
typedef typename func::PlaceholderTuple<LeftReduced>::List LeftPlaceholders;
typedef typename func::PlaceholderTuple<RightReduced>::List RightPlaceholders;
typedef typename func::PlaceholderTuple<LeftReduced>::List TrailingPlaceholders;
typedef typename func::PlaceholderTuple<RightReduced>::List LeadingPlaceholders;
// ... and splice these placeholders on top of the original argument type list,
// thus retaining the types to be closed, but setting a placeholder for each remaining argument
typedef typename Splice<ArgsList, LeftPlaceholders, VAL_CNT>::List LeftReplaced;
typedef typename Splice<ArgsList, RightPlaceholders, 0 >::List RightReplaced;
typedef typename Splice<ArgsList, TrailingPlaceholders, VAL_CNT>::List LeftReplaced;
typedef typename Splice<ArgsList, LeadingPlaceholders, 0 >::List RightReplaced;
typedef typename Types<LeftReplaced>::Seq LeftReplacedTypes;
typedef typename Types<RightReplaced>::Seq RightReplacedTypes;
@ -505,17 +608,17 @@ namespace func{
// create a "builder" helper, which accepts exactly the value tuple elements
// and puts them at the right location, while default-constructing the remaining
// (=placeholder)-arguments. Using this builder helper, we can finally set up
// the argument tuples (Left/RightReplacedArgs) used for the tr1::bind call
typedef tuple::BuildTuple<LeftReplaced, ValList> BuildL;
typedef tuple::BuildTuple<RightReplaced, ValList, ROFFSET> BuildR;
// the argument tuples (Left/RightReplacedArgs) used for the std::bind call
using BuildL = PartInitTuple<LeftReplacedTypes, ValTypes, 0 >;
using BuildR = PartInitTuple<RightReplacedTypes, ValTypes, ROFFSET>;
/** Contains the argument values, starting from left.
* Any remaining positions are occupied by binding placeholders */
using LeftReplacedArgs = Tuple<LeftReplacedTypes>;
/** Tuple to hold all argument values, starting from left.
* Any remaining positions behind the substitute values are occupied by binding placeholders */
using LeftReplacedArgs = typename BuildL::TargetTuple;
/** Contains the argument values, aligned to the end of the function argument list.
* Any remaining positions before are occupied by binding placeholders */
using RightReplacedArgs = Tuple<RightReplacedTypes>;
/** Tuple to hold all argument values, aligned to the end of the function argument list.
* Any remaining positions before the substitute values are occupied by binding placeholders */
using RightReplacedArgs = typename BuildR::TargetTuple;
public:
@ -523,8 +626,8 @@ namespace func{
typedef function<typename FunctionTypedef<Ret,ArgsR>::Sig> RightReducedFunc;
/** do a partial function application, closing the first arguments
* f(a,b,c)->res + (a,b) yields f(c)->res
/** do a partial function application, closing the first arguments</br>
* `f(a,b,c)->res + (a,b)` yields `f(c)->res`
*
* @param f function, function pointer or functor
* @param arg value tuple, used to close function arguments starting from left
@ -532,14 +635,14 @@ namespace func{
* closed arguments; on invocation, only the remaining arguments need to be supplied.
*/
static LeftReducedFunc
bindFront (SIG& f, Tuple<VAL> const& arg)
bindFront (SIG& f, Tuple<ValTypes> const& arg)
{
LeftReplacedArgs params (BuildL::create(arg));
return func::Apply<ARG_CNT>::template bind<LeftReducedFunc> (f, params);
}
/** do a partial function application, closing the last arguments
* f(a,b,c)->res + (b,c) yields f(a)->res
/** do a partial function application, closing the last arguments</br>
* `f(a,b,c)->res + (b,c)` yields `f(a)->res`
*
* @param f function, function pointer or functor
* @param arg value tuple, used to close function arguments starting from right
@ -547,7 +650,7 @@ namespace func{
* closed arguments; on invocation, only the remaining arguments need to be supplied.
*/
static RightReducedFunc
bindBack (SIG& f, Tuple<VAL> const& arg)
bindBack (SIG& f, Tuple<ValTypes> const& arg)
{
RightReplacedArgs params (BuildR::create(arg));
return func::Apply<ARG_CNT>::template bind<RightReducedFunc> (f, params);
@ -677,7 +780,7 @@ namespace func{
* Bind a specific argument to an arbitrary value.
* Especially, this "value" might be another binder.
*/
template<typename SIG, typename X, uint n>
template<typename SIG, typename X, uint pos>
class BindToArgument
{
typedef typename _Fun<SIG>::Args Args;
@ -687,10 +790,10 @@ namespace func{
enum { ARG_CNT = count<ArgsList>::value };
typedef typename Splice<ArgsList, ValList, n>::Front RemainingFront;
typedef typename Splice<ArgsList, ValList, n>::Back RemainingBack;
typedef typename Splice<ArgsList, ValList, pos>::Front RemainingFront;
typedef typename Splice<ArgsList, ValList, pos>::Back RemainingBack;
typedef typename func::PlaceholderTuple<RemainingFront>::List PlaceholdersBefore;
typedef typename func::PlaceholderTuple<RemainingBack,n+1>::List PlaceholdersBehind;
typedef typename func::PlaceholderTuple<RemainingBack,pos+1>::List PlaceholdersBehind;
typedef typename Append< typename Append< PlaceholdersBefore
, ValList >::List
, PlaceholdersBehind >::List PreparedArgs;
@ -701,15 +804,16 @@ namespace func{
using ReducedSig = typename FunctionTypedef<Ret,RemainingArgs>::Sig;
using BuildPreparedArgs = tuple::BuildTuple<PreparedArgs, ValList, n>;
typedef Tuple<PreparedArgTypes> PreparedArgTuple;
using BuildPreparedArgs = func::PartInitTuple<PreparedArgTypes, Types<X>, pos>;
using PreparedArgTuple = typename BuildPreparedArgs::TargetTuple;
public:
typedef function<ReducedSig> ReducedFunc;
static ReducedFunc
reduced (SIG& f, Tuple<ValList> const& val)
reduced (SIG& f, Tuple<Types<X>> const& val)
{
PreparedArgTuple params (BuildPreparedArgs::create(val));
return func::Apply<ARG_CNT>::template bind<ReducedFunc> (f, params);
@ -777,25 +881,25 @@ namespace func{
* argument tuple and can be used to apply them
* to various functions repeatedly.
*/
template<typename ARG>
template<typename...ARG>
inline
typename _Sig<void, ARG>::Applicator
tupleApplicator (Tuple<ARG>& args)
typename _Sig<void, Types<ARG...>>::Applicator
tupleApplicator (std::tuple<ARG...>& args)
{
typedef typename _Sig<void,ARG>::Type Signature;
typedef typename _Sig<void,Types<ARG...>>::Type Signature;
return TupleApplicator<Signature> (args);
}
/** apply the given function to the argument tuple */
template<typename SIG, typename ARG>
template<typename SIG, typename...ARG>
inline
typename _Fun<SIG>::Ret
apply (SIG& f, Tuple<ARG>& args)
apply (SIG& f, std::tuple<ARG...>& args)
{
typedef typename _Fun<SIG>::Ret Ret; //
typedef typename _Sig<Ret,ARG>::Type Signature; // Note: deliberately re-building the Signature Type
return TupleApplicator<Signature> (args) (f); // in order to get better error messages here
typedef typename _Fun<SIG>::Ret Ret; //
typedef typename _Sig<Ret,Types<ARG...>>::Type Signature; // Note: deliberately re-building the Signature Type
return TupleApplicator<Signature> (args) (f); // in order to get better error messages here
}
/** close the given function over all arguments,
@ -803,12 +907,12 @@ namespace func{
* @return a closure object, which can be
* invoked later to yield the
* function result. */
template<typename SIG, typename ARG>
template<typename SIG, typename...ARG>
inline
typename _Clo<SIG,ARG>::Type
closure (SIG& f, Tuple<ARG>& args)
typename _Clo<SIG,Types<ARG...>>::Type
closure (SIG& f, std::tuple<ARG...>& args)
{
typedef typename _Clo<SIG,ARG>::Type Closure;
typedef typename _Clo<SIG,Types<ARG...>>::Type Closure;
return Closure (f,args);
}

View file

@ -0,0 +1,797 @@
/*
TUPLE-HELPER.hpp - metaprogramming utilities for type and data tuples
Copyright (C) Lumiera.org
2016, 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 tuple.hpp
** Metaprogramming with tuples-of-types and the `std::tuple` record.
** The metaprogramming part of this header complements typelist.hpp and allows
** some additional manipulations on type sequences, especially to integrate
** with the Tuples provided by the standard library.
**
** @see control::CommandDef usage example
** @see tuple-test.cpp
** @see typelist.hpp
** @see function.hpp
** @see generator.hpp
**
*/
#ifndef LIB_META_TUPLE_HELPER_H
#define LIB_META_TUPLE_HELPER_H
#include "lib/meta/typelist.hpp"
#include "lib/meta/typelist-util.hpp"
#include "lib/meta/typeseq-util.hpp"
#include "lib/meta/util.hpp"
#include <tuple>
namespace lib {
namespace meta {
namespace {
template<typename SEQ>
struct BuildTupleType
: std::false_type
{ };
template<typename...TYPES>
struct BuildTupleType<Types<TYPES...>>
{
using Type = std::tuple<TYPES...>;
};
}
template<typename...TYPES>
using Tuple = typename BuildTupleType<TYPES>::Type;
/** Hold a sequence of index numbers as template parameters */
template<size_t...idx>
struct IndexSeq
{
template<size_t i>
using AppendElm = IndexSeq<idx..., i>;
};
/** build an `IndexSeq<0, 1, 2, ..., n-1>` */
template<size_t n>
struct BuildIndexSeq
{
using Ascending = typename BuildIndexSeq<n-1>::Ascending::AppendElm<n>;
template<size_t i>
using FilledWith = typename BuildIndexSeq<n-1>::FilledWith<i>::AppendElm<i>;
};
template<>
struct BuildIndexSeq<0>
{
using Ascending = IndexSeq<>;
template<size_t>
using FilledWith = IndexSeq<>;;
};
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////OBSOLETE :: TICKET #988
/**
* simple generic Tuple datatype.
* Usable both for metaprogramming and as a generic record.
* The types within this tuple can either be specified
* as Type sequence or as typelist. Default and canonical
* is the type-sequence based tuple form \c Tuple<Types<T1,T2,...>>
* The secondary from of specifying a tuple is based on a typelist
* (and this form is used actually to implement the storage, while
* the plain-flat (type sequence based) form acts as interface.
*
* Irrespective of the flavour actually used, you can always
* - get the canonical TupleType (sequence based)
* - get the types of head and tail, and a list version of the types
* - access the head element and the tail tuple
* - access the Nth element and a shifted-b-N sub (tail) tuple
*/
template<class TYPES>
struct Tuple;
template<>
struct Tuple<NullType>
{
typedef NullType HeadType;
typedef Types<> TailType;
typedef Types<> Type;
typedef NullType ArgList;
typedef Tuple<Type> TupleType;
typedef Tuple<NullType> ThisType;
typedef Tuple<NullType> Tail;
enum { SIZE = 0 };
NullType& getHead() { return bottom(); }
Tail& getTail() { return *this; }
Tuple (HeadType const&, Tail const&) { }
Tuple () { }
template<uint> struct ShiftedTuple { typedef Tail Type;};
template<uint> Tail& getShifted () { return *this; }
template<uint> NullType& getAt () { return bottom(); }
const NullType getHead_const() const { return bottom(); }
const Tail& getTail_const() const { return *this; }
TupleType&
tupleCast ()
{
return reinterpret_cast<TupleType&> (*this);
}
NullType&
bottom() const
{
return (NullType&) (*this);
}
};
template<class TY, class TYPES>
struct Tuple<Node<TY,TYPES> >
: Tuple<TYPES>
{
typedef TY HeadType;
typedef typename Tuple<TYPES>::Type TailType;
typedef typename Prepend<TY,TailType>::Seq Type;
typedef Node<TY,TYPES> ArgList;
typedef Tuple<Type> TupleType;
typedef Tuple<ArgList> ThisType;
typedef Tuple<TYPES> Tail;
enum { SIZE = count<ArgList>::value };
Tuple ( TY a1 =TY()
, Tail tail =Tail()
)
: Tail (tail.getHead(), tail.getTail()),
val_(a1)
{ }
TY & getHead() { return val_; }
Tail& getTail() { return static_cast<Tail&> (*this); }
TY const& getHead_const() const { return val_; }
Tail const& getTail_const() const { return static_cast<const Tail&> (*this); }
template<uint i>
class ShiftedTuple
{
typedef typename Tuple::Type OurType_;
typedef typename Shifted<OurType_,i>::Type ShiftedTypes_;
public:
typedef Tuple<typename ShiftedTypes_::List> Type;
};
template<uint i>
typename ShiftedTuple<i>::Type&
getShifted ()
{
typedef typename ShiftedTuple<i>::Type Tail_I;
return static_cast<Tail_I&> (*this);
}
TupleType&
tupleCast () ///< note makes this List-style Tuple appear as plain-flat Tuple
{
return reinterpret_cast<TupleType&> (*this);
}
template<uint i>
typename Shifted<Type,i>::Head&
getAt ()
{
return getShifted<i>().getHead();
}
private:
TY val_;
};
template< typename T1
, typename T2
, typename T3
, typename T4
, typename T5
, typename T6
, typename T7
, typename T8
, typename T9
>
struct Tuple<Types<T1,T2,T3,T4,T5,T6,T7,T8,T9> >
: Tuple<typename Types<T1,T2,T3,T4,T5,T6,T7,T8,T9>::List>
{
typedef T1 HeadType;
typedef Types<T2,T3,T4,T5,T6,T7,T8,T9,NullType> TailType;
typedef Types<T1,T2,T3,T4,T5,T6,T7,T8,T9> Type;
typedef typename Type::List ArgList;
typedef Tuple<Type> TupleType;
typedef Tuple<Type> ThisType;
typedef Tuple<TailType> Tail;
enum { SIZE = count<ArgList>::value };
/** standard ctor: create from values */
Tuple ( T1 a1 =T1()
, T2 a2 =T2()
, T3 a3 =T3()
, T4 a4 =T4()
, T5 a5 =T5()
, T6 a6 =T6()
, T7 a7 =T7()
, T8 a8 =T8()
, T9 a9 =T9()
)
: Tuple<ArgList>(a1, Tuple<TailType>(a2,a3,a4,a5,a6,a7,a8,a9))
{ }
/** shortcut: allow copy construction from a tuple
* which is rather defined by a list type */
Tuple (Tuple<ArgList> const& listTuple)
: Tuple<ArgList> (listTuple)
{ }
using Tuple<ArgList>::getHead;
Tail& getTail() ///< note makes the Tail appear as plain-flat shifted tuple
{
return Tuple<ArgList>::getTail().tupleCast();
}
};
template<>
struct Tuple<Types<> >
: Tuple<NullType>
{
enum { SIZE = 0 };
typedef Tuple<NullType> TupNilList;
typedef Tuple<Types<> > ThisType;
typedef ThisType Tail;
Tuple ( NullType =NullType()
, NullType =NullType()
, NullType =NullType()
, NullType =NullType()
, NullType =NullType()
, NullType =NullType()
, NullType =NullType()
, NullType =NullType()
, NullType =NullType()
)
{ } ///< end recursion of chained ctor calls
/** shortcut: allow copy construction from a tuple
* which is rather defined by a list type */
Tuple (TupNilList const&)
{ }
};
/** specialisation to shift plain tuple types */
template<class TYPES, uint i>
struct Shifted<Tuple<TYPES>,i>
{
typedef typename Shifted<TYPES,i>::Type Type;
typedef typename Shifted<TYPES,i>::Head Head;
typedef Tuple<Type> TupleType;
};
template<class TYPES>
struct Shifted<Tuple<TYPES>, 0>
{
typedef typename Tuple<TYPES>::Type Type;
typedef typename Tuple<TYPES>::HeadType Head;
typedef Tuple<Type> TupleType;
};
/* ====== Helpers for working with Tuples ========= */
namespace tuple { // some convenience access functions
template<uint n, class TUP>
inline
typename Shifted<TUP,n>::Head&
element (TUP& tup)
{
return tup.template getAt<n>();
}
inline
Tuple< Types<> >
makeNullTuple ()
{
return Tuple<Types<> > ();
}
template<typename T1>
inline
Tuple< Types<T1> >
make ( T1 a1
)
{
return Tuple<Types<T1> > (a1);
}
template< typename T1
, typename T2
>
inline
Tuple< Types<T1,T2> >
make ( T1 a1
, T2 a2
)
{
return Tuple<Types<T1,T2> > (a1,a2);
}
template< typename T1
, typename T2
, typename T3
>
inline
Tuple< Types<T1,T2,T3> >
make ( T1 a1
, T2 a2
, T3 a3
)
{
return Tuple<Types<T1,T2,T3> > (a1,a2,a3);
}
template< typename T1
, typename T2
, typename T3
, typename T4
>
inline
Tuple< Types<T1,T2,T3,T4> >
make ( T1 a1
, T2 a2
, T3 a3
, T4 a4
)
{
return Tuple<Types<T1,T2,T3,T4> > (a1,a2,a3,a4);
}
template< typename T1
, typename T2
, typename T3
, typename T4
, typename T5
>
inline
Tuple< Types<T1,T2,T3,T4,T5> >
make ( T1 a1
, T2 a2
, T3 a3
, T4 a4
, T5 a5
)
{
return Tuple<Types<T1,T2,T3,T4,T5> > (a1,a2,a3,a4,a5);
}
template< typename T1
, typename T2
, typename T3
, typename T4
, typename T5
, typename T6
>
inline
Tuple< Types<T1,T2,T3,T4,T5,T6> >
make ( T1 a1
, T2 a2
, T3 a3
, T4 a4
, T5 a5
, T6 a6
)
{
return Tuple<Types<T1,T2,T3,T4,T5,T6> > (a1,a2,a3,a4,a5,a6);
}
template< typename T1
, typename T2
, typename T3
, typename T4
, typename T5
, typename T6
, typename T7
>
inline
Tuple< Types<T1,T2,T3,T4,T5,T6,T7> >
make ( T1 a1
, T2 a2
, T3 a3
, T4 a4
, T5 a5
, T6 a6
, T7 a7
)
{
return Tuple<Types<T1,T2,T3,T4,T5,T6,T7> > (a1,a2,a3,a4,a5,a6,a7);
}
template< typename T1
, typename T2
, typename T3
, typename T4
, typename T5
, typename T6
, typename T7
, typename T8
>
inline
Tuple< Types<T1,T2,T3,T4,T5,T6,T7,T8> >
make ( T1 a1
, T2 a2
, T3 a3
, T4 a4
, T5 a5
, T6 a6
, T7 a7
, T8 a8
)
{
return Tuple<Types<T1,T2,T3,T4,T5,T6,T7,T8> > (a1,a2,a3,a4,a5,a6,a7,a8);
}
template< typename T1
, typename T2
, typename T3
, typename T4
, typename T5
, typename T6
, typename T7
, typename T8
, typename T9
>
inline
Tuple< Types<T1,T2,T3,T4,T5,T6,T7,T8,T9> >
make ( T1 a1
, T2 a2
, T3 a3
, T4 a4
, T5 a5
, T6 a6
, T7 a7
, T8 a8
, T9 a9
)
{
return Tuple<Types<T1,T2,T3,T4,T5,T6,T7,T8,T9> > (a1,a2,a3,a4,a5,a6,a7,a8,a9);
}
/**
* Helper to construct a new tuple, partially using provided argument values.
* Arguments are expected as a tuple, which is assumed to be a sub-tuple of
* the target type to be created. The start index of this sub-tuple may be
* provided as additional parameter, otherwise it is assumed to be zero,
* (i.e. the sub tuple starting left aligned). Any further arguments
* of the target type, which aren't covered by the argument tuple,
* are default initialised.
* @param TYPES type sequence or type list denoting the target tuple type
* @param ARGS type sequence of type list denoting the argument tuple type
* @param pos start index of the ARGS sequence within the TYPES sequence
*
* @note call the embedded #create function to invoke
* @note when types or positions disagree, argument tuple will be ignored
* @see TypeTuple_test#check_build_from_subTuple
*/
template<typename TYPES, typename ARGS, uint pos=0>
struct BuildTuple
{
typedef typename Tuple<TYPES>::TupleType ThisTuple;
typedef typename Tuple<TYPES>::ArgList TypeList;
typedef typename Tuple<ARGS>::ArgList ArgTypeList;
/**
* @param the argument values, contained in a list or flat- tuple
* of the type denoted by ARGS
* @return a plain-flat Tuple<TYPES> instance, initialised with
* the values found within arg
*/
static ThisTuple
create (Tuple<ArgTypeList> const& arg)
{
return BuildTuple<TypeList,ArgTypeList,pos>
::create(arg)
.tupleCast();
}
};
template<typename TYPES, typename ARGS, uint pos>
struct BuildTuple<Tuple<TYPES>, Tuple<ARGS>, pos> ///< tuples allowed instead of plain type sequences/lists
: BuildTuple<TYPES,ARGS,pos>
{ };
template< typename T
, typename TS
, typename A
, typename AS
, uint pos
>
struct BuildTuple<Node<T,TS>, Node<A,AS>, pos> ///< case: recursion \em before start of arg tuple
{
typedef Tuple<Node<T,TS> > ThisTuple;
typedef Tuple<Node<A,AS> > ThisArg;
static ThisTuple
create (ThisArg const& arg)
{
return ThisTuple( T()
, BuildTuple<TS, Node<A,AS>, pos-1>::create(arg)
);
}
};
template< typename A
, typename TS
, typename AS
>
struct BuildTuple<Node<A,TS>, Node<A,AS>, 0> ///< case: start of argument tuple detected
{
typedef Tuple<Node<A,TS> > ThisTuple;
typedef Tuple<Node<A,AS> > ThisArg;
static ThisTuple
create (ThisArg const& arg)
{
return ThisTuple( arg.getHead_const()
, BuildTuple<TS, AS, 0>::create (arg.getTail_const())
);
}
};
template< typename ARGS
, uint i
>
struct BuildTuple<NullType, ARGS, i> ///< case: hit end of target typelist
{
typedef Tuple<NullType> ThisTuple;
typedef Tuple<ARGS> ThisArg;
static ThisTuple
create (ThisArg const&)
{
return ThisTuple();
}
};
template< typename T
, typename TS
, uint i
>
struct BuildTuple<Node<T,TS>, NullType, i> ///< case: hit end of argument tuple
{
typedef Tuple<Node<T,TS> > ThisTuple;
typedef Tuple<NullType> ThisArg;
static ThisTuple
create (ThisArg const&)
{
return ThisTuple();
}
};
} // (END) access / tuple building helper functions (namespace tuple)
/** Trait template for detecting a type tuple */
template<typename TUP>
class is_Tuple
{
template<class X> struct Check { typedef No_t It; };
template<class TY> struct Check<Tuple<TY> > { typedef Yes_t It; };
public:
static const bool value = (sizeof(Yes_t)==sizeof(typename Check<TUP>::It));
};
/** Trait template detecting especially tuples
* built directly on top of a Typelist */
template<typename TUP>
class is_TupleListType
{
template<class X>
struct Check
{
enum{ result = sizeof(No_t)};
};
template<class N>
struct Check<Tuple<N> >
{
template<class H, class T>
Yes_t static check(Node<H,T>*);
Yes_t static check(NullType*);
No_t static check(...);
enum{ result = sizeof(check( (N*)0)) };
};
public:
static const bool value = (sizeof(Yes_t)== Check<TUP>::result);
};
/** Trait template to discern plain tuples and list-type tuples */
template<typename TUP>
struct is_TuplePlain
{
static const bool value = is_Tuple<TUP>::value
&& !is_TupleListType<TUP>::value;
};
/** Trait template detecting an empty tuple type */
template<typename TUP>
class is_NullTuple
{
template<class X>
struct Check
{
enum{ result = sizeof(No_t)};
};
template<class TY>
struct Check<Tuple<TY> >
{
Yes_t static check(Types<>*);
Yes_t static check(NullType*);
No_t static check(...);
enum{ result = sizeof(check( (TY*)0)) };
};
public:
static const bool value = (sizeof(Yes_t)== Check<TUP>::result);
};
/**
* Decorating a tuple type with auxiliary data access operations.
* This helper template builds up a subclass of the given TUP (base) type
* (which is assumed to be a Tuple or at least need to be copy constructible
* from \c Tuple<TYPES> ). The purpose is to use the Tuple as storage, but
* to add a layer of access functions, which in turn might rely on the exact
* type of the individual elements within the Tuple. To achieve this, for each
* type within the Tuple, the TUP type is decorated with an instance of the
* template passed in as template template parameter _X_. Each of these
* decorating instances is provided with a index allowing to access "his"
* specific element within the underlying tuple.
*
* The decorating template _X_ need to take its own base class as template
* parameter. Typically, operations on _X_ will be defined in a recursive fashion,
* calling down into this templated base class. To support this, an instantiation
* of _X_ with the empty type sequence is generated for detecting recursion end
* (built as innermost decorator, i.e. immediate subclass of TUP)
*/
template
< typename TYPES ///< Type sequence to use within the Accessor (usually the Tuple Types)
, template<class,class,class, uint> class _X_ ///< user provided template<Type, Base, TupleType, arg-No>
, class TUP =Tuple<TYPES> ///< the tuple type to build on
, uint i = 0 ///< tuple element index counter
>
class BuildTupleAccessor
{
typedef Tuple<TYPES> ArgTuple;
typedef typename ArgTuple::HeadType Head;
typedef typename ArgTuple::TailType Tail;
typedef BuildTupleAccessor<Tail,_X_,TUP, i+1> NextBuilder;
typedef typename NextBuilder::Accessor NextAccessor;
ArgTuple const& argData_;
public:
/** type of the product created by this template.
* Will be a subclass of TUP */
typedef _X_< Head // the type to use for this accessor
, NextAccessor // the base type to inherit from
, TUP // the tuple type we build upon
, i // current element index
> Accessor;
BuildTupleAccessor (ArgTuple const& tup)
: argData_(tup)
{ }
/** used to get the product of this builder template... */
operator Accessor() { return Accessor(argData_); }
};
template
< template<class,class,class, uint> class _X_
, class TUP
, uint i
>
class BuildTupleAccessor<Types<>, _X_, TUP, i>
{
typedef Tuple<Types<> > ArgTuple;
ArgTuple const& argData_;
public:
typedef _X_<NullType, TUP, TUP, 0> Accessor;
BuildTupleAccessor (ArgTuple const& tup)
: argData_(tup)
{ }
/** used to get the product of this builder template... */
operator Accessor() { return Accessor(argData_); }
};
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////OBSOLETE :: TICKET #988
}} // namespace lib::meta
#endif /*LIB_META_TUPLE_HELPER_H*/

View file

@ -33,7 +33,9 @@
** much more capable, but also has the downside of pulling in a lot of other headers.
** But when all we need is to define a generic typed record of N data elements and
** later re-accessing them (but no further advanced processing), the Tuple template
** might come in handy.
** might come in handy.
**
** @deprecated obsoleted by `std::tuple` -- to be removed soon /////////////////////TICKET #988
**
** @see control::CommandDef usage example
** @see tuple-test.cpp
@ -74,6 +76,8 @@ namespace meta {
* - get the types of head and tail, and a list version of the types
* - access the head element and the tail tuple
* - access the Nth element and a shifted-b-N sub (tail) tuple
*
* @deprecated will be replaced by `std::tuple`
*/
template<class TYPES>
struct Tuple;

View file

@ -1478,25 +1478,25 @@ While generally there is //no limitation// on the number and type of parameters,
Usually, parameters should be passed //by value// &amp;mdash; with the exception of target object(s), which are typically bound as MObjectRef, causing them to be resolved at commad execution time (late binding).
</pre>
</div>
<div title="CommandHandling" modifier="Ichthyostega" created="200906072048" modified="200910090044" tags="SessionLogic spec draft decision design img">
<pre>Organising any ''mutating'' operations executable by the user (via GUI) by means of the [[command pattern|http://en.wikipedia.org/wiki/Command_pattern]] can be considered //state of the art//&amp;nbsp; today. First of all, it allows to discern the specific implementation operations to be called on one or several objects within the HighLevelModel from the operation requested by the user, the latter being rather a concept. A command can be labeled clearly, executed under controlled circumstances, allowing transactional behaviour.
<div title="CommandHandling" modifier="Ichthyostega" created="200906072048" modified="201601162032" tags="SessionLogic spec draft decision design img" changecount="12">
<pre>To organise any ''mutating'' operation executable by the user (via GUI) by means of the [[command pattern|http://en.wikipedia.org/wiki/Command_pattern]] can be considered //state of the art//&amp;nbsp; today. First of all, it allows to discern the specific implementation operations to be called on one or several objects within the HighLevelModel from the operation requested by the user, the latter being rather a concept. A command can be labeled clearly, executed under controlled circumstances, allowing transactional behaviour.
!Defining commands
[&gt;img[Structure of Commands|uml/fig134021.png]] Basically, a command could contain arbitrary operations, but we'll assume that it causes a well defined mutation within the HighLevelModel, which can be ''undone''. Thus, when defining (&amp;rarr;[[syntax|CommandDefinition]]) a command, we need to specify not only a function doing the mutation, but also another function which might be called later to reverse the effect of the action. Besides, the action operates on a number of ''target'' objects and additionally may require a set of ''parameter'' values. These are to be stored explicitly within the command object, thus creating a ''closure'' &amp;mdash; the operation //must not//&amp;nbsp; rely on other hidden parameters (maybe with the exception of very generic singleton system services?).
[&gt;img[Structure of Commands|uml/fig134021.png]] Basically, a command could contain arbitrary operations, but we'll assume that it causes a well defined mutation within the HighLevelModel, which can be ''undone''. Thus, when defining (&amp;rarr;[[syntax|CommandDefinition]]) a command, we need to specify not only a function to perform the mutation, but also another function which might be called later to reverse the effect of the action. Besides, the action operates on a number of ''target'' objects and additionally may require a set of ''parameter'' values. These are to be stored explicitly within the command object, thus creating a ''closure'' -- the operation //must not//&amp;nbsp; rely on other hidden parameters (with the exception of generic singleton system services).
Theoretically, defining the &quot;undo&quot; operation might utilise two different approaches:
* specifying an //inverse operation,// known to cancel out the effect of the command
* capturing a //state memento,// which can later be played back to restore the state found prior to executing the command.
While obviously the first solution is much simpler to implement on behalf of the command framework, the second solution has distinct advantages, especially in the context of an editing application: there might be rounding or calculation errors, the inverse might be difficult to define correctly, the effect of the operation might depend on circumstances, be random, or might even trigger a resolution operation to yield the final result. Thus the decision is within Lumiera Proc-Layer to //favour state capturing// &amp;mdash; but in a modified, semi-manual and not completely exclusive way.
Theoretically, providing an &quot;undo&quot; functionality might draw on two different approaches:
* to specify an //inverse operation,// known to cancel out the effect of the command
* capturing of a //state memento,// which can later be played back in order to restore the state found prior to command execution.
While obviously the first solution looks elegant and is much simpler to implement on behalf of the command framework, the second solution has distinct advantages, especially in the context of an editing application: there might be rounding or calculation errors, the inverse might be difficult to define correctly, the effect of the operation might depend on circumstances, be random, or might even trigger a query resolution operation to yield the final result. Thus for Lumiera the decision is to //favour state capturing// -- but in a modified, semi-manual and not completely exclusive way.
!Undo state
While the usual »Memento« implementation might automatically capture the whole model (resulting in a lot of data to be stored and some uncertainty about the scope of the model to be captured), in Lumiera we rely instead on the client code to provide a ''capture function''&amp;nbsp;and a ''playback function'' alongside with the actual operation. To help with this task, we provide a set of standard handlers for common situations. This way, operations might capture very specific information, might provide an &quot;intelligent undo&quot; to restore a given semantic instead of just a fixed value &amp;mdash; and moreover the client code is free actually to employ the &quot;inverse operation&quot; model in special cases where it just makes more sense than capturing state.
While the usual »Memento« implementation might automatically capture the whole model (resulting in a lot of data to be stored and some uncertainty about the scope of the model to be captured), in Lumiera we rely instead on the client code to provide a ''capture function''&amp;nbsp;and a ''playback function'' alongside with the actual operation. To help with this task, we provide a set of standard handlers for common situations. This way, operations might capture very specific information, might provide an &quot;intelligent undo&quot; to restore a given semantic instead of just a fixed value -- and moreover the client code is free actually to employ the &quot;inverse operation&quot; model in special cases where it just makes more sense than capturing state.
!Handling of commands
A command may be [[defined|CommandDefinition]] completely from scratch, or it might just serve as a CommandPrototype with specific targets and parameters. The command could then be serialised and later be recovered and re-bound with the parameters, but usually it will be handed over to the ProcDispatcher, pending execution. When ''invoking'', the handling sequence is to [[log the command|SessionStorage]], then call the ''undo capture function'', followed from calling the actual ''operation function''. After success, the logging and [[undo registration|UndoManager]] is completed. In any case, finally the ''result signal'' (a functor previously stored within the command) is emitted. {{red{10/09 WIP: not clear if we acutally implement this concept}}}
A command may be [[defined|CommandDefinition]] completely from scratch, or it might just serve as a CommandPrototype with specific targets and parameters. The command could then be serialised and later be recovered and re-bound with the parameters, but usually it will be handed over to the ProcDispatcher, pending execution. When ''invoking'', the handling sequence is to [[log the command|SessionStorage]], then call the ''undo capture function'', followed from calling the actual ''operation function''. After success, the logging and [[undo registration|UndoManager]] is completed. In any case, finally the ''result signal'' (a functor previously stored within the command) is emitted. {{red{10/09 WIP: not clear if we indeed implement this concept}}}
By design, commands are single-serving value objects; executing an operation repeatedly requires creating a collection of command objects, one for each invocation. While nothing prevents you from invoking the command operation functor several times, each invocation will overwrite the undo state captrued by the previous invocation. Thus, each command instance should bes seen as the promise (or later the trace) of a single operation execution. In a similar vein, the undo capturing should be defined as to be self sufficient, so that invoking just the undo functor of a single command performes any necessary steps to restore the situation found before invoking the corresponding mutation functor &amp;mdash; of course only //with respect to the topic covered by this command.// So, while commands provide a lot of flexibility and allow to do a multitude of things, certainly there is an intended CommandLifecycle.
By design, commands are single-serving value objects; executing an operation repeatedly requires creating a collection of command objects, one for each invocation. While nothing prevents you from invoking the command operation functor several times, each invocation will overwrite the undo state captured by the previous invocation. Thus, each command instance should be seen as the promise (or later the trace) of a single operation execution. In a similar vein, the undo capturing should be defined as to be self sufficient, so that invoking just the undo functor of a single command performs any necessary steps to restore the situation found before invoking the corresponding mutation functor -- of course only //with respect to the topic covered by this command.// So, while commands provide a lot of flexibility and allow to do a multitude of things, certainly there is an intended CommandLifecycle.
&amp;rarr; command [[definition|CommandDefinition]] and [[-lifecycle|CommandLifecycle]]
&amp;rarr; more on possible [[command usage scenarios|CommandUsage]]
&amp;rarr; more details regarding [[command implementation|CommandImpl]]
@ -1517,9 +1517,9 @@ To support this handling scheme, some infrastructure is in place:
* performing the actual execution is delegated to a handling pattern object, accessed by name.
</pre>
</div>
<div title="CommandLifecycle" modifier="Ichthyostega" created="200907210135" modified="201112222244" tags="SessionLogic spec draft design img">
<div title="CommandLifecycle" modifier="Ichthyostega" created="200907210135" modified="201601162035" tags="SessionLogic spec draft design img" changecount="1">
<pre>[&lt;img[Structure of Commands|uml/fig135173.png]]
While generally the command framework was designed to be flexible and allow a lot of different use cases, execution paths and to serve various goals, there is an ''intended lifecycle'' &amp;mdash; commands are expected to go through several distinct states.
While generally the command framework was designed to be flexible and allow a lot of different use cases, employ different execution paths and to serve various goals, there is an ''intended lifecycle'' &amp;mdash; commands are expected to go through several distinct states.
The handling of a command starts out with a ''command ID'' provided by the client code. Command ~IDs are unique (human readable) identifiers and should be organised in a hierarchical fashion. When provided with an ID, the CommandRegistry tries to fetch an existing command definition. In case this fails, we enter the [[command definition stage|CommandDefinition]], which includes specifying functions to implement the operation, state capturing and UNDO. When all of this information is available, the entity is called a ''command definition''. Conceptually, it is comparable to a //class// or //meta object.//