lumiera_/src/lib/meta/function-closure.hpp
Ichthyostega 1a2c2ededa clean-up: switch the tricky function-closure
This is one of the most problematic headers, because it is highly complex
and comprises tightly interwoven definitions (in functional programming style),
which in turn are used deep within other features.

What concerns me is that this header is very much tangled
and pushes me (as the author) to my mental limits.

And on top of this comes that this code has to deal with intricate aspects
like perfect forwarding, and proper handling of binder instances and
function argument copying (which basically should be left to `std::bind`)

Fortunately, the changes ''for this specific topic'' are transparent:
Type sequences are not used on the API for function closure and composition,
but only as an internal tool to assemble argument tuples used for either
binding or invocation of the resulting (partially closed) function.
2025-06-04 01:49:07 +02:00

942 lines
34 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
FUNCTION-CLOSURE.hpp - metaprogramming tools for closing a function over given arguments
Copyright (C)
2009, Hermann Vosseler <Ichthyostega@web.de>
  **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.
*/
/** @file function-closure.hpp
** Partial function application and building a complete function closure.
** These are features known from functional programming and are here implemented
** based on std::bind, to support especially the case when some arguments of a
** function are known at an earlier stage, and should thus be »baked in«, while
** further arguments will be supplied later, for the actual invocation. This
** implies to _close_ (and thus bind) some arguments immediately, while keeping
** other arguments open to — so the result of this operation is again a function,
** albeit with fewer arguments.
** Additionally, we allow for _composing_ (chaining) of two functions.
** @warning this header is in a state of transition as of 6/2025, because functionality
** of this kind will certainly be needed in future, but with full support for lambdas,
** move-only types and perfect forwarding. The current implementation of std::bind
** is already quite optimal regarding this support. A gradual rework has been started,
** and will lead to a complete rewrite of the core functionality eventually, making
** better use of variadic templates and library functions like std::apply, which
** were not available at the time of the first implementation.
**
** @todo the implementation is able to handle partial application with N arguments,
** but currently we need just one argument, thus only this case was wrapped
** up into a convenient functions func::applyFirst and func::applyLast
** @todo 11/23 these functor-utils were written at a time when support for handling
** generic functions in C++ was woefully inadequate; at that time, we neither
** had Lambda-support in the language, nor the ability to use variadic arguments.
** Providing a one-shot function-style interface for this kind of manipulations
** is still considered beneficial, and thus we should gradually modernise
** the tools we want to retain...
** @todo 2/25 note that there is a bind_front in C++20 and C++23 will provide a bind_back
** helper, which would provide the implementation fully in accordance with current
** expectations (including move, constexpr); if we choose to retain a generic
** function-style front-end, it should be aligned with these standard facilities.
** We might want to retain a simple generic interface especially for binding some
** selected argument, which handles the intricacies of storing the functor.
** @todo 6/25 note however that the full-fledged partial function application is a
** **relevant core feature** and is used in the NodeBuilder (see tuple-closure.hpp).
**
** @see control::CommandDef usage example
** @see function-closure-test.hpp
** @see function-composition-test.hpp
** @see function.hpp
**
*/
#ifndef LIB_META_FUNCTION_CLOSURE_H
#define LIB_META_FUNCTION_CLOSURE_H
#include "lib/meta/function.hpp"
#include "lib/meta/tuple-helper.hpp"
#include "lib/util.hpp"
#include <functional>
#include <utility>
#include <tuple>
namespace lib {
namespace meta{
namespace func{
using std::tuple;
using std::function;
using std::forward;
using std::move;
namespace { // helpers for binding and applying a function to an argument tuple
using std::get;
/**
* this Helper with repetitive specialisations for up to nine arguments
* is used either to apply a function to arguments given as a tuple, or
* to create the actual closure (functor) over all function arguments.
* @todo 2/2025 should be replaced by a single variadic template, and
* implemented using std::apply. Note also that std::bind would
* support perfect-forwarding, especially also for the functor;
* the latter would allow to use move-only functors.
*/
template<uint n>
struct Apply;
template<> //__________________________________
struct Apply<0> ///< Apply function without Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP&)
{
return f ();
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP&)
{
return std::bind (f);
}
};
template<> //_________________________________
struct Apply<1> ///< Apply function with 1 Argument
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f (get<0>(arg));
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg));
}
};
template<> //_________________________________
struct Apply<2> ///< Apply function with 2 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f ( get<0>(arg)
, get<1>(arg)
);
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
);
}
};
template<> //_________________________________
struct Apply<3> ///< Apply function with 3 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
);
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
);
}
};
template<> //_________________________________
struct Apply<4> ///< Apply function with 4 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
);
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
);
}
};
template<> //_________________________________
struct Apply<5> ///< Apply function with 5 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
);
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
);
}
};
template<> //_________________________________
struct Apply<6> ///< Apply function with 6 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
);
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
);
}
};
template<> //_________________________________
struct Apply<7> ///< Apply function with 7 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
);
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
);
}
};
template<> //_________________________________
struct Apply<8> ///< Apply function with 8 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
);
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
);
}
};
template<> //_________________________________
struct Apply<9> ///< Apply function with 9 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
invoke (FUN& f, TUP & arg)
{
return f ( get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
, get<8>(arg)
);
}
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
, get<8>(arg)
);
}
};
/* ===== Helpers for partial function application ===== */
/** @note relying on the implementation type
* since we need to _build_ placeholders */
using std::_Placeholder;
/**
* Build a list of standard function argument placeholder types.
* For each of the elements of the provided reference list,
* a Placeholder is added, numbers counting up starting with 1 (!)
*/
template<typename TYPES, size_t i=1>
struct PlaceholderTuple
: PlaceholderTuple<typename TYPES::List>
{ };
template<typename X, typename TAIL, size_t i>
struct PlaceholderTuple<Node<X,TAIL>, i>
{
using TailPlaceholders = typename PlaceholderTuple<TAIL,i+1>::List;
using List = Node<_Placeholder<i>, TailPlaceholders>;
};
template<size_t i>
struct PlaceholderTuple<Nil, i>
{
using List = Nil;
};
using std::tuple_element;
using std::tuple_size;
using std::get;
/**
* 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 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 \a SRC, at which the sequence of init-arguments starts;
* all other positions will just be default initialised
* @see lib::meta::TupleConstructor
*/
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>();
}
};
};
} // (END) impl-namespace
/* ======= core operations: closures and partial application ========= */
/**
* Closure-creating template.
* The instance is linked (reference) to a given concrete argument tuple.
* A functor with a matching signature may then either be _closed_ over
* this argument values, or even be invoked right away with theses arguments.
* @warning we take functor objects _and parameters_ by reference
*/
template<typename SIG>
class TupleApplicator
{
using Args = typename _Fun<SIG>::Args;
using Ret = typename _Fun<SIG>::Ret;
using BoundFunc = function<Ret()>;
enum { ARG_CNT = count<typename Args::List>() };
/** storing a ref to the parameter tuple */
Tuple<Args>& params_;
public:
TupleApplicator (Tuple<Args>& args)
: params_(args)
{ }
BoundFunc bind (SIG& f) { return Apply<ARG_CNT>::template bind<BoundFunc> (f, params_); }
BoundFunc bind (function<SIG> const& f) { return Apply<ARG_CNT>::template bind<BoundFunc> (f, params_); }
Ret operator() (SIG& f) { return Apply<ARG_CNT>::template invoke<Ret> (f, params_); }
Ret operator() (function<SIG>& f) { return Apply<ARG_CNT>::template invoke<Ret> (f, params_); }
};
/**
* Closing a function over its arguments.
* This is a small usage example or spin-off,
* having almost the same effect than invoking `std::bind()`.
* The notable difference is that the function arguments for
* creating the closure are passed in as one tuple compound.
*/
template<typename SIG>
class FunctionClosure
{
typedef typename _Fun<SIG>::Args Args;
typedef typename _Fun<SIG>::Ret Ret;
function<Ret(void)> closure_;
public:
FunctionClosure (SIG& f, Tuple<Args>& arg)
: closure_(TupleApplicator<SIG>(arg).bind(f))
{ }
FunctionClosure (function<SIG> const& f, Tuple<Args>& arg)
: closure_(TupleApplicator<SIG>(arg).bind(f))
{ }
Ret operator() () { return closure_(); }
typedef Ret result_type; ///< for STL use
typedef void argument_type;
};
/**
* Partial function application
* Takes a function and a value tuple,
* using the latter to close function arguments
* either from the front (left) or aligned to the end
* of the function argument list. Result is a "reduced" function,
* expecting only the remaining "un-closed" arguments at invocation.
* @tparam SIG signature of the function to be closed, either as
* function reference type or `std::function` object
* @tparam VAL type sequence describing the tuple of values
* used for closing arguments
* @note the construction of this helper template does not verify or
* match types to the signature. In case of mismatch, you'll get
* a compilation failure from `std::bind` (which can be confusing)
* @todo 11/2023 started with modernising these functor utils.
* The most relevant bindFirst() / bindLast() operations do no longer
* rely on the PApply template. There is however the more general case
* of _binding multiple arguments,_ which is still used at a few places.
* Possibly PApply should be rewritten from scratch, using modern tooling.
*/
template<typename SIG, typename VAL>
class PApply
{
using Args = typename _Fun<SIG>::Args;
using Ret = typename _Fun<SIG>::Ret;
using ArgsList = typename Args::List;
using ValList = typename VAL::List;
using ValTypes = typename TySeq<ValList>::Seq; // reconstruct a type-seq from a type-list
enum { ARG_CNT = count<ArgsList>()
, VAL_CNT = count<ValList>()
, ROFFSET = (VAL_CNT < ARG_CNT)? ARG_CNT-VAL_CNT : 0
};
// create list of the *remaining* arguments, after applying the ValList
using LeftReduced = typename Splice<ArgsList, ValList>::Back;
using RightReduced = typename Splice<ArgsList, ValList, ROFFSET>::Front;
using ArgsL = typename TySeq<LeftReduced>::Seq;
using ArgsR = typename TySeq<RightReduced>::Seq;
// build a list, where each of the *remaining* arguments is replaced by a placeholder marker
using TrailingPlaceholders = typename func::PlaceholderTuple<LeftReduced>::List;
using LeadingPlaceholders = typename func::PlaceholderTuple<RightReduced>::List;
// ... and splice these placeholders on top of the original argument type list,
// thus retaining the types to be closed, but setting a placeholder for each remaining argument
using LeftReplaced = typename Splice<ArgsList, TrailingPlaceholders, VAL_CNT>::List;
using RightReplaced = typename Splice<ArgsList, LeadingPlaceholders, 0 >::List;
using LeftReplacedTypes = typename TySeq<LeftReplaced>::Seq;
using RightReplacedTypes = typename TySeq<RightReplaced>::Seq;
// create a "builder" helper, which accepts exactly the value tuple elements
// 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
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 = 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 = Tuple<RightReplacedTypes>;
public:
using LeftReducedFunc = function<typename BuildFunType<Ret,ArgsL>::Sig>;
using RightReducedFunc = function<typename BuildFunType<Ret,ArgsR>::Sig>;
/** do a partial function application, closing the first arguments<br/>
* `f(a,b,c)->res + (a,b)` yields `f(c)->res`
*
* @param f function, function pointer or functor
* @param arg value tuple, used to close function arguments starting from left
* @return new function object, holding copies of the values and using them at the
* closed arguments; on invocation, only the remaining arguments need to be supplied.
* @note BuildL, i.e. the TupleApplicator _must take its arguments by-value._ Any attempt
* towards »perfect-forwarding« would be potentially fragile and not worth the effort,
* since the optimiser sees the operation as a whole.
* @todo 2/2025 However, the LeftReplacedArgs _could_ then possibly moved into the bind function,
* as could the functor, once we replace the Apply-template by STDLIB features.
*/
static LeftReducedFunc
bindFront (SIG const& f, Tuple<ValTypes> arg)
{
LeftReplacedArgs params {BuildL(std::move(arg))};
return func::Apply<ARG_CNT>::template bind<LeftReducedFunc> (f, params);
}
/** do a partial function application, closing the last arguments<br/>
* `f(a,b,c)->res + (b,c)` yields `f(a)->res`
*
* @param f function, function pointer or functor
* @param arg value tuple, used to close function arguments, aligned to the right end.
* @return new function object, holding copies of the values and using them at the
* closed arguments; on invocation, only the remaining arguments need to be supplied.
*/
static RightReducedFunc
bindBack (SIG const& f, Tuple<ValTypes> arg)
{
RightReplacedArgs params {BuildR(std::move(arg))};
return func::Apply<ARG_CNT>::template bind<RightReducedFunc> (f, params);
}
};
/**
* Bind a specific argument to an arbitrary value.
* Especially, this "value" might be another binder.
*/
template<typename SIG, typename X, uint pos>
class BindToArgument
{
using Args = typename _Fun<SIG>::Args;
using Ret = typename _Fun<SIG>::Ret;
using ArgsList = typename Args::List;
using ValList = typename TySeq<X>::List;
enum { ARG_CNT = count<ArgsList>() };
using RemainingFront = typename Splice<ArgsList, ValList, pos>::Front;
using RemainingBack = typename Splice<ArgsList, ValList, pos>::Back;
using PlaceholdersBefore = typename func::PlaceholderTuple<RemainingFront>::List;
using PlaceholdersBehind = typename func::PlaceholderTuple<RemainingBack,pos+1>::List;
using PreparedArgs = typename Append< typename Append< PlaceholdersBefore
, ValList >::List
, PlaceholdersBehind
>::List;
using ReducedArgs = typename Append<RemainingFront, RemainingBack>::List;
using PreparedArgTypes = typename TySeq<PreparedArgs>::Seq;
using RemainingArgs = typename TySeq<ReducedArgs>::Seq;
using ReducedSig = typename BuildFunType<Ret,RemainingArgs>::Sig;
template<class SRC, class TAR, size_t i>
using IdxSelector = typename PartiallyInitTuple<SRC, TAR, pos>::template IndexMapper<i>;
using BuildPreparedArgs = TupleConstructor<PreparedArgTypes, IdxSelector>;
public:
using ReducedFunc = function<ReducedSig>;
static ReducedFunc
reduced (SIG& f, X val)
{
Tuple<PreparedArgTypes> params {BuildPreparedArgs{std::make_tuple (val)}};
return func::Apply<ARG_CNT>::template bind<ReducedFunc> (f, params);
}
};
namespace { // ...helpers for specifying types in function declarations....
using std::get;
using util::unConst;
template<typename RET, typename ARG>
struct _Sig
{
using Type = typename BuildFunType<RET, ARG>::Sig;
using Applicator = TupleApplicator<Type>;
};
template<typename SIG, typename ARG>
struct _Clo
{
using Ret = typename _Fun<SIG>::Ret;
using Signature = typename _Sig<Ret,ARG>::Type;
using Type = FunctionClosure<Signature>;
};
template<typename FUN1, typename FUN2>
struct _Chain
{
using Ret = typename _Fun<FUN2>::Ret;
using Args = typename _Fun<FUN1>::Args;
using FunType = typename BuildFunType<Ret,Args>::Fun;
static auto adaptedFunType() { return FunType{}; }
template<typename F1, typename F2
,typename RET, typename... ARGS>
static auto
composedFunctions (F1&& f1, F2&& f2, _Fun<RET(ARGS...)>)
{
tuple<F1,F2> binding{forward<F1> (f1)
,forward<F2> (f2)
};
return [binding = move(binding)]
(ARGS ...args) -> RET
{
auto& functor1 = get<0>(binding);
auto& functor2 = get<1>(binding);
//
return functor2 (functor1 (forward<ARGS> (args)...));
};
}
};
template<typename FUN>
struct _PapS
{
using Ret = typename _Fun<FUN>::Ret;
using Args = typename _Fun<FUN>::Args;
using Arg = typename Split<Args>::Head;
using Rest = typename Split<Args>::Tail;
using FunType = typename BuildFunType<Ret,Rest>::Fun;
static auto adaptedFunType() { return FunType{}; }
template<typename F, typename A
,typename RET, typename... ARGS>
static auto
bindFrontArg (F&& fun, A&& arg, _Fun<RET(ARGS...)>)
{
tuple<F,A> binding{forward<F> (fun)
,forward<A> (arg)
};
return [binding = move(binding)]
(ARGS ...args) -> RET
{
auto& functor = get<0>(binding);
// //Warning: might corrupt ownership
return functor ( forward<A> (unConst (get<1>(binding)))
, forward<ARGS> (args)...);
};
}
};
template<typename FUN>
struct _PapE
{
using Ret = typename _Fun<FUN>::Ret;
using Args = typename _Fun<FUN>::Args;
using Arg = typename Split<Args>::End;
using Rest = typename Split<Args>::Prefix;
using FunType = typename BuildFunType<Ret,Rest>::Fun;
static auto adaptedFunType() { return FunType{}; }
template<typename F, typename A
,typename RET, typename... ARGS>
static auto
bindBackArg (F&& fun, A&& arg, _Fun<RET(ARGS...)>)
{
tuple<F,A> binding{forward<F> (fun)
,forward<A> (arg)
};
return [binding = move(binding)]
(ARGS ...args) -> RET
{
auto& functor = get<0>(binding);
//
return functor ( forward<ARGS> (args)...
, forward<A> (unConst (get<1>(binding))));
};
}
};
} // (End) argument type shortcuts
/* ========== function-style interface ============= */
/** build a TupleApplicator, which embodies the given
* argument tuple and can be used to apply them
* to various functions repeatedly.
*/
template<typename...ARG>
inline
typename _Sig<void, TySeq<ARG...>>::Applicator
tupleApplicator (std::tuple<ARG...>& args)
{
using Signature = typename _Sig<void,TySeq<ARG...>>::Type;
return TupleApplicator<Signature>{args};
}
/** apply the given function to the argument tuple
* @deprecated 11/23 meanwhile provided by the standard lib! */
template<typename SIG, typename...ARG>
inline
typename _Fun<SIG>::Ret
apply (SIG& f, std::tuple<ARG...>& args)
{
using Ret = typename _Fun<SIG>::Ret; //
using Signature = typename _Sig<Ret,TySeq<ARG...>>::Type; // Note: deliberately re-building the Signature Type
return TupleApplicator<Signature>{args} (f); // in order to get better error messages here
}
/** close the given function over all arguments,
* using the values from the argument tuple.
* @return a closure object, which can be
* invoked later to yield the
* function result. */
template<typename SIG, typename...ARG>
inline
typename _Clo<SIG,TySeq<ARG...>>::Type
closure (SIG& f, std::tuple<ARG...>& args)
{
using Closure = typename _Clo<SIG,TySeq<ARG...>>::Type;
return Closure (f,args);
}
/** close the given function over the first argument.
* @warning never tie an ownership-managing object by-value! */
template<typename FUN, typename ARG>
inline auto
applyFirst (FUN&& fun, ARG&& arg)
{
static_assert (_Fun<FUN>(), "expect something function-like");
return _PapS<FUN>::bindFrontArg (forward<FUN> (fun)
,forward<ARG> (arg)
,_PapS<FUN>::adaptedFunType());
}
/** close the given function over the last argument */
template<typename FUN, typename ARG>
inline auto
applyLast (FUN&& fun, ARG&& arg)
{
static_assert (_Fun<FUN>(), "expect something function-like");
return _PapE<FUN>::bindBackArg (forward<FUN> (fun)
,forward<ARG> (arg)
,_PapE<FUN>::adaptedFunType());
}
/** bind the last function argument to an arbitrary term,
* which especially might be a (nested) binder... */
template<typename SIG, typename TERM>
inline
typename _PapE<SIG>::FunType::Functor
bindLast (SIG& f, TERM&& arg)
{
enum { LAST_POS = -1 + count<typename _Fun<SIG>::Args>() };
return BindToArgument<SIG,TERM,LAST_POS>::reduced (f, std::forward<TERM> (arg));
}
/** build a functor chaining the given functions: feed the result of f1 into f2.
* @note the mathematical notation would be `chained ≔ f2∘f1`
*/
template<typename FUN1, typename FUN2>
inline auto
chained (FUN1&& f1, FUN2&& f2)
{
static_assert (_Fun<FUN1>(), "expect something function-like for function-1");
static_assert (_Fun<FUN2>(), "expect something function-like for function-2");
using Chain = _Chain<FUN1,FUN2>;
return Chain::composedFunctions (forward<FUN1> (f1)
,forward<FUN2> (f2)
,Chain::adaptedFunType());
}
}}} // namespace lib::meta::func
#endif