2017-09-28 16:28:15 +02:00
|
|
|
|
/*
|
|
|
|
|
|
VariadicArgumentPicker(Test) - access individual arguments from a variadic template
|
|
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2008, Hermann Vosseler <Ichthyostega@web.de>
|
2017-09-28 16:28:15 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** 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. See the file COPYING for further details.
|
2017-09-28 16:28:15 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2017-09-28 16:28:15 +02:00
|
|
|
|
|
|
|
|
|
|
|
2017-09-29 03:21:47 +02:00
|
|
|
|
/** @file variadic-argument-picker-test.cpp
|
2017-09-28 16:28:15 +02:00
|
|
|
|
** verify metaprogramming manipulation on a variadic parameter pack.
|
|
|
|
|
|
** The unittest VariadicArgumentPicker_test calls a variadic function with different
|
|
|
|
|
|
** number of arguments and it employs variadic-helper.hpp to alter the order of passed arguments.
|
|
|
|
|
|
**
|
|
|
|
|
|
** The primary difficulty when dealing with variadic templates is the fact that a variadic
|
|
|
|
|
|
** parameter pack is not a first class type, rather a special language construct which can only
|
|
|
|
|
|
** be used to perform a pattern match. For this reason, metaprogramming has to proceed "backwards",
|
|
|
|
|
|
** by invoking a delegate template, thereby dissecting the parameter pack by a pattern match.
|
|
|
|
|
|
** The key technique for more elaborate manipulation is to construct a variadic index sequence
|
|
|
|
|
|
** as a helper, and then to expand that index sequence to drive instantiation of a helper
|
|
|
|
|
|
** template on individual arguments.
|
|
|
|
|
|
**
|
|
|
|
|
|
** @see variadic-helper.hpp
|
|
|
|
|
|
** @see path-array.hpp
|
|
|
|
|
|
** @see TypeSeqManipl_test
|
|
|
|
|
|
** @see TupleRecordInit_test
|
|
|
|
|
|
** @see TypeListManip_test
|
|
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
|
|
|
|
|
#include "lib/meta/variadic-helper.hpp"
|
|
|
|
|
|
#include "lib/format-string.hpp"
|
|
|
|
|
|
#include "lib/format-cout.hpp"
|
|
|
|
|
|
#include "lib/format-util.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
|
|
using util::_Fmt;
|
|
|
|
|
|
using util::join;
|
|
|
|
|
|
using util::toString;
|
|
|
|
|
|
using std::forward;
|
|
|
|
|
|
using std::string;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace meta {
|
|
|
|
|
|
namespace test {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace { // test data
|
|
|
|
|
|
|
|
|
|
|
|
template<int n>
|
|
|
|
|
|
struct N
|
|
|
|
|
|
{
|
|
|
|
|
|
uint i_;
|
|
|
|
|
|
static int instanceCnt;
|
|
|
|
|
|
|
2024-11-13 02:23:23 +01:00
|
|
|
|
N (uint x = rani(1 + abs(n)))
|
2017-09-28 16:28:15 +02:00
|
|
|
|
: i_{x}
|
|
|
|
|
|
{
|
|
|
|
|
|
++instanceCnt;
|
|
|
|
|
|
}
|
|
|
|
|
|
~N()
|
|
|
|
|
|
{
|
|
|
|
|
|
--instanceCnt;
|
|
|
|
|
|
}
|
|
|
|
|
|
N (N const& r)
|
|
|
|
|
|
: i_{r.i_}
|
|
|
|
|
|
{
|
|
|
|
|
|
++instanceCnt;
|
|
|
|
|
|
}
|
2017-09-29 03:21:47 +02:00
|
|
|
|
N (N&& rr)
|
|
|
|
|
|
{
|
|
|
|
|
|
std::swap (i_, rr.i_);
|
|
|
|
|
|
++instanceCnt;
|
|
|
|
|
|
}
|
2017-09-28 16:28:15 +02:00
|
|
|
|
|
2025-06-07 23:59:57 +02:00
|
|
|
|
// instanceCnt remains same...
|
2017-09-28 16:28:15 +02:00
|
|
|
|
N& operator= (N const&) = default;
|
|
|
|
|
|
N& operator= (N&&) = default;
|
|
|
|
|
|
|
|
|
|
|
|
operator string() const
|
|
|
|
|
|
{
|
|
|
|
|
|
static _Fmt format{"%s──%s─"};
|
|
|
|
|
|
return format % typeStr(*this) % i_;
|
|
|
|
|
|
}
|
2025-06-07 23:59:57 +02:00
|
|
|
|
friend bool operator== (N const& l, N const& r) { return l.i_ == r.i_; }
|
|
|
|
|
|
friend bool operator!= (N const& l, N const& r) { return l.i_ != r.i_; }
|
2017-09-28 16:28:15 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template<int n>
|
|
|
|
|
|
int N<n>::instanceCnt = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Variadic test function to invoke
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class...ARGS>
|
|
|
|
|
|
string
|
|
|
|
|
|
fun (ARGS&& ...args)
|
|
|
|
|
|
{
|
|
|
|
|
|
static _Fmt format{"%2d╎%s┤"};
|
|
|
|
|
|
return format % sizeof...(ARGS) % join({toString(args)...}, "┼");
|
|
|
|
|
|
}
|
|
|
|
|
|
} // (End) test data
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************//**
|
|
|
|
|
|
* @test check the basic utilities for manipulating variadic template arguments.
|
|
|
|
|
|
* - single out and pick an argument designated by index
|
|
|
|
|
|
* - pick an argument with fallback on default construction
|
|
|
|
|
|
* - demonstrate usage by reordering the position of arguments
|
|
|
|
|
|
*/
|
|
|
|
|
|
class VariadicArgumentPicker_test : public Test
|
|
|
|
|
|
{
|
|
|
|
|
|
virtual void
|
2025-06-07 23:59:57 +02:00
|
|
|
|
run (Arg)
|
2017-09-28 16:28:15 +02:00
|
|
|
|
{
|
2024-11-13 02:23:23 +01:00
|
|
|
|
seedRand();
|
2017-09-28 16:28:15 +02:00
|
|
|
|
verify_fixture();
|
|
|
|
|
|
check_pickArg ();
|
|
|
|
|
|
check_pickInit();
|
|
|
|
|
|
check_reorderedArguments();
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (0 == N<0>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<1>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<2>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<3>::instanceCnt);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
verify_fixture ()
|
|
|
|
|
|
{
|
|
|
|
|
|
CHECK (0 == N<0>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<1>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<2>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<3>::instanceCnt);
|
|
|
|
|
|
{
|
|
|
|
|
|
N<1> n1;
|
|
|
|
|
|
N<2> n2;
|
|
|
|
|
|
N<3> n3;
|
2017-09-29 03:21:47 +02:00
|
|
|
|
N<3> nn{n3};
|
|
|
|
|
|
cout << fun (n1,n2,n3,nn) << endl;
|
2017-09-28 16:28:15 +02:00
|
|
|
|
|
|
|
|
|
|
CHECK (0 == N<0>::instanceCnt);
|
|
|
|
|
|
CHECK (1 == N<1>::instanceCnt);
|
|
|
|
|
|
CHECK (1 == N<2>::instanceCnt);
|
2017-09-29 03:21:47 +02:00
|
|
|
|
CHECK (2 == N<3>::instanceCnt);
|
2017-09-28 16:28:15 +02:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
CHECK (0 == N<0>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<1>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<2>::instanceCnt);
|
|
|
|
|
|
CHECK (0 == N<3>::instanceCnt);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
check_pickArg ()
|
|
|
|
|
|
{
|
|
|
|
|
|
N<1> n1;
|
|
|
|
|
|
N<2> n2;
|
|
|
|
|
|
N<3> n3;
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (n1 == pickArg<0> (n1,n2,n3));
|
|
|
|
|
|
CHECK (n2 == pickArg<1> (n1,n2,n3));
|
|
|
|
|
|
CHECK (n3 == pickArg<2> (n1,n2,n3));
|
2017-09-29 00:51:13 +02:00
|
|
|
|
|
|
|
|
|
|
// does not compile...
|
|
|
|
|
|
// pickArg<3> (n1,n2,n3);
|
2017-09-29 02:35:15 +02:00
|
|
|
|
|
2017-09-29 03:21:47 +02:00
|
|
|
|
N<0> n0{42};
|
|
|
|
|
|
CHECK (n0 != pickArg<0> (N<0>{23}));
|
2017-09-29 02:35:15 +02:00
|
|
|
|
CHECK (n0 == pickArg<0> (N<0>{n0}));
|
2017-09-28 16:28:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
check_pickInit ()
|
|
|
|
|
|
{
|
2017-09-29 02:35:15 +02:00
|
|
|
|
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) ));
|
|
|
|
|
|
|
2017-09-29 03:21:47 +02:00
|
|
|
|
CHECK ("N<0>" == typeStr(pickInit<3,N0> (n1,n2)));
|
|
|
|
|
|
CHECK ("N<0>" == typeStr(pickInit<3,N0> (1,"2")));
|
|
|
|
|
|
CHECK ("N<0>" == typeStr(pickInit<3,N0> ()));
|
2017-09-28 16:28:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-09-29 03:21:47 +02:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Demonstration of argument manipulation through metaprogramming.
|
|
|
|
|
|
* This function invokes the ubiquitous `fun` test function with arbitrary arguments,
|
|
|
|
|
|
* but it re-orders and even prunes arguments as dictated by the Index sequence parameter.
|
|
|
|
|
|
* @remarks Note some fine points:
|
|
|
|
|
|
* - the value of the parameter `IndexSeq<idx...>` is irrelevant
|
|
|
|
|
|
* - rather, its sole purpose is to _pattern match_ against the type
|
|
|
|
|
|
* - we use `IndexSeq` just as an container to hold the sequence `idx...`
|
|
|
|
|
|
* - the expression within the function argument list expands the argument packs
|
|
|
|
|
|
* - and both of them are treated separately
|
|
|
|
|
|
* - the actual arguments are wrapped into a `std::forward` for _perfect forwarding_
|
|
|
|
|
|
* - but the key trick of the whole operation lies in the expansion of the `idx...` pack
|
|
|
|
|
|
* - note that `idx` without the `...` sits within the template list of `pickArg<idx>`
|
|
|
|
|
|
* - while the corresponding expansion operator is outmost and thus works on the whole expression
|
|
|
|
|
|
* - and thus it is the `idx...` pack which actually drives the generation of several `pickArg` instantiations
|
2025-06-07 23:59:57 +02:00
|
|
|
|
* - in the end the net effect is that the _Index sequence_ absolutely dictates which arguments are picked
|
2017-09-29 03:21:47 +02:00
|
|
|
|
*/
|
2017-09-29 02:35:15 +02:00
|
|
|
|
template<class...ARGS, size_t...idx>
|
|
|
|
|
|
static auto
|
|
|
|
|
|
call_with_reversed_arguments (IndexSeq<idx...>, ARGS&& ...args)
|
|
|
|
|
|
{
|
|
|
|
|
|
return fun (pickArg<idx> (forward<ARGS>(args)...) ...);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-09-29 03:21:47 +02:00
|
|
|
|
/** @test demonstrate reordering of arguments */
|
2017-09-28 16:28:15 +02:00
|
|
|
|
void
|
|
|
|
|
|
check_reorderedArguments ()
|
|
|
|
|
|
{
|
2017-09-29 02:35:15 +02:00
|
|
|
|
N<0> n0;
|
|
|
|
|
|
N<1> n1;
|
|
|
|
|
|
N<2> n2;
|
|
|
|
|
|
N<3> n3;
|
|
|
|
|
|
|
|
|
|
|
|
cout << fun (n0,n1,n2,n3) << endl;
|
|
|
|
|
|
|
2025-07-05 20:08:18 +02:00
|
|
|
|
using Backwards = BuildIndexSeq<4>::Descending; // 3,2,1,0
|
|
|
|
|
|
using Back2 = BuildIndexSeq<2>::Descending; // 1,0
|
|
|
|
|
|
using After2 = BuildIndexSeq<4>::After<2>; // 2,3
|
2017-09-29 02:35:15 +02:00
|
|
|
|
|
2017-09-29 03:21:47 +02:00
|
|
|
|
cout << call_with_reversed_arguments (Backwards(), n0,n1,n2,n3) <<endl;
|
|
|
|
|
|
cout << call_with_reversed_arguments (Back2() , n0,n1,n2,n3) <<endl;
|
|
|
|
|
|
cout << call_with_reversed_arguments (After2() , n0,n1,n2,n3) <<endl;
|
2017-09-28 16:28:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Register this test class... */
|
|
|
|
|
|
LAUNCHER (VariadicArgumentPicker_test, "unit common");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}}} // namespace lib::meta::test
|