refactoring: move new library helpers into final destination
This commit is contained in:
parent
e748473b77
commit
ae7912dc99
6 changed files with 361 additions and 273 deletions
209
research/try.cpp
209
research/try.cpp
|
|
@ -48,11 +48,8 @@
|
|||
typedef unsigned int uint;
|
||||
|
||||
#include "lib/symbol.hpp"
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/meta/generator.hpp"
|
||||
#include "lib/meta/typelist-manip.hpp"
|
||||
#include "lib/meta/tuple-helper.hpp"
|
||||
#include "lib/meta/tuple-record-init.hpp"
|
||||
#include "lib/format-cout.hpp"
|
||||
#include "lib/format-util.hpp"
|
||||
|
||||
|
|
@ -65,220 +62,27 @@ using lib::idi::EntryID;
|
|||
using lib::diff::Rec;
|
||||
using lib::diff::MakeRec;
|
||||
using lib::diff::GenNode;
|
||||
using lib::diff::DataValues;
|
||||
using lib::meta::Types;
|
||||
using lib::meta::Tuple;
|
||||
using lib::meta::Pick;
|
||||
using lib::meta::IndexSeq;
|
||||
using lib::meta::IndexIter;
|
||||
using lib::meta::BuildIndexSeq;
|
||||
using lib::meta::InstantiateChained;
|
||||
using lib::meta::Filter;
|
||||
using lib::meta::buildTuple;
|
||||
using lib::time::TimeVar;
|
||||
using lib::time::Time;
|
||||
|
||||
using std::string;
|
||||
using std::tuple;
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
using std::__not_;
|
||||
using std::__and_;
|
||||
using std::__or_;
|
||||
using std::is_constructible;
|
||||
using lib::meta::is_narrowingInit;
|
||||
using lib::meta::Strip;
|
||||
|
||||
|
||||
using DataCapPredicate = Variant<DataValues>::Predicate;
|
||||
|
||||
|
||||
template<typename TAR>
|
||||
struct GenNodeAccessor
|
||||
: boost::noncopyable
|
||||
{
|
||||
|
||||
template<typename TY>
|
||||
struct allow_Conversion
|
||||
: __and_<is_constructible<TAR, TY const&>
|
||||
,__not_<is_narrowingInit<typename Strip<TY>::TypePlain
|
||||
,typename Strip<TAR>::TypePlain>>
|
||||
>
|
||||
{ };
|
||||
|
||||
using SupportedSourceTypes = typename Filter<DataValues::List, allow_Conversion>::List;
|
||||
|
||||
|
||||
|
||||
struct ConverterBase
|
||||
: DataCapPredicate
|
||||
{
|
||||
char buffer[sizeof(TAR)];
|
||||
};
|
||||
|
||||
template<typename TY, class BA>
|
||||
class Converter
|
||||
: public BA
|
||||
{
|
||||
virtual bool
|
||||
handle(TY const& srcElm)
|
||||
{
|
||||
new(&(BA::buffer)) TAR{srcElm};
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
using ConversionBuffer = InstantiateChained< SupportedSourceTypes
|
||||
, Converter
|
||||
, ConverterBase
|
||||
>;
|
||||
|
||||
ConversionBuffer converter_;
|
||||
|
||||
public:
|
||||
GenNodeAccessor (GenNode const& node)
|
||||
: converter_()
|
||||
{
|
||||
if (not node.data.accept (converter_))
|
||||
throw error::Invalid ("Unable to build «" + util::typeStr<TAR>()
|
||||
+"» element from " + string(node));
|
||||
}
|
||||
|
||||
operator TAR ()
|
||||
{
|
||||
return *reinterpret_cast<TAR*> (&converter_.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template<class SRC, class TAR>
|
||||
struct ElementExtractor;
|
||||
|
||||
template<typename...TYPES>
|
||||
struct ElementExtractor<Rec, tuple<TYPES...>>
|
||||
{
|
||||
template<size_t i>
|
||||
using TargetType = typename Pick<Types<TYPES...>, i>::Type;
|
||||
|
||||
|
||||
template<size_t i>
|
||||
struct Access
|
||||
{
|
||||
Rec const& values;
|
||||
|
||||
operator TargetType<i> ()
|
||||
{
|
||||
return GenNodeAccessor<TargetType<i>>(values.child(i));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template< typename TYPES
|
||||
, template<class,class, size_t> class _ElmMapper_
|
||||
, class SEQ
|
||||
>
|
||||
struct TupleConstructor;
|
||||
|
||||
template< typename TYPES
|
||||
, template<class,class, size_t> class _ElmMapper_
|
||||
, size_t...idx
|
||||
>
|
||||
struct TupleConstructor<TYPES, _ElmMapper_, IndexSeq<idx...>>
|
||||
: Tuple<TYPES>
|
||||
{
|
||||
|
||||
public:
|
||||
template<class SRC>
|
||||
TupleConstructor (SRC values)
|
||||
: Tuple<TYPES> (_ElmMapper_<SRC, Tuple<TYPES>, idx>{values}...)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
template<class SRC, class TAR, size_t i>
|
||||
using PickArg = typename ElementExtractor<SRC, TAR>::template Access<i>;
|
||||
|
||||
|
||||
template<typename TYPES, class SRC>
|
||||
Tuple<TYPES>
|
||||
buildTuple (SRC values)
|
||||
{
|
||||
using IndexSeq = typename IndexIter<TYPES>::Seq;
|
||||
|
||||
return TupleConstructor<TYPES, PickArg, IndexSeq> (values);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////TODO reworked for function-closure.hpp
|
||||
|
||||
template<typename SRC, typename TAR, size_t start>
|
||||
struct PartiallyInitTuple
|
||||
{
|
||||
template<size_t i>
|
||||
using DestType = typename std::tuple_element<i, TAR>::type;
|
||||
|
||||
|
||||
/**
|
||||
* define those index positions in the target tuple,
|
||||
* where init arguments shall be used on construction.
|
||||
* All other arguments will just be default initialised.
|
||||
*/
|
||||
static constexpr bool
|
||||
useArg (size_t idx)
|
||||
{
|
||||
return (start <= idx)
|
||||
and (idx < start + std::tuple_size<SRC>());
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<size_t idx, bool doPick = PartiallyInitTuple::useArg(idx)>
|
||||
struct IndexMapper
|
||||
{
|
||||
SRC const& initArgs;
|
||||
|
||||
operator DestType<idx>()
|
||||
{
|
||||
return std::get<idx-start> (initArgs);
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t idx>
|
||||
struct IndexMapper<idx, false>
|
||||
{
|
||||
SRC const& initArgs;
|
||||
|
||||
operator DestType<idx>()
|
||||
{
|
||||
return DestType<idx>();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template<typename TYPES, typename ARGS, size_t start>
|
||||
struct SomeArgs
|
||||
{
|
||||
|
||||
template<class SRC, class TAR, size_t i>
|
||||
using IdxSelector = typename PartiallyInitTuple<SRC, TAR, start>::template IndexMapper<i>;
|
||||
using IdxSelector = typename lib::meta::func::PartiallyInitTuple<SRC, TAR, start>::template IndexMapper<i>;
|
||||
|
||||
static Tuple<TYPES>
|
||||
doIt (Tuple<ARGS> const& args)
|
||||
{
|
||||
using IndexSeq = typename IndexIter<TYPES>::Seq;
|
||||
|
||||
return TupleConstructor<TYPES, IdxSelector, IndexSeq> (args);
|
||||
return lib::meta::TupleConstructor<TYPES, IdxSelector> (args);
|
||||
}
|
||||
};
|
||||
////////////////////////TODO reworked for function-closure.hpp
|
||||
|
|
@ -295,11 +99,12 @@ struct SomeArgs
|
|||
void
|
||||
verifyConversions()
|
||||
{
|
||||
using lib::meta::GenNodeAccessor;
|
||||
using std::is_arithmetic;
|
||||
using std::is_floating_point;
|
||||
using lib::meta::is_nonFloat;
|
||||
using lib::hash::LuidH;
|
||||
|
||||
|
||||
|
||||
EVAL_PREDICATE(is_arithmetic<int> ::value)
|
||||
EVAL_PREDICATE(is_arithmetic<size_t> ::value)
|
||||
|
|
@ -333,7 +138,7 @@ main (int, char**)
|
|||
|
||||
Rec args = MakeRec().scope("lalü", 42);
|
||||
Rec urgs = MakeRec().scope("lalü", "lala", 12, 34, 5.6, Time(7,8,9));
|
||||
|
||||
|
||||
cout << args <<endl;
|
||||
cout << urgs <<endl;
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,8 @@ namespace diff{
|
|||
if (children_.size() <= idx)
|
||||
throw error::Invalid ("Child index " +util::toString(idx)
|
||||
+" out of bounds [0.."+util::toString(children_.size())
|
||||
+"[");
|
||||
+"["
|
||||
,error::LUMIERA_ERROR_INDEX_BOUNDS);
|
||||
return children_[idx];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -398,97 +398,72 @@ namespace func{
|
|||
|
||||
|
||||
/**
|
||||
* 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>`.
|
||||
* Builder for a tuple instance, where only some ctor parameters are supplied,
|
||||
* while the remaining arguments will be default constructed. The use case is
|
||||
* creating of a function 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
|
||||
* need to be supplied, while the other arguments will use the values hereby
|
||||
* "baked" into the closure.
|
||||
* @tparam TAR full target tuple type. Some oft the elements within this tuple will
|
||||
* be default constructed, some will be initialised from the SRC tuple
|
||||
* @tparam SRC argument tuple type, for the values _actually to be initialised_ here.
|
||||
* @tparam start position within TYPES, at which the sequence of init-arguments starts;
|
||||
* all other positions will just be default initialised
|
||||
* all other positions will just be default initialised
|
||||
* @see lib::meta::TupleConstructor
|
||||
*/
|
||||
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>
|
||||
template<typename SRC, typename TAR, size_t start>
|
||||
struct PartiallyInitTuple
|
||||
{
|
||||
using TargetTuple = Tuple<Types<TYPES...>>;
|
||||
using ArgTuple = Tuple<Types<ARGS...>>;
|
||||
template<size_t i>
|
||||
using DestType = typename std::tuple_element<i, TAR>::type;
|
||||
|
||||
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>
|
||||
/**
|
||||
* define those index positions in the target tuple,
|
||||
* where init arguments shall be used on construction.
|
||||
* All other arguments will just be default initialised.
|
||||
*/
|
||||
static constexpr bool
|
||||
shall_pick ()
|
||||
useArg (size_t idx)
|
||||
{
|
||||
return (start <= idx) and (idx < start+len);
|
||||
return (start <= idx)
|
||||
and (idx < start + std::tuple_size<SRC>());
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<size_t idx, bool doPick>
|
||||
struct IndexMapper;
|
||||
|
||||
template<size_t idx>
|
||||
struct IndexMapper<idx, true>
|
||||
template<size_t idx, bool doPick = PartiallyInitTuple::useArg(idx)>
|
||||
struct IndexMapper
|
||||
{
|
||||
DestType<idx>
|
||||
operator() (ArgTuple initArgs)
|
||||
SRC const& initArgs;
|
||||
|
||||
operator DestType<idx>()
|
||||
{
|
||||
return get<idx-start> (initArgs);
|
||||
return std::get<idx-start> (initArgs);
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t idx>
|
||||
struct IndexMapper<idx, false>
|
||||
{
|
||||
DestType<idx>
|
||||
operator() (ArgTuple)
|
||||
SRC const& initArgs;
|
||||
|
||||
operator DestType<idx>()
|
||||
{
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
/* ======= core operations: closures and partial application ========= */
|
||||
|
||||
/**
|
||||
|
|
@ -609,16 +584,23 @@ namespace func{
|
|||
// 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 std::bind call
|
||||
using BuildL = PartInitTuple<LeftReplacedTypes, ValTypes, 0 >;
|
||||
using BuildR = PartInitTuple<RightReplacedTypes, ValTypes, ROFFSET>;
|
||||
template<class SRC, class TAR, size_t i>
|
||||
using IdxSelectorL = typename PartiallyInitTuple<SRC, TAR, 0>::template IndexMapper<i>;
|
||||
|
||||
template<class SRC, class TAR, size_t i>
|
||||
using IdxSelectorR = typename PartiallyInitTuple<SRC, TAR, ROFFSET>::template IndexMapper<i>;
|
||||
|
||||
using BuildL = TupleConstructor<LeftReplacedTypes, IdxSelectorL>;
|
||||
using BuildR = TupleConstructor<RightReplacedTypes, IdxSelectorR>;
|
||||
|
||||
|
||||
/** 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;
|
||||
using LeftReplacedArgs = Tuple<LeftReplacedTypes>;
|
||||
|
||||
/** 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;
|
||||
using RightReplacedArgs = Tuple<RightReplacedTypes>;
|
||||
|
||||
|
||||
public:
|
||||
|
|
@ -637,7 +619,7 @@ namespace func{
|
|||
static LeftReducedFunc
|
||||
bindFront (SIG& f, Tuple<ValTypes> const& arg)
|
||||
{
|
||||
LeftReplacedArgs params (BuildL::create(arg));
|
||||
LeftReplacedArgs params {BuildL(arg)};
|
||||
return func::Apply<ARG_CNT>::template bind<LeftReducedFunc> (f, params);
|
||||
}
|
||||
|
||||
|
|
@ -652,7 +634,7 @@ namespace func{
|
|||
static RightReducedFunc
|
||||
bindBack (SIG& f, Tuple<ValTypes> const& arg)
|
||||
{
|
||||
RightReplacedArgs params (BuildR::create(arg));
|
||||
RightReplacedArgs params {BuildR(arg)};
|
||||
return func::Apply<ARG_CNT>::template bind<RightReducedFunc> (f, params);
|
||||
}
|
||||
};
|
||||
|
|
@ -804,9 +786,11 @@ namespace func{
|
|||
|
||||
using ReducedSig = typename FunctionTypedef<Ret,RemainingArgs>::Sig;
|
||||
|
||||
using BuildPreparedArgs = func::PartInitTuple<PreparedArgTypes, Types<X>, pos>;
|
||||
template<class SRC, class TAR, size_t i>
|
||||
using IdxSelector = typename PartiallyInitTuple<SRC, TAR, pos>::template IndexMapper<i>;
|
||||
|
||||
using BuildPreparedArgs = TupleConstructor<PreparedArgTypes, IdxSelector>;
|
||||
|
||||
using PreparedArgTuple = typename BuildPreparedArgs::TargetTuple;
|
||||
|
||||
|
||||
public:
|
||||
|
|
@ -815,7 +799,7 @@ namespace func{
|
|||
static ReducedFunc
|
||||
reduced (SIG& f, Tuple<Types<X>> const& val)
|
||||
{
|
||||
PreparedArgTuple params (BuildPreparedArgs::create(val));
|
||||
Tuple<PreparedArgTypes> params {BuildPreparedArgs(val)};
|
||||
return func::Apply<ARG_CNT>::template bind<ReducedFunc> (f, params);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
*/
|
||||
|
||||
|
||||
/** @file tuple.hpp
|
||||
/** @file tuple-helper.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
|
||||
|
|
@ -244,6 +244,87 @@ namespace meta {
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Extensible Adapter to construct a distinct tuple
|
||||
* from some arbitrary source type. This includes the
|
||||
* possibility to re-map elements or element positions.
|
||||
* @tparam TYPES sequence of types to use for the tuple
|
||||
* @tparam _ElmMapper_ a _template_ to extract each
|
||||
* constructor argument from the source value.
|
||||
* On invocation, we'll pick up the source type from the
|
||||
* actual ctor argument, and then invoke this helper template
|
||||
* iteratively for each component of the tuple, with arguments
|
||||
* - the source type, as picked up from the constructor
|
||||
* - the target tuple type, i.e. `Tuple<TYPES>`
|
||||
* - the actual index position of the tuple element
|
||||
* to be initialised through this concrete instantiation.
|
||||
* @remarks this design has several extension points. Pretty much
|
||||
* any conceivable initialisation logic can be embodied in the
|
||||
* `_ElmMapper_` template. The sole requirement is that the
|
||||
* concrete instance is _assignable_ by the source type and
|
||||
* _convertible_ to the individual member type of the target
|
||||
* tuple it is invoked for. Moreover, it is possible to build
|
||||
* a generic _element extractor_, which will be specialised
|
||||
* on base of the source type accepted. See \ref ExtractArg
|
||||
*/
|
||||
template< typename TYPES
|
||||
, template<class,class, size_t> class _ElmMapper_
|
||||
>
|
||||
struct TupleConstructor
|
||||
: Tuple<TYPES>
|
||||
{
|
||||
using TypeIdxIterator = typename IndexIter<TYPES>::Seq;
|
||||
|
||||
protected:
|
||||
template<class SRC, size_t...idx>
|
||||
TupleConstructor (SRC values, IndexSeq<idx...>*)
|
||||
: Tuple<TYPES> (_ElmMapper_<SRC, Tuple<TYPES>, idx>{values}...)
|
||||
{ }
|
||||
|
||||
|
||||
public:
|
||||
template<class SRC>
|
||||
TupleConstructor (SRC values)
|
||||
: TupleConstructor (values, (TypeIdxIterator*)nullptr)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generic converter to somehow extract values from the "source"
|
||||
* type to fill and initialise a tuple of given target type.
|
||||
* @note to be specialised. The concrete specialisation is
|
||||
* assumed to provide a _member template_ `Access<size_t>`,
|
||||
* which in turn picks and converts the value for the n-th
|
||||
* tuple element.
|
||||
*/
|
||||
template<class SRC, class TAR>
|
||||
struct ElementExtractor;
|
||||
|
||||
|
||||
template<class SRC, class TAR, size_t i>
|
||||
using ExtractArg = typename ElementExtractor<SRC, TAR>::template Access<i>;
|
||||
|
||||
|
||||
/**
|
||||
* convenience shortcut to build a tuple from some suitable source data.
|
||||
* For this to work, there needs to be a partial specialisation for
|
||||
* (\ref ElementExtractor) to deal with the concrete source type given.
|
||||
* @note we provide such a specialisation for `Record<GenNode>`, which
|
||||
* allows us to fill an (argument) tuple from a sequence of generic
|
||||
* data values, with run-time type compatibility check.
|
||||
*/
|
||||
template<typename TYPES, class SRC>
|
||||
Tuple<TYPES>
|
||||
buildTuple (SRC values)
|
||||
{
|
||||
return TupleConstructor<TYPES, ExtractArg> (values);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
216
src/lib/meta/tuple-record-init.hpp
Normal file
216
src/lib/meta/tuple-record-init.hpp
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
TUPLE-RECORD-INIT.hpp - adapter to construct tuple components from GenNode entries
|
||||
|
||||
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-record-init.hpp
|
||||
** Specialised adapter to consume a record of GenNode entries to build a tuple.
|
||||
** This is a complement to lib::meta::TupleConstructor to deal with arguments
|
||||
** passed in our "External Tree Description" form. This is relevant for structural
|
||||
** diff and the invocation of actions driven by messages via the UI-Bus.
|
||||
**
|
||||
** In those cases, a sequence of arguments will be passed in a run-time sequential
|
||||
** container, as a sequence of GenNode entries. The latter are variant records,
|
||||
** which means they can hold any of a small collection of basic types, like
|
||||
** numbers, strings, time values etc. So we have to face two challenges here.
|
||||
** - the sequence of types in a std::tuple is fixed at compile time, as is
|
||||
** the sequence of constructor arguments to build a given tuple type.
|
||||
** Thus we need a compile-time iteration over a run-time sequence container
|
||||
** - the concrete type of the given initialisation value is opaque, hidden within
|
||||
** the GenNode variant record. And especially this means that the concrete type
|
||||
** is known only at runtime. But we need to generate the construction code at
|
||||
** compile time. The remedy here is to use double dispatch, i.e. to build a
|
||||
** trampoline table with all the basically possible conversion paths for one
|
||||
** specific type within the target tuple. For example, if one component of our
|
||||
** tuple is a `long`, we'll provide a conversion path for the case when the
|
||||
** GenNode holds a `short`, an `int`, and -- depending on the platform -- when
|
||||
** it holds a `int64_t` (please recall that long and int are the same type on
|
||||
** 32bit platforms!). When encountering any other of the possible types within
|
||||
** GenNode (e.g. `string` or `TimeSpan`), a type mismatch exception is raised.
|
||||
**
|
||||
** @see control::CommandDef usage example
|
||||
** @see TupleHelper_test
|
||||
** @see typelist.hpp
|
||||
** @see function.hpp
|
||||
** @see generator.hpp
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_META_TUPLE_RECORD_INIT_H
|
||||
#define LIB_META_TUPLE_RECORD_INIT_H
|
||||
|
||||
#include "lib/meta/tuple-helper.hpp"
|
||||
#include "lib/meta/typelist-manip.hpp"
|
||||
#include "lib/meta/typeseq-util.hpp"
|
||||
#include "lib/meta/generator.hpp"
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/meta/trait.hpp"
|
||||
|
||||
|
||||
|
||||
namespace lib {
|
||||
namespace meta {
|
||||
|
||||
namespace { // implementation details...
|
||||
|
||||
using lib::Variant;
|
||||
using lib::diff::Rec;
|
||||
using lib::diff::GenNode;
|
||||
using lib::diff::DataValues;
|
||||
|
||||
namespace error = lumiera::error;
|
||||
|
||||
/** the visitor type our converter is based on */
|
||||
using DataCapPredicate = Variant<DataValues>::Predicate;
|
||||
|
||||
|
||||
/** @internal
|
||||
* specialised Accessor to retrieve a given target type
|
||||
* from a run-time typed, opaque GenNode element. Since GenNode
|
||||
* may hold some value from a (\ref lib::diff::DataValues small
|
||||
* and fixed selection of types), we prepare a converter function
|
||||
* for each sensible conversion path to the given `TAR` target type.
|
||||
* This converter will placement-construct a target value in a buffer
|
||||
* reserved within this within this Accessor object.
|
||||
* @warning template bloat: we get a converter for each possible conversion
|
||||
* for each instantiation of this template, irrespective if we actually
|
||||
* need it or not. The price is payed per distinct type used within some
|
||||
* target tuple; we get a VTable and some conversion functions for each.
|
||||
*/
|
||||
template<typename TAR>
|
||||
struct GenNodeAccessor
|
||||
: boost::noncopyable
|
||||
{
|
||||
|
||||
template<typename TY>
|
||||
struct allow_Conversion
|
||||
: __and_<is_constructible<TAR, TY const&>
|
||||
,__not_<is_narrowingInit<typename Strip<TY>::TypePlain
|
||||
,typename Strip<TAR>::TypePlain>>
|
||||
>
|
||||
{ };
|
||||
|
||||
using SupportedSourceTypes = typename Filter<DataValues::List, allow_Conversion>::List;
|
||||
|
||||
|
||||
|
||||
struct ConverterBase
|
||||
: DataCapPredicate
|
||||
{
|
||||
char buffer[sizeof(TAR)];
|
||||
};
|
||||
|
||||
template<typename TY, class BA>
|
||||
class Converter
|
||||
: public BA
|
||||
{
|
||||
virtual bool
|
||||
handle(TY const& srcElm)
|
||||
{
|
||||
new(&(BA::buffer)) TAR{srcElm};
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
using ConversionBuffer = InstantiateChained< SupportedSourceTypes // for each of those types...
|
||||
, Converter // instantiate the Converter template
|
||||
, ConverterBase // and use this as common base class.
|
||||
>; // RESULT: for each type we get a handle(TY)
|
||||
|
||||
ConversionBuffer converter_;
|
||||
|
||||
TAR&
|
||||
accessTargetElement()
|
||||
{
|
||||
return *reinterpret_cast<TAR*> (&converter_.buffer);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
GenNodeAccessor (GenNode const& node)
|
||||
: converter_()
|
||||
{
|
||||
if (not node.data.accept (converter_))
|
||||
throw error::Invalid ("Unable to build «" + util::typeStr<TAR>()
|
||||
+"» element from " + string(node)
|
||||
,error::LUMIERA_ERROR_WRONG_TYPE);
|
||||
}
|
||||
|
||||
/** @note RAII: when the ctor succeeds, we know the target element was built */
|
||||
~GenNodeAccessor()
|
||||
{
|
||||
accessTargetElement().~TAR();
|
||||
}
|
||||
|
||||
|
||||
operator TAR ()
|
||||
{
|
||||
return accessTargetElement();
|
||||
}
|
||||
};
|
||||
|
||||
}//(End)implementation details
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Concrete specialisation to build a std::tuple from a sequence
|
||||
* of GenNode elements, with run-time type compatibility check.
|
||||
* @remarks intention is to use this within (\ref TupleConstructor),
|
||||
* as will happen automatically when invoking (\ref buildTuple() )
|
||||
* with a `Record<GenNode>` as argument. The embedded template
|
||||
* `Access<i>` will be initialised with (a reference) to the
|
||||
* source record, followed by an attempt to convert it to the
|
||||
* individual member type at position i within the target tuple.
|
||||
* @throws error::Invalid when the number of types within the target
|
||||
* exceeds the number of children within the source record.
|
||||
* \ref LUMIERA_ERROR_INDEX_BOUNDS
|
||||
* @throws error::Invalid when some source GenNode element
|
||||
* can not be sensibly converted to the corresponding
|
||||
* type in the target tuple.
|
||||
* \ref LUMIERA_ERROR_WRONG_TYPE
|
||||
*/
|
||||
template<typename...TYPES>
|
||||
struct ElementExtractor<lib::diff::Rec, std::tuple<TYPES...>>
|
||||
{
|
||||
template<size_t i>
|
||||
using TargetType = typename Pick<Types<TYPES...>, i>::Type;
|
||||
|
||||
|
||||
template<size_t i>
|
||||
struct Access
|
||||
{
|
||||
Rec const& values;
|
||||
|
||||
operator TargetType<i> ()
|
||||
{
|
||||
return GenNodeAccessor<TargetType<i>> (values.child(i));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
}} // namespace lib::meta
|
||||
#endif /*LIB_META_TUPLE_RECORD_INIT_H*/
|
||||
|
|
@ -44,6 +44,7 @@ namespace diff{
|
|||
namespace test{
|
||||
|
||||
using lumiera::error::LUMIERA_ERROR_INVALID;
|
||||
using lumiera::error::LUMIERA_ERROR_INDEX_BOUNDS;
|
||||
using lumiera::error::LUMIERA_ERROR_BOTTOM_VALUE;
|
||||
|
||||
namespace {//Test fixture....
|
||||
|
|
@ -162,7 +163,7 @@ namespace test{
|
|||
CHECK (util::contains (enterprise, "Worf"));
|
||||
|
||||
VERIFY_ERROR (INVALID, enterprise.get("warp10"));
|
||||
VERIFY_ERROR (INVALID, enterprise.child(12));
|
||||
VERIFY_ERROR (INDEX_BOUNDS, enterprise.child(12));
|
||||
|
||||
cout << "enterprise = "
|
||||
<< enterprise <<endl;
|
||||
|
|
|
|||
Loading…
Reference in a new issue