refactoring: move new library helpers into final destination

This commit is contained in:
Fischlurch 2016-01-28 15:19:09 +01:00
parent e748473b77
commit ae7912dc99
6 changed files with 361 additions and 273 deletions

View file

@ -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;

View file

@ -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];
}

View file

@ -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);
}
};

View file

@ -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);
}

View 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*/

View file

@ -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;