Metaprogramming: extend testcase and remould pickInit to support arbitrary arguments

as it turned out, the solution from yesterday works only with uniform argument lists,
but not with arbitrarily mixed types. Moreover the whole trickery with the
indices was shitty -- better use a predicate decision on template argument level.
This simple solution somehow just didn't occur to me...
This commit is contained in:
Fischlurch 2017-09-29 02:35:15 +02:00
parent 636ab6e608
commit 4348cd462c
2 changed files with 81 additions and 26 deletions

View file

@ -150,6 +150,9 @@ namespace meta {
{
template<size_t i>
using AppendElm = IndexSeq<idx..., i>;
template<size_t i>
using PrependElm = IndexSeq<i, idx...>;
};
/**
@ -159,10 +162,11 @@ namespace meta {
template<size_t n>
struct BuildIndexSeq
{
using Ascending = typename BuildIndexSeq<n-1>::Ascending::template AppendElm<n-1>;
using Ascending = typename BuildIndexSeq<n-1>::Ascending::template AppendElm<n-1>;
using Descending = typename BuildIndexSeq<n-1>::Descending::template PrependElm<n-1>;
template<size_t d>
using OffsetBy = typename BuildIndexSeq<n-1>::template OffsetBy<d>::template AppendElm<n-1+d>;
using OffsetBy = typename BuildIndexSeq<n-1>::template OffsetBy<d>::template AppendElm<n-1+d>;
template<size_t x>
using FilledWith = typename BuildIndexSeq<n-1>::template FilledWith<x>::template AppendElm<x>;
@ -179,10 +183,11 @@ namespace meta {
{
using Empty = IndexSeq<>;
using Ascending = Empty;
using Ascending = Empty;
using Descending = Empty;
template<size_t>
using OffsetBy = Empty;
using OffsetBy = Empty;
template<size_t>
using FilledWith = Empty;
@ -205,10 +210,11 @@ namespace meta {
enum {SIZ = sizeof...(TYPES) };
using Builder = BuildIndexSeq<SIZ>;
using Ascending = typename Builder::Ascending;
using Ascending = typename Builder::Ascending;
using Descending = typename Builder::Descending;
template<size_t d>
using OffsetBy = typename Builder::template OffsetBy<d>;
using OffsetBy = typename Builder::template OffsetBy<d>;
template<size_t x>
using FilledWith = typename Builder::template FilledWith<x>;
@ -228,10 +234,11 @@ namespace meta {
enum {SIZ = lib::meta::count<typename Types<TYPES...>::List>::value };
using Builder = BuildIndexSeq<SIZ>;
using Ascending = typename Builder::Ascending;
using Ascending = typename Builder::Ascending;
using Descending = typename Builder::Descending;
template<size_t d>
using OffsetBy = typename Builder::template OffsetBy<d>;
using OffsetBy = typename Builder::template OffsetBy<d>;
template<size_t x>
using FilledWith = typename Builder::template FilledWith<x>;
@ -245,6 +252,9 @@ namespace meta {
/* ==== Manipulation of variadic arguments ==== **/
namespace { // Implementation delegate template...
@ -274,6 +284,27 @@ namespace meta {
return std::forward<ARG>(a);
}
};
/**
* @internal helper to decide if SelectVararg shall be applied.
* When the boolean condition does not hold, then, instead of selecting
* from the argument list, an element of type DEFAULT is created as fallback.
*/
template<bool, typename, size_t idx>
struct SelectOrInit
: SelectVararg<idx>
{ };
template<typename DEFAULT, size_t idx>
struct SelectOrInit<false, DEFAULT, idx>
{
template<typename...ARGS>
static DEFAULT
get (ARGS&&...)
{
return DEFAULT{};
}
};
}//(End)Implementation
@ -305,20 +336,7 @@ namespace meta {
constexpr inline auto
pickInit (ARGS&&... args)
{
enum{
SIZ = sizeof...(args),
IDX = idx < SIZ? idx : 0
};
return (idx < SIZ)? SelectVararg<IDX>::get (std::forward<ARGS> (args)...)
: DEFAULT{};
}
template<size_t, typename DEFAULT>
constexpr inline DEFAULT
pickInit ()
{
return DEFAULT{};
return SelectOrInit<(idx < sizeof...(args)), DEFAULT, idx>::get (std::forward<ARGS> (args)...);
}

View file

@ -185,23 +185,60 @@ namespace test {
// does not compile...
// pickArg<3> (n1,n2,n3);
N<0> n0;
CHECK (n0 != pickArg<0> (N<0>{}));
CHECK (n0 == pickArg<0> (N<0>{n0}));
}
void
check_pickInit ()
{
UNIMPLEMENTED ("pick or init");
N<1> n1;
N<2> n2;
using N0 = N<0>;
CHECK (1 == (pickInit<0,int> (1,2) ));
CHECK (2 == (pickInit<1,int> (1,2) ));
CHECK (0 == (pickInit<2,int> (1,2) ));
CHECK (n1 == (pickInit<0,N0> (n1,n2) ));
CHECK (n2 == (pickInit<1,N0> (n1,n2) ));
N0 n0 = pickInit<3,N0> (n1,n2);
CHECK (n0 != (pickInit<3,N0> (n1,n2) )); // same type (N<0>) but different new instance
CHECK ("N<0>" == typeStr(pickInit<2,N0> (n1,n2)));
CHECK ("N<0>" == typeStr(pickInit<2,N0> (1,"2")));
CHECK ("N<0>" == typeStr(pickInit<2,N0> ()));
}
template<class...ARGS, size_t...idx>
static auto
call_with_reversed_arguments (IndexSeq<idx...>, ARGS&& ...args)
{
return fun (pickArg<idx> (forward<ARGS>(args)...) ...);
}
void
check_reorderedArguments ()
{
UNIMPLEMENTED ("reorder arguments of function call");
N<0> n0;
N<1> n1;
N<2> n2;
N<3> n3;
cout << fun (n0,n1,n2,n3) << endl;
using Backwards = typename BuildIndexSeq<4>::Descending;
using Back2 = typename BuildIndexSeq<2>::Descending;
using After2 = typename BuildIndexSeq<4>::After<2>;
cout << call_with_reversed_arguments (Backwards(), n0,n1,n2,n3);
cout << call_with_reversed_arguments (Back2() , n0,n1,n2,n3);
cout << call_with_reversed_arguments (After2() , n0,n1,n2,n3);
}
};