diff --git a/research/try.cpp b/research/try.cpp index 1165a5ba7..21c4e95a7 100644 --- a/research/try.cpp +++ b/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::Predicate; - - -template -struct GenNodeAccessor - : boost::noncopyable - { - - template - struct allow_Conversion - : __and_ - ,__not_::TypePlain - ,typename Strip::TypePlain>> - > - { }; - - using SupportedSourceTypes = typename Filter::List; - - - - struct ConverterBase - : DataCapPredicate - { - char buffer[sizeof(TAR)]; - }; - - template - 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() - +"» element from " + string(node)); - } - - operator TAR () - { - return *reinterpret_cast (&converter_.buffer); - } - }; - - - - -template -struct ElementExtractor; - -template -struct ElementExtractor> - { - template - using TargetType = typename Pick, i>::Type; - - - template - struct Access - { - Rec const& values; - - operator TargetType () - { - return GenNodeAccessor>(values.child(i)); - } - - }; - - }; - - - - - - -template< typename TYPES - , template class _ElmMapper_ - , class SEQ - > -struct TupleConstructor; - -template< typename TYPES - , template class _ElmMapper_ - , size_t...idx - > -struct TupleConstructor> - : Tuple - { - - public: - template - TupleConstructor (SRC values) - : Tuple (_ElmMapper_, idx>{values}...) - { } - }; - - -template -using PickArg = typename ElementExtractor::template Access; - - -template -Tuple -buildTuple (SRC values) -{ - using IndexSeq = typename IndexIter::Seq; - - return TupleConstructor (values); -} - - ////////////////////////TODO reworked for function-closure.hpp - - template - struct PartiallyInitTuple - { - template - using DestType = typename std::tuple_element::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()); - } - - - - template - struct IndexMapper - { - SRC const& initArgs; - - operator DestType() - { - return std::get (initArgs); - } - }; - - template - struct IndexMapper - { - SRC const& initArgs; - - operator DestType() - { - return DestType(); - } - }; - }; - - template struct SomeArgs { template - using IdxSelector = typename PartiallyInitTuple::template IndexMapper; + using IdxSelector = typename lib::meta::func::PartiallyInitTuple::template IndexMapper; static Tuple doIt (Tuple const& args) { - using IndexSeq = typename IndexIter::Seq; - - return TupleConstructor (args); + return lib::meta::TupleConstructor (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 ::value) EVAL_PREDICATE(is_arithmetic ::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 <`. + * 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`. * 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 - struct PartInitTuple; - - template - struct PartInitTuple, Types, start> + template + struct PartiallyInitTuple { - using TargetTuple = Tuple>; - using ArgTuple = Tuple>; + template + using DestType = typename std::tuple_element::type; - using TupleIdx = typename BuildIndexSeq::value>::Ascending; - static constexpr size_t len = tuple_size(); - - template - using DestType = typename tuple_element::type; - - - template + /** + * 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()); } - - template - struct IndexMapper; - template - struct IndexMapper + template + struct IndexMapper { - DestType - operator() (ArgTuple initArgs) + SRC const& initArgs; + + operator DestType() { - return get (initArgs); + return std::get (initArgs); } }; template struct IndexMapper { - DestType - operator() (ArgTuple) + SRC const& initArgs; + + operator DestType() { return DestType(); } }; - - - template - struct TupleBuilder; - - template - struct TupleBuilder> - { - static TargetTuple - unpackIntoCtor (ArgTuple initArgs) - { - return TargetTuple ((IndexMapper()>()(initArgs))...); - } - }; - - - static TargetTuple - create (ArgTuple initArgs) - { - return TupleBuilder::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; - using BuildR = PartInitTuple; + template + using IdxSelectorL = typename PartiallyInitTuple::template IndexMapper; + + template + using IdxSelectorR = typename PartiallyInitTuple::template IndexMapper; + + using BuildL = TupleConstructor; + using BuildR = TupleConstructor; + /** 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; /** 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; public: @@ -637,7 +619,7 @@ namespace func{ static LeftReducedFunc bindFront (SIG& f, Tuple const& arg) { - LeftReplacedArgs params (BuildL::create(arg)); + LeftReplacedArgs params {BuildL(arg)}; return func::Apply::template bind (f, params); } @@ -652,7 +634,7 @@ namespace func{ static RightReducedFunc bindBack (SIG& f, Tuple const& arg) { - RightReplacedArgs params (BuildR::create(arg)); + RightReplacedArgs params {BuildR(arg)}; return func::Apply::template bind (f, params); } }; @@ -804,9 +786,11 @@ namespace func{ using ReducedSig = typename FunctionTypedef::Sig; - using BuildPreparedArgs = func::PartInitTuple, pos>; + template + using IdxSelector = typename PartiallyInitTuple::template IndexMapper; + + using BuildPreparedArgs = TupleConstructor; - using PreparedArgTuple = typename BuildPreparedArgs::TargetTuple; public: @@ -815,7 +799,7 @@ namespace func{ static ReducedFunc reduced (SIG& f, Tuple> const& val) { - PreparedArgTuple params (BuildPreparedArgs::create(val)); + Tuple params {BuildPreparedArgs(val)}; return func::Apply::template bind (f, params); } }; diff --git a/src/lib/meta/tuple-helper.hpp b/src/lib/meta/tuple-helper.hpp index 3fc5d47fc..3098a5fb9 100644 --- a/src/lib/meta/tuple-helper.hpp +++ b/src/lib/meta/tuple-helper.hpp @@ -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` + * - 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 _ElmMapper_ + > + struct TupleConstructor + : Tuple + { + using TypeIdxIterator = typename IndexIter::Seq; + + protected: + template + TupleConstructor (SRC values, IndexSeq*) + : Tuple (_ElmMapper_, idx>{values}...) + { } + + + public: + template + 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`, + * which in turn picks and converts the value for the n-th + * tuple element. + */ + template + struct ElementExtractor; + + + template + using ExtractArg = typename ElementExtractor::template Access; + + + /** + * 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`, which + * allows us to fill an (argument) tuple from a sequence of generic + * data values, with run-time type compatibility check. + */ + template + Tuple + buildTuple (SRC values) + { + return TupleConstructor (values); + } + diff --git a/src/lib/meta/tuple-record-init.hpp b/src/lib/meta/tuple-record-init.hpp new file mode 100644 index 000000000..1219de4a6 --- /dev/null +++ b/src/lib/meta/tuple-record-init.hpp @@ -0,0 +1,216 @@ +/* + TUPLE-RECORD-INIT.hpp - adapter to construct tuple components from GenNode entries + + Copyright (C) Lumiera.org + 2016, Hermann Vosseler + + 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::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 + struct GenNodeAccessor + : boost::noncopyable + { + + template + struct allow_Conversion + : __and_ + ,__not_::TypePlain + ,typename Strip::TypePlain>> + > + { }; + + using SupportedSourceTypes = typename Filter::List; + + + + struct ConverterBase + : DataCapPredicate + { + char buffer[sizeof(TAR)]; + }; + + template + 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 (&converter_.buffer); + } + + + public: + GenNodeAccessor (GenNode const& node) + : converter_() + { + if (not node.data.accept (converter_)) + throw error::Invalid ("Unable to build «" + util::typeStr() + +"» 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` as argument. The embedded template + * `Access` 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 + struct ElementExtractor> + { + template + using TargetType = typename Pick, i>::Type; + + + template + struct Access + { + Rec const& values; + + operator TargetType () + { + return GenNodeAccessor> (values.child(i)); + } + }; + }; + + + +}} // namespace lib::meta +#endif /*LIB_META_TUPLE_RECORD_INIT_H*/ diff --git a/tests/library/diff/generic-record-representation-test.cpp b/tests/library/diff/generic-record-representation-test.cpp index cbe258b0f..0d4d516f2 100644 --- a/tests/library/diff/generic-record-representation-test.cpp +++ b/tests/library/diff/generic-record-representation-test.cpp @@ -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 <