2009-06-28 20:41:33 +02:00
|
|
|
|
/*
|
2009-06-29 06:33:42 +02:00
|
|
|
|
FunctionComposition(Test) - functional composition and partial application
|
2010-12-10 02:55:40 +01: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
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2009, Hermann Vosseler <Ichthyostega@web.de>
|
2010-12-10 02:55:40 +01: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.
|
2010-12-10 02:55:40 +01: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
|
|
|
|
* *****************************************************************/
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2017-02-22 01:54:20 +01:00
|
|
|
|
/** @file function-composition-test.cpp
|
2017-02-22 03:17:18 +01:00
|
|
|
|
** unit test \ref FunctionComposition_test
|
2016-11-03 18:20:10 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
|
|
|
|
|
#include "lib/test/test-helper.hpp"
|
|
|
|
|
|
#include "lib/meta/typelist.hpp"
|
|
|
|
|
|
#include "lib/meta/function.hpp"
|
|
|
|
|
|
#include "lib/meta/function-closure.hpp"
|
|
|
|
|
|
#include "meta/typelist-diagnostics.hpp"
|
|
|
|
|
|
|
2016-01-17 23:55:41 +01:00
|
|
|
|
#include <tuple>
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2011-12-03 02:56:50 +01:00
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace meta {
|
|
|
|
|
|
namespace test {
|
2009-06-29 06:33:42 +02:00
|
|
|
|
|
2009-07-03 14:31:52 +02:00
|
|
|
|
using ::test::Test;
|
2025-06-05 19:11:46 +02:00
|
|
|
|
using lib::test::showType;
|
2023-11-22 22:11:59 +01:00
|
|
|
|
using lib::meta::_Fun;
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
using func::bindFirst;
|
2009-07-08 05:36:02 +02:00
|
|
|
|
using func::bindLast;
|
2009-07-03 14:31:52 +02:00
|
|
|
|
using func::PApply;
|
2009-07-08 05:36:02 +02:00
|
|
|
|
using func::BindToArgument;
|
2016-01-17 23:55:41 +01:00
|
|
|
|
using std::make_tuple;
|
|
|
|
|
|
using std::get;
|
2009-06-29 06:33:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
2010-12-17 22:51:27 +01:00
|
|
|
|
namespace { // test functions
|
|
|
|
|
|
|
|
|
|
|
|
Num<1> _1_;
|
|
|
|
|
|
Num<2> _2_;
|
|
|
|
|
|
Num<3> _3_;
|
|
|
|
|
|
Num<4> _4_;
|
|
|
|
|
|
Num<5> _5_;
|
|
|
|
|
|
Num<6> _6_;
|
|
|
|
|
|
Num<7> _7_;
|
|
|
|
|
|
Num<8> _8_;
|
|
|
|
|
|
Num<9> _9_;
|
|
|
|
|
|
|
|
|
|
|
|
/** "Function-1" will be used at the front side, accepting a tuple of values */
|
|
|
|
|
|
template<uint i>
|
|
|
|
|
|
Num<i>
|
|
|
|
|
|
fun11 ( Num<i> val1
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
return val1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<uint i, uint ii>
|
|
|
|
|
|
Num<i>
|
|
|
|
|
|
fun12 ( Num<i> val1
|
|
|
|
|
|
, Num<ii> val2
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
val1.o_ += val2.o_;
|
|
|
|
|
|
return val1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<uint i, uint ii, uint iii>
|
|
|
|
|
|
Num<i>
|
|
|
|
|
|
fun13 ( Num<i> val1
|
|
|
|
|
|
, Num<ii> val2
|
|
|
|
|
|
, Num<iii> val3
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
val1.o_ += val2.o_ + val3.o_;
|
|
|
|
|
|
return val1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<uint i, uint ii, uint iii, uint iv>
|
|
|
|
|
|
Num<i>
|
|
|
|
|
|
fun14 ( Num<i> val1
|
|
|
|
|
|
, Num<ii> val2
|
|
|
|
|
|
, Num<iii> val3
|
|
|
|
|
|
, Num<iv> val4
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
val1.o_ += val2.o_ + val3.o_ + val4.o_;
|
|
|
|
|
|
return val1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<uint i, uint ii, uint iii, uint iv, uint v>
|
|
|
|
|
|
Num<i>
|
|
|
|
|
|
fun15 ( Num<i> val1
|
|
|
|
|
|
, Num<ii> val2
|
|
|
|
|
|
, Num<iii> val3
|
|
|
|
|
|
, Num<iv> val4
|
|
|
|
|
|
, Num<v> val5
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
val1.o_ += val2.o_ + val3.o_ + val4.o_ + val5.o_;
|
|
|
|
|
|
return val1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** "Function-2" can be chained behind fun1 */
|
|
|
|
|
|
template<class II>
|
|
|
|
|
|
int
|
|
|
|
|
|
fun2 (II val)
|
|
|
|
|
|
{
|
|
|
|
|
|
return val.o_;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // (End) test data
|
2009-06-29 06:33:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-10-24 23:06:36 +02:00
|
|
|
|
/**************************************************************************//**
|
2009-06-29 06:33:42 +02:00
|
|
|
|
* @test this test covers some extensions and variations on function closures:
|
2009-07-09 04:41:44 +02:00
|
|
|
|
* - partial application of a function, returning a partial closure
|
2012-01-07 00:02:03 +01:00
|
|
|
|
* - variation: binding an arbitrary term, might even be a nested binder
|
2009-07-01 03:23:26 +02:00
|
|
|
|
* - chaining of two functions with suitable arguments ("composition")
|
2009-06-28 20:41:33 +02:00
|
|
|
|
*/
|
2009-06-29 06:33:42 +02:00
|
|
|
|
class FunctionComposition_test : public Test
|
2009-06-28 20:41:33 +02:00
|
|
|
|
{
|
|
|
|
|
|
virtual void
|
2010-12-10 02:55:40 +01:00
|
|
|
|
run (Arg)
|
2009-06-28 20:41:33 +02:00
|
|
|
|
{
|
2023-11-22 22:11:59 +01:00
|
|
|
|
check_diagnostics();
|
|
|
|
|
|
check_partialApplication();
|
|
|
|
|
|
check_functionalComposition();
|
|
|
|
|
|
check_bindToArbitraryParameter();
|
|
|
|
|
|
|
|
|
|
|
|
verify_referenceHandling();
|
2009-06-28 20:41:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-01-07 00:02:03 +01:00
|
|
|
|
/** verify the test input data */
|
2009-06-28 20:41:33 +02:00
|
|
|
|
void
|
|
|
|
|
|
check_diagnostics ()
|
|
|
|
|
|
{
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (6 == (fun13<1,2,3> (_1_, _2_, _3_)).o_ );
|
|
|
|
|
|
CHECK (6 == (fun13<1,1,1> (Num<1>(3), Num<1>(2), Num<1>(1))).o_ );
|
2010-12-17 22:51:27 +01:00
|
|
|
|
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK ( 1 == fun2 (fun11<1> (_1_)) );
|
|
|
|
|
|
CHECK ( 3 == fun2 (fun12<1,2> (_1_, _2_)) );
|
|
|
|
|
|
CHECK ( 6 == fun2 (fun13<1,2,3> (_1_, _2_, _3_)) );
|
|
|
|
|
|
CHECK (10 == fun2 (fun14<1,2,3,4> (_1_, _2_, _3_, _4_)) );
|
|
|
|
|
|
CHECK (15 == fun2 (fun15<1,2,3,4,5> (_1_, _2_, _3_, _4_, _5_)) );
|
2010-12-17 22:51:27 +01:00
|
|
|
|
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK ( 9 == fun2 (fun13<2,3,4> (_2_, _3_, _4_)) );
|
|
|
|
|
|
CHECK (18 == fun2 (fun13<5,6,7> (_5_, _6_, _7_)) );
|
|
|
|
|
|
CHECK (24 == fun2 (fun13<9,8,7> (_9_, _8_, _7_)) );
|
2009-06-28 20:41:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2009-06-29 06:34:20 +02:00
|
|
|
|
check_partialApplication ()
|
2009-06-28 20:41:33 +02:00
|
|
|
|
{
|
2009-06-29 06:34:20 +02:00
|
|
|
|
// Because the code of the partial function application is very technical,
|
|
|
|
|
|
// the following might serve as explanation what actually happens....
|
|
|
|
|
|
// (and actually it's a leftover from initial debugging)
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2009-06-29 06:34:20 +02:00
|
|
|
|
typedef Num<1> Sig123(Num<1>, Num<2>, Num<3>); // signature of the original function
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2009-06-29 06:34:20 +02:00
|
|
|
|
typedef Num<1> Sig23(Num<2>, Num<3>); // signature after having closed over the first argument
|
2025-06-04 01:49:07 +02:00
|
|
|
|
using F23 = function<Sig23>; // and a std::function object to hold such a function
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2025-06-04 01:49:07 +02:00
|
|
|
|
Sig123& f = fun13<1,2,3>; // the actual input: a reference to the bare function
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
2009-06-29 06:34:20 +02:00
|
|
|
|
// Version1: do a direct argument binding----------------- //
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2018-01-12 03:03:25 +01:00
|
|
|
|
using PH1 = std::_Placeholder<1>; // std::function argument placeholders
|
2016-01-17 23:55:41 +01:00
|
|
|
|
using PH2 = std::_Placeholder<2>;
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2009-06-29 06:34:20 +02:00
|
|
|
|
PH1 ph1; // these empty structs are used to mark the arguments to be kept "open"
|
|
|
|
|
|
PH2 ph2;
|
|
|
|
|
|
Num<1> num18 (18); // ...and this value is for closing the first function argument
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2016-01-17 23:55:41 +01:00
|
|
|
|
F23 fun_23 = std::bind (f, num18 // do the actual binding (i.e. close the first argument with a constant value)
|
|
|
|
|
|
, ph1
|
|
|
|
|
|
, ph2
|
|
|
|
|
|
);
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
clean-up: simplify function-closure -- investigate `BindToArgument`
...because swapping in the new standards-based implementation
leads to compile failures on tests to cover out-of-bounds cases.
Under the (wrong) assumption, that some mistake must be hidden in
the Splice-metafunction, I first provided a complete test coverage;
while the actual problem was right below my nose, and quite obvious...
The old implementation, being based on a case distinction over the argument count,
simply was not able even to notice excess arguments; other the new implementation,
based on variadics and `std::apply`, which is fully generic and thus
passes excess arguments to `std::bind` when a position beyond the actual
argument list is specified to be closed.
The old behaviour was to silently ignore such an out-of-bounds spec,
and this can be reinstated by explicitly capping the prepared tuple
of binders and actual arguments passed to `std::bind`
Another question of course is, if being tolerant here is a good idea.
And beyond that, function-closure.hpp is still terrifyingly complex,
unorganised and use-case driven, to start with....
2025-06-05 18:00:05 +02:00
|
|
|
|
int r1 = fun_23 (_2_,_3_).o_; // and invoke the resulting functor ("closure"), providing the remaining arguments
|
|
|
|
|
|
CHECK (23 == r1); // result ≡ num18 + _2_ + _3_ ≙ 18 + 2 + 3
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-06-29 06:34:20 +02:00
|
|
|
|
// Version2: extract the binding arguments from a tuple--- //
|
|
|
|
|
|
|
2025-06-07 18:04:59 +02:00
|
|
|
|
using PartialArg = Tuple<Types<Num<1>, PH1, PH2>>; // Tuple type to hold the binding values. Note the placeholder types
|
2025-06-04 01:49:07 +02:00
|
|
|
|
PartialArg arg{num18, PH1(), PH2()}; // Value for partial application (the placeholders are default constructed)
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
2016-01-17 23:55:41 +01:00
|
|
|
|
fun_23 = std::bind (f, get<0>(arg) // now extract the values to bind from this tuple
|
|
|
|
|
|
, get<1>(arg)
|
|
|
|
|
|
, get<2>(arg)
|
|
|
|
|
|
);
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
clean-up: simplify function-closure -- investigate `BindToArgument`
...because swapping in the new standards-based implementation
leads to compile failures on tests to cover out-of-bounds cases.
Under the (wrong) assumption, that some mistake must be hidden in
the Splice-metafunction, I first provided a complete test coverage;
while the actual problem was right below my nose, and quite obvious...
The old implementation, being based on a case distinction over the argument count,
simply was not able even to notice excess arguments; other the new implementation,
based on variadics and `std::apply`, which is fully generic and thus
passes excess arguments to `std::bind` when a position beyond the actual
argument list is specified to be closed.
The old behaviour was to silently ignore such an out-of-bounds spec,
and this can be reinstated by explicitly capping the prepared tuple
of binders and actual arguments passed to `std::bind`
Another question of course is, if being tolerant here is a good idea.
And beyond that, function-closure.hpp is still terrifyingly complex,
unorganised and use-case driven, to start with....
2025-06-05 18:00:05 +02:00
|
|
|
|
int r2 = fun_23 (_2_,_3_).o_; // and invoke the resulting functor....
|
|
|
|
|
|
CHECK (23 == r2);
|
|
|
|
|
|
|
|
|
|
|
|
// function-closure.hpp defines a shorthand for this operation
|
|
|
|
|
|
fun_23 = func::bindArgTuple (f, arg);
|
|
|
|
|
|
|
|
|
|
|
|
int r3 = fun_23 (_2_,_3_).o_;
|
|
|
|
|
|
CHECK (23 == r3);
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Version3: let the PApply-template do the work for us--- //
|
|
|
|
|
|
|
2025-06-07 18:04:59 +02:00
|
|
|
|
using ArgTypes = Types<Num<1>>; // now package just the argument(s) to be applied into a tuple
|
2025-06-04 01:49:07 +02:00
|
|
|
|
Tuple<ArgTypes> args_to_bind{Num<1>(18)};
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
|
|
|
|
|
fun_23 = PApply<Sig123, ArgTypes>::bindFront (f , args_to_bind);
|
|
|
|
|
|
// "bindFront" will close the parameters starting from left....
|
clean-up: simplify function-closure -- investigate `BindToArgument`
...because swapping in the new standards-based implementation
leads to compile failures on tests to cover out-of-bounds cases.
Under the (wrong) assumption, that some mistake must be hidden in
the Splice-metafunction, I first provided a complete test coverage;
while the actual problem was right below my nose, and quite obvious...
The old implementation, being based on a case distinction over the argument count,
simply was not able even to notice excess arguments; other the new implementation,
based on variadics and `std::apply`, which is fully generic and thus
passes excess arguments to `std::bind` when a position beyond the actual
argument list is specified to be closed.
The old behaviour was to silently ignore such an out-of-bounds spec,
and this can be reinstated by explicitly capping the prepared tuple
of binders and actual arguments passed to `std::bind`
Another question of course is, if being tolerant here is a good idea.
And beyond that, function-closure.hpp is still terrifyingly complex,
unorganised and use-case driven, to start with....
2025-06-05 18:00:05 +02:00
|
|
|
|
|
|
|
|
|
|
int r4 = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
|
|
|
|
|
|
CHECK (23 == r4);
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Version4: as you'd typically do it in real life-------- //
|
|
|
|
|
|
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
fun_23 = func::bindFirst (f, Num<1>(18)); // use the convenience function API to close over a single value
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
clean-up: simplify function-closure -- investigate `BindToArgument`
...because swapping in the new standards-based implementation
leads to compile failures on tests to cover out-of-bounds cases.
Under the (wrong) assumption, that some mistake must be hidden in
the Splice-metafunction, I first provided a complete test coverage;
while the actual problem was right below my nose, and quite obvious...
The old implementation, being based on a case distinction over the argument count,
simply was not able even to notice excess arguments; other the new implementation,
based on variadics and `std::apply`, which is fully generic and thus
passes excess arguments to `std::bind` when a position beyond the actual
argument list is specified to be closed.
The old behaviour was to silently ignore such an out-of-bounds spec,
and this can be reinstated by explicitly capping the prepared tuple
of binders and actual arguments passed to `std::bind`
Another question of course is, if being tolerant here is a good idea.
And beyond that, function-closure.hpp is still terrifyingly complex,
unorganised and use-case driven, to start with....
2025-06-05 18:00:05 +02:00
|
|
|
|
int r5 = fun_23(_2_,_3_).o_; // invoke the resulting functor...
|
|
|
|
|
|
CHECK (23 == r5);
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// what follows is the real unit test...
|
2025-06-04 01:49:07 +02:00
|
|
|
|
function<Sig123> func123{f}; // alternatively do it with an std::function object
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
fun_23 = func::bindFirst (func123, Num<1>(19));
|
|
|
|
|
|
int r6 = fun_23(_2_,_3_).o_;
|
|
|
|
|
|
CHECK (24 == r6);
|
2009-07-01 02:29:11 +02:00
|
|
|
|
|
2025-06-04 01:49:07 +02:00
|
|
|
|
using F12 = function<Num<1>(Num<1>, Num<2>)>;
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
F12 fun_12 = func::bindLast (f, Num<3>(20)); // close the *last* argument of a function
|
clean-up: simplify function-closure -- investigate `BindToArgument`
...because swapping in the new standards-based implementation
leads to compile failures on tests to cover out-of-bounds cases.
Under the (wrong) assumption, that some mistake must be hidden in
the Splice-metafunction, I first provided a complete test coverage;
while the actual problem was right below my nose, and quite obvious...
The old implementation, being based on a case distinction over the argument count,
simply was not able even to notice excess arguments; other the new implementation,
based on variadics and `std::apply`, which is fully generic and thus
passes excess arguments to `std::bind` when a position beyond the actual
argument list is specified to be closed.
The old behaviour was to silently ignore such an out-of-bounds spec,
and this can be reinstated by explicitly capping the prepared tuple
of binders and actual arguments passed to `std::bind`
Another question of course is, if being tolerant here is a good idea.
And beyond that, function-closure.hpp is still terrifyingly complex,
unorganised and use-case driven, to start with....
2025-06-05 18:00:05 +02:00
|
|
|
|
int r7 = fun_12(_1_,_2_).o_;
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
CHECK (23 == r7);
|
2009-07-01 02:29:11 +02:00
|
|
|
|
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
fun_12 = func::bindLast (func123, Num<3>(21)); // alternatively use a function object
|
clean-up: simplify function-closure -- investigate `BindToArgument`
...because swapping in the new standards-based implementation
leads to compile failures on tests to cover out-of-bounds cases.
Under the (wrong) assumption, that some mistake must be hidden in
the Splice-metafunction, I first provided a complete test coverage;
while the actual problem was right below my nose, and quite obvious...
The old implementation, being based on a case distinction over the argument count,
simply was not able even to notice excess arguments; other the new implementation,
based on variadics and `std::apply`, which is fully generic and thus
passes excess arguments to `std::bind` when a position beyond the actual
argument list is specified to be closed.
The old behaviour was to silently ignore such an out-of-bounds spec,
and this can be reinstated by explicitly capping the prepared tuple
of binders and actual arguments passed to `std::bind`
Another question of course is, if being tolerant here is a good idea.
And beyond that, function-closure.hpp is still terrifyingly complex,
unorganised and use-case driven, to start with....
2025-06-05 18:00:05 +02:00
|
|
|
|
int r8 = fun_12(_1_,_2_).o_;
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
CHECK (24 == r8);
|
|
|
|
|
|
|
|
|
|
|
|
Sig123* fP = &f; // a function pointer works too
|
|
|
|
|
|
fun_12 = func::bindLast (fP, Num<3>(22));
|
|
|
|
|
|
int r9 = fun_12(_1_,_2_).o_;
|
|
|
|
|
|
CHECK (25 == r9);
|
2009-07-01 03:23:26 +02:00
|
|
|
|
// cover more cases....
|
|
|
|
|
|
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
CHECK (1 == (func::bindLast (fun11<1> , _1_ ) ( ) ).o_);
|
|
|
|
|
|
CHECK (1+3 == (func::bindLast (fun12<1,3> , _3_ ) (_1_) ).o_);
|
|
|
|
|
|
CHECK (1+3+5 == (func::bindLast (fun13<1,3,5> , _5_ ) (_1_,_3_) ).o_);
|
|
|
|
|
|
CHECK (1+3+5+7 == (func::bindLast (fun14<1,3,5,7> , _7_ ) (_1_,_3_,_5_) ).o_);
|
|
|
|
|
|
CHECK (1+3+5+7+9 == (func::bindLast (fun15<1,3,5,7,9>, _9_ ) (_1_,_3_,_5_,_7_)).o_);
|
2009-07-01 03:23:26 +02:00
|
|
|
|
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
CHECK (9+8+7+6+5 == (func::bindFirst(fun15<9,8,7,6,5>, _9_ ) (_8_,_7_,_6_,_5_)).o_);
|
|
|
|
|
|
CHECK ( 8+7+6+5 == (func::bindFirst( fun14<8,7,6,5>, _8_ ) (_7_,_6_,_5_)).o_);
|
|
|
|
|
|
CHECK ( 7+6+5 == (func::bindFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_);
|
|
|
|
|
|
CHECK ( 6+5 == (func::bindFirst( fun12<6,5>, _6_ ) (_5_)).o_);
|
|
|
|
|
|
CHECK ( 5 == (func::bindFirst( fun11<5>, _5_ ) ( )).o_);
|
2009-07-01 04:47:52 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Finally a more convoluted example
|
|
|
|
|
|
// covering the general case of partial function closure:
|
2025-06-04 01:49:07 +02:00
|
|
|
|
typedef Num<5> Sig54321 (Num<5>, Num<4>, Num<3>, Num<2>, Num<1>); // Signature of the 5-argument function
|
|
|
|
|
|
typedef Num<5> Sig54 (Num<5>, Num<4>); // ...closing the last 3 arguments should yield this 2-argument function
|
2025-06-07 18:04:59 +02:00
|
|
|
|
using Args2Close = Types<Num<3>, Num<2>, Num<1>>; // Tuple type to hold the 3 argument values used for the closure
|
2009-07-01 04:47:52 +02:00
|
|
|
|
|
|
|
|
|
|
// Close the trailing 3 arguments of the 5-argument function...
|
2025-06-04 01:49:07 +02:00
|
|
|
|
function<Sig54> fun_54 = PApply<Sig54321,Args2Close>::bindBack (fun15<5,4,3,2,1>
|
|
|
|
|
|
,make_tuple (_3_,_2_,_1_)
|
2009-07-01 04:47:52 +02:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// apply the remaining argument values
|
2025-06-04 01:49:07 +02:00
|
|
|
|
Num<5> resN5 = fun_54(_5_,_4_);
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (5+4+3+2+1 == resN5.o_);
|
2009-06-28 20:41:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
2009-06-28 20:41:33 +02:00
|
|
|
|
void
|
2009-06-29 06:34:20 +02:00
|
|
|
|
check_functionalComposition ()
|
2009-06-28 20:41:33 +02:00
|
|
|
|
{
|
2009-07-03 13:49:12 +02:00
|
|
|
|
typedef int Sig2(Num<1>);
|
|
|
|
|
|
typedef Num<1> Sig11(Num<1>);
|
2009-07-01 04:47:52 +02:00
|
|
|
|
typedef Num<1> Sig12(Num<1>,Num<2>);
|
2009-07-03 13:49:12 +02:00
|
|
|
|
typedef Num<1> Sig13(Num<1>,Num<2>,Num<3>);
|
|
|
|
|
|
typedef Num<1> Sig14(Num<1>,Num<2>,Num<3>,Num<4>);
|
|
|
|
|
|
typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
|
|
|
|
|
|
|
|
|
|
|
|
Sig2 & ff = fun2< Num<1> >;
|
|
|
|
|
|
Sig11& f1 = fun11<1>;
|
|
|
|
|
|
Sig12& f2 = fun12<1,2>;
|
|
|
|
|
|
Sig13& f3 = fun13<1,2,3>;
|
|
|
|
|
|
Sig14& f4 = fun14<1,2,3,4>;
|
|
|
|
|
|
Sig15& f5 = fun15<1,2,3,4,5>;
|
|
|
|
|
|
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (1 == func::chained(f1, ff) (_1_) );
|
|
|
|
|
|
CHECK (1+2 == func::chained(f2, ff) (_1_,_2_) );
|
|
|
|
|
|
CHECK (1+2+3 == func::chained(f3, ff) (_1_,_2_,_3_) );
|
|
|
|
|
|
CHECK (1+2+3+4 == func::chained(f4, ff) (_1_,_2_,_3_,_4_) );
|
|
|
|
|
|
CHECK (1+2+3+4+5 == func::chained(f5, ff) (_1_,_2_,_3_,_4_,_5_) );
|
2009-07-09 17:22:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function<Sig15> f5_fun = f5; // also works with function objects...
|
|
|
|
|
|
function<Sig2> ff_fun = ff;
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (1+2+3+4+5 == func::chained(f5_fun, ff ) (_1_,_2_,_3_,_4_,_5_) );
|
|
|
|
|
|
CHECK (1+2+3+4+5 == func::chained(f5, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
|
|
|
|
|
|
CHECK (1+2+3+4+5 == func::chained(f5_fun, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
|
2009-06-28 20:41:33 +02:00
|
|
|
|
}
|
2009-06-29 06:34:20 +02:00
|
|
|
|
|
2009-07-06 05:25:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
check_bindToArbitraryParameter ()
|
|
|
|
|
|
{
|
|
|
|
|
|
typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
|
2009-07-08 05:36:02 +02:00
|
|
|
|
|
2009-07-06 05:25:33 +02:00
|
|
|
|
typedef Num<1> SigR1( Num<2>,Num<3>,Num<4>,Num<5>);
|
|
|
|
|
|
typedef Num<1> SigR2(Num<1>, Num<3>,Num<4>,Num<5>);
|
|
|
|
|
|
typedef Num<1> SigR3(Num<1>,Num<2>, Num<4>,Num<5>);
|
|
|
|
|
|
typedef Num<1> SigR4(Num<1>,Num<2>,Num<3>, Num<5>);
|
|
|
|
|
|
typedef Num<1> SigR5(Num<1>,Num<2>,Num<3>,Num<4> );
|
2009-07-09 04:41:44 +02:00
|
|
|
|
|
2009-07-08 05:36:02 +02:00
|
|
|
|
typedef Num<5> SigA5(Num<5>);
|
2009-07-06 05:25:33 +02:00
|
|
|
|
|
|
|
|
|
|
Sig15& f = fun15<1,2,3,4,5>;
|
2009-07-08 05:36:02 +02:00
|
|
|
|
SigA5& f5 = fun11<5>;
|
2009-07-06 05:25:33 +02:00
|
|
|
|
|
2025-02-17 21:18:37 +01:00
|
|
|
|
function<SigR1> f_bound_1 = BindToArgument<Sig15,char,0>::reduced (f, 55);
|
|
|
|
|
|
function<SigR2> f_bound_2 = BindToArgument<Sig15,char,1>::reduced (f, 55);
|
|
|
|
|
|
function<SigR3> f_bound_3 = BindToArgument<Sig15,char,2>::reduced (f, 55);
|
|
|
|
|
|
function<SigR4> f_bound_4 = BindToArgument<Sig15,char,3>::reduced (f, 55);
|
|
|
|
|
|
function<SigR5> f_bound_5 = BindToArgument<Sig15,char,4>::reduced (f, 55);
|
2009-07-08 05:36:02 +02:00
|
|
|
|
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (55+2+3+4+5 == f_bound_1 ( _2_,_3_,_4_,_5_) );
|
|
|
|
|
|
CHECK (1+55+3+4+5 == f_bound_2 (_1_, _3_,_4_,_5_) );
|
|
|
|
|
|
CHECK (1+2+55+4+5 == f_bound_3 (_1_,_2_, _4_,_5_) );
|
|
|
|
|
|
CHECK (1+2+3+55+5 == f_bound_4 (_1_,_2_,_3_, _5_) );
|
|
|
|
|
|
CHECK (1+2+3+4+55 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
|
2009-07-06 05:25:33 +02:00
|
|
|
|
|
2009-07-09 04:41:44 +02:00
|
|
|
|
|
|
|
|
|
|
// degenerate case: specify wrong argument position (behind end of argument list)
|
|
|
|
|
|
// causes the argument to be simply ignored and no binding to happen
|
2025-02-17 21:18:37 +01:00
|
|
|
|
function<Sig15> f_bound_X = BindToArgument<Sig15,char,5>::reduced (f, 88);
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (1+2+3+4+5 == f_bound_X (_1_,_2_,_3_,_4_,_5_) );
|
2009-07-09 04:41:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* check the convenient function-style API */
|
|
|
|
|
|
|
2014-04-03 22:42:48 +02:00
|
|
|
|
using std::bind;
|
2009-07-06 05:25:33 +02:00
|
|
|
|
|
2009-07-08 05:36:02 +02:00
|
|
|
|
f_bound_5 = bindLast (f, bind(f5, Num<5>(99)));
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
|
2009-07-09 04:41:44 +02:00
|
|
|
|
|
|
|
|
|
|
f_bound_5 = bindLast (f, bind(&f5, Num<5>(99))); // can bind function pointer
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
|
2009-07-09 04:41:44 +02:00
|
|
|
|
|
|
|
|
|
|
function<Sig15> asFunctor(f);
|
|
|
|
|
|
f_bound_5 = bindLast (asFunctor, bind(f5, Num<5>(88))); // use functor instead of direct ref
|
2010-12-10 02:55:40 +01:00
|
|
|
|
CHECK (1+2+3+4+88 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
|
2009-07-06 05:25:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-11-22 22:11:59 +01:00
|
|
|
|
|
|
|
|
|
|
/** @internal static function to pass as reference for test */
|
|
|
|
|
|
static long floorIt (float it) { return long(floor (it)); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @test ensure reference types and arguments are handled properly */
|
|
|
|
|
|
void
|
|
|
|
|
|
verify_referenceHandling()
|
|
|
|
|
|
{
|
|
|
|
|
|
int ii = 99;
|
|
|
|
|
|
float ff = 88;
|
|
|
|
|
|
auto fun = std::function{[](float& f, int& i, long l) -> double { return f + i + l; }};
|
|
|
|
|
|
auto& f1 = fun;
|
|
|
|
|
|
|
|
|
|
|
|
// build chained and a partially applied functors
|
|
|
|
|
|
auto chain = func::chained(f1,floorIt);
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
auto pappl = func::bindFirst (f1, ff);
|
2023-11-22 22:11:59 +01:00
|
|
|
|
|
|
|
|
|
|
using Sig1 = _Fun<decltype(f1)>::Sig;
|
|
|
|
|
|
using SigC = _Fun<decltype(chain)>::Sig;
|
|
|
|
|
|
using SigP = _Fun<decltype(pappl)>::Sig;
|
|
|
|
|
|
|
2025-06-05 19:11:46 +02:00
|
|
|
|
CHECK (showType<Sig1>() == "double (float&, int&, long)"_expect);
|
|
|
|
|
|
CHECK (showType<SigC>() == "long (float&, int&, long)"_expect);
|
|
|
|
|
|
CHECK (showType<SigP>() == "double (int&, long)"_expect);
|
2023-11-22 22:11:59 +01:00
|
|
|
|
|
|
|
|
|
|
CHECK (220 == f1 (ff,ii,33));
|
|
|
|
|
|
CHECK (220 == chain(ff,ii,33));
|
|
|
|
|
|
CHECK (220 == pappl( ii,33));
|
|
|
|
|
|
|
|
|
|
|
|
// change original values to prove that references were
|
|
|
|
|
|
// passed and stored properly in the adapted functors
|
|
|
|
|
|
ii = 22;
|
|
|
|
|
|
ff = 42;
|
|
|
|
|
|
|
|
|
|
|
|
CHECK ( 97 == f1 (ff,ii,33));
|
|
|
|
|
|
CHECK ( 97 == chain(ff,ii,33));
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
|
|
|
|
|
|
// NOTE: the partial-application generates a std::bind (Binder object),
|
|
|
|
|
|
// which deliberately _decays_ arguments to values.
|
|
|
|
|
|
CHECK (143 == pappl( ii,33)); // --> uses original *value* for f, but the int-ref (88+22+33)
|
2023-11-22 22:11:59 +01:00
|
|
|
|
|
|
|
|
|
|
// can even exchange the actual function, since f1 was passed as reference
|
|
|
|
|
|
fun = [](float& f, int& i, size_t s) -> double { return f - i - s; };
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (-13 == f1 (ff,ii,33));
|
|
|
|
|
|
CHECK (-13 == chain(ff,ii,33));
|
clean-up: simplify function-closure -- enable forwarding and remove workarounds
This is a rather intricate and technical change, but allows in the end
to switch back all usages to a main implementation patch, which is now
based on `func::BindToArgument` — so this could become the final
implementation core and replace the old `PApply` template eventually...
Largely, these changes are related to allow for ''perfect forwarding''
of the functor and the argument values to be closed; these will be
copied into the ''Binder object'' created by `std::bind`.
Notably the `TupleConstructor` was changed to perfect-forward its »source«
into the specialised `ElmMapper`; this is possible since the latter
already receives a `SRC` template parameter, which can be supplied
with whatever base type the `std::forward` invocation will expose.
In the specialisation relevant here, template `PartiallyInitTuple`,
now an ''universal reference'' is stored and passed to `std::get`,
so that (depending on the input used), either a LValue or an
RValue reference is used for the extracted data elements.
After these changes, all existing usages of `applyFirst()` or `applyLast()`
can be replaced by this modernised implementation back-end, thus obsoleting
the various hard-coded workaround added during the last years.
2025-06-06 03:23:34 +02:00
|
|
|
|
|
|
|
|
|
|
CHECK (143 == pappl( ii,33)); // Note again: uses original value for the function and the float
|
2023-11-22 22:11:59 +01:00
|
|
|
|
}
|
2009-06-28 20:41:33 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Register this test class... */
|
2009-06-29 06:34:20 +02:00
|
|
|
|
LAUNCHER (FunctionComposition_test, "unit common");
|
2009-06-28 20:41:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-12-03 02:56:50 +01:00
|
|
|
|
}}} // namespace lib::meta::test
|