2009-06-16 11:39:36 +02:00
/*
FUNCTION . hpp - metaprogramming utilities for transforming function types
2010-12-17 23:28:49 +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 )
2025-06-07 18:04:59 +02:00
2009 - 2025 , Hermann Vosseler < Ichthyostega @ web . de >
2010-12-17 23:28:49 +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-17 23:28:49 +01:00
2009-06-16 11:39:36 +02:00
*/
/** @file function.hpp
2025-06-07 18:04:59 +02:00
* * Metaprogramming tools for detecting and transforming function types .
2009-06-16 11:39:36 +02:00
* * Sometimes it is necessary to build and remould a function signature , e . g . for
* * creating a functor or a closure based on an existing function of function pointer .
2025-06-07 18:04:59 +02:00
* * Functors , especially in the form of Lambdas , can be used to parametrise a common
* * skeleton of computation logic to make it work with concrete implementation parts
* * tied directly to the actual usage . A _functor centric_ approach places the focus
* * on the _computation structure_ and often complements the more _object centric_ one ,
* * where relations and responsibilities are structured and built from building blocks
* * with fixed type relations .
2009-06-16 11:39:36 +02:00
* *
2025-06-07 18:04:59 +02:00
* * However , handling arbitrary functions as part of a common structure scheme requires
* * to cope with generally unknown argument and result types . Notably the number and
* * sequence of the expected arguments of an unknown function can be arbitrary , which
* * places the onus upon the handling and receiving context to adapt to actual result
* * types and to provide suitable invocation arguments . Typically , failure to do so
* * will produce a sway of almost impenetrable compilation failure messages , making
* * the reason of the failure difficult to spot . So to handle this challenge , ways
* * to inspect and isolate parts of the function ' s _signature_ become essential .
2016-03-25 19:58:09 +01:00
* *
2025-06-07 18:04:59 +02:00
* * The _trait template_ [ _Fun ] ( \ ref lib : : meta : : _Fun ) is the entrance point to
* * extract information regarding the function signature at compile time . Defined as
* * a series partial template specialisations , it allows to create a level playing field
* * and abstract over the wide array of different flavours of » invokable entities « :
* * this can be direct references to some function in the code , function pointers ,
* * pointers to function members of an object , generally any object with an _function call_
* * ` operator ( ) ` , and especially the relevant features of the standard library , which are
* * the generic function adapter ` std : : function ` and the _binder_ objects created when
* * _partially closing_ ( binding ) some function arguments ahead of the actual invocation .
* * And last but not least the _Lambda functions_ , which represent an abstracted , opaque
* * and anonymous function and are supported by a special syntactical construct , allowing
* * to pass _blocks of C + + code_ directly as argument to some other function or object .
2016-03-25 19:58:09 +01:00
* *
2025-06-07 18:04:59 +02:00
* * Code to control the process of compilation , by detecting some aspects of the involved
* * types and react accordingly , is a form of * * metaprogramming * * — and allows for dynamic
* * * * code generation * * right from the ongoing compilation process . The flexible and open
* * nature of function arguments often directly leads into such metaprogramming tasks .
* * While the contemporary C + + language offers the language construct of _variadics_ ,
* * such variadic template argument packs can only be dissected and processed further
* * by instantiating another template with the argument pack and then exploiting
* * _partial template specialisation_ to perform a _pattern match_ against the variadic
* * arguments . Sometimes this approach comes in natural , yet in many commonplace situations
* * if feels kind of » backwards « and leads to very tricky , hard to decipher template code .
* * The Lumiera support library thus offers special features to handle _type sequences_
* * and _type lists_ directly , allowing to process by recursive list programming patterns
* * as known from the LISP language . The ` _Fun ` trait mentioned above directly provides
* * an entrance point to this kind of in - compilation programming , as it exposes a nested
* * type definition ` _Fun < F > : : Args ` , which is an instance of the * * type sequence * * template
* * \ ref lib : : meta : : Types . This in turn exposes a nested type definition ` List ` , leading
* * to the [ Loki Type Lists ] ( \ ref typelist . hpp ) .
* *
* * Furthermore , this header defines some very widely used abbreviations for the most
* * common tasks of detecting aspects of a function . These are often used in conjunction
* * with the _constexpr if_ available since C + + 17 :
* * - _FunRet is a templated type definition to extract the return type
* * - _FunArg extracts the type of single - arg function and fails compilation else
* * - has_Arity < N > is a _meta - function_ to check for a specific number of arguments
* * - is_NullaryFun , is_UnaryFun , is_BinaryFun , is_TernaryFun ( common abbreviations )
* * - the metafunction has_Sig is often placed close to the entrance point of an
* * elaborate , template based ecosystem and helps to produce better error messages
2009-06-16 11:39:36 +02:00
* *
2009-06-17 06:55:18 +02:00
* * @ see function - closure . hpp generic function application
2009-06-16 11:39:36 +02:00
* * @ see typelist . hpp
* * @ see tuple . hpp
* *
*/
2011-12-03 02:56:50 +01:00
# ifndef LIB_META_FUNCTION_H
# define LIB_META_FUNCTION_H
2009-06-16 11:39:36 +02:00
# include "lib/meta/typelist.hpp"
2017-11-24 02:48:16 +01:00
# include "lib/meta/util.hpp"
2009-06-16 11:39:36 +02:00
2014-04-03 22:42:48 +02:00
# include <functional>
2009-06-16 11:39:36 +02:00
2011-12-03 02:56:50 +01:00
namespace lib {
namespace meta {
2009-06-16 11:39:36 +02:00
2014-04-03 22:42:48 +02:00
using std : : function ;
2009-06-17 06:55:18 +02:00
2025-06-07 18:04:59 +02:00
/**************************************************************************/ /**
* Trait template for uniform access to function signature types .
* Extracts the type information contained in a function or functor type ,
2018-03-26 07:47:59 +02:00
* so it can be manipulated by metaprogramming . This template works on
2017-03-19 02:07:18 +01:00
* anything _function like_ , irrespective if the parameter is given
* as function reference , function pointer , member function pointer ,
2018-03-26 07:47:59 +02:00
* functor object , ` std : : function ` or lambda . The embedded typedefs
2025-06-07 18:04:59 +02:00
* allow to expose
2018-03-26 07:47:59 +02:00
* - ` Ret ` : the return type
* - ` Args ` : the sequence of argument types as type sequence ` Types < ARGS . . . > `
* - ` Sig ` : the bare function signature type
* - ` Functor ` : corresponding Functor type which can be instantiated or copied .
2025-06-07 18:04:59 +02:00
* - ` ARITY ` : number of arguments
2017-03-19 02:07:18 +01:00
*
2017-11-24 02:48:16 +01:00
* This template can also be used in metaprogramming with ` enable_if ` to enable
* some definition or specialisation only if a function - like type was detected ; thus
* the base case holds no nested type definitions and inherits from std : : false_type .
2025-06-07 18:04:59 +02:00
* The primary , catch - all case gets activated whenever used on functor objects
* proper , i . e . anything with an ` operator ( ) ` .
2017-03-19 02:07:18 +01:00
* The following explicit specialisations handle the other cases , which are
* not objects , but primitive types ( function ( member ) pointers and references ) .
* @ remarks The key trick of this solution is to rely on ` decltype ` of ` operator ( ) `
2022-09-27 01:51:21 +02:00
* and was proposed 10 / 2011 by user « [ kennytm ] » in this [ stackoverflow ] .
2017-03-19 02:29:39 +01:00
* @ note for a member pointer to function , only the actual arguments in the
* function signature are reflected . But if you bind such a member
* pointer into a ` std : : function ` , an additional first parameter
* will show up to take the ` this ` pointer of the class instance .
2017-11-24 02:48:16 +01:00
* @ warning this detection scheme fails when the signature of a function call
* operator is ambiguous , which is especially the case
* - when there are several overloads of ` operator ( ) `
* - when the function call operator is templated
* - on * generic lambdas *
2025-06-07 18:04:59 +02:00
* - on results of std : : bind
2017-11-24 02:48:16 +01:00
* All these cases will activate the base ( false ) case , as if the
* tested subject was not a function at all . Generally speaking ,
* it is _not possible_ to probe a generic lambda or templated function ,
* unless you bind it beforehand into a std : : function with correct signature .
2017-03-19 02:29:39 +01:00
* @ see FunctionSignature_test
2017-03-19 02:07:18 +01:00
*
* [ kennytm ] : http : //stackoverflow.com/users/224671/kennytm
2022-09-27 01:51:21 +02:00
* [ stackoverflow ] : http : //stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda/7943765#7943765 "figure out parameter and return type of a Lambda"
2012-06-28 03:42:48 +02:00
*/
2017-11-24 02:48:16 +01:00
template < typename FUN , typename SEL = void >
2012-06-28 03:42:48 +02:00
struct _Fun
2017-11-24 02:48:16 +01:00
: std : : false_type
2018-03-24 02:19:49 +01:00
{
using Functor = FUN ;
} ;
2017-11-24 02:48:16 +01:00
/** Specialisation for function objects and lambdas */
template < typename FUN >
struct _Fun < FUN , enable_if < has_FunctionOperator < FUN > > >
Functor-Tools: reshape generic function signature trait to integrate Lambdas (#994)
As a first step towards a gradual rework of our function metaprogramming helpers,
this change prepends a generic case for all kinds of functors to our existing
solution, which up to now was entirely based on explicit specialisations.
C++11 supplied the new language construct 'decltype(EXPR)', which allows us
to capture any class with an function operator, which also includes the Lambdas.
The solution was proposed 2011 on StackOverflow
http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda/7943765#7943765
We used it already with success within our TreeMutator.
But obviously the goal should be to unite all the function trait / metaprogramming helpers,
which unfortunately is a more expensive undertaking, since it also involves
to get rid of the explicit specialisations and retrofit our Types<XXX...> helper
to rely on variadic templates rather than on loki-style typelists.
This first step here is rather conservative, since we'll still rely on our
explicit specialisations in most cases. Only the Lambdas will go through the
new, generic case, and from there invoke the specialisation for member functions.
The latter need to be rectified as well, which is subject of the next changeset...
2017-03-18 22:06:44 +01:00
: _Fun < decltype ( & FUN : : operator ( ) ) >
2018-03-24 02:19:49 +01:00
{
using Functor = FUN ;
} ;
Functor-Tools: reshape generic function signature trait to integrate Lambdas (#994)
As a first step towards a gradual rework of our function metaprogramming helpers,
this change prepends a generic case for all kinds of functors to our existing
solution, which up to now was entirely based on explicit specialisations.
C++11 supplied the new language construct 'decltype(EXPR)', which allows us
to capture any class with an function operator, which also includes the Lambdas.
The solution was proposed 2011 on StackOverflow
http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda/7943765#7943765
We used it already with success within our TreeMutator.
But obviously the goal should be to unite all the function trait / metaprogramming helpers,
which unfortunately is a more expensive undertaking, since it also involves
to get rid of the explicit specialisations and retrofit our Types<XXX...> helper
to rely on variadic templates rather than on loki-style typelists.
This first step here is rather conservative, since we'll still rely on our
explicit specialisations in most cases. Only the Lambdas will go through the
new, generic case, and from there invoke the specialisation for member functions.
The latter need to be rectified as well, which is subject of the next changeset...
2017-03-18 22:06:44 +01:00
/** Specialisation for a bare function signature */
template < typename RET , typename . . . ARGS >
struct _Fun < RET ( ARGS . . . ) >
2017-11-24 02:48:16 +01:00
: std : : true_type
2012-06-28 03:42:48 +02:00
{
2017-03-19 02:07:18 +01:00
using Ret = RET ;
2025-06-07 18:04:59 +02:00
using Args = Types < ARGS . . . > ;
Functor-Tools: reshape generic function signature trait to integrate Lambdas (#994)
As a first step towards a gradual rework of our function metaprogramming helpers,
this change prepends a generic case for all kinds of functors to our existing
solution, which up to now was entirely based on explicit specialisations.
C++11 supplied the new language construct 'decltype(EXPR)', which allows us
to capture any class with an function operator, which also includes the Lambdas.
The solution was proposed 2011 on StackOverflow
http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda/7943765#7943765
We used it already with success within our TreeMutator.
But obviously the goal should be to unite all the function trait / metaprogramming helpers,
which unfortunately is a more expensive undertaking, since it also involves
to get rid of the explicit specialisations and retrofit our Types<XXX...> helper
to rely on variadic templates rather than on loki-style typelists.
This first step here is rather conservative, since we'll still rely on our
explicit specialisations in most cases. Only the Lambdas will go through the
new, generic case, and from there invoke the specialisation for member functions.
The latter need to be rectified as well, which is subject of the next changeset...
2017-03-18 22:06:44 +01:00
using Sig = RET ( ARGS . . . ) ;
2018-03-24 02:19:49 +01:00
using Functor = std : : function < Sig > ;
2023-10-10 19:47:39 +02:00
enum { ARITY = sizeof . . . ( ARGS ) } ;
2012-06-28 03:42:48 +02:00
} ;
2020-02-21 20:15:21 +01:00
/** Specialisation to strip `noexcept` from the signature */
template < typename RET , typename . . . ARGS >
struct _Fun < RET ( ARGS . . . ) noexcept >
: _Fun < RET ( ARGS . . . ) >
{ } ;
2012-06-28 03:42:48 +02:00
/** Specialisation for using a function pointer */
template < typename SIG >
struct _Fun < SIG * >
2017-03-19 02:07:18 +01:00
: _Fun < SIG >
{ } ;
2023-11-26 03:04:59 +01:00
/** Specialisation to strip spurious const for type analysis */
template < typename SIG >
struct _Fun < SIG const & >
: _Fun < SIG >
{ } ;
2012-09-01 17:33:42 +02:00
/** Specialisation when using a function reference */
template < typename SIG >
struct _Fun < SIG & >
2017-03-19 02:07:18 +01:00
: _Fun < SIG >
{ } ;
2012-06-28 03:42:48 +02:00
2017-03-19 02:07:18 +01:00
/** Specialisation for passing a rvalue reference */
template < typename SIG >
struct _Fun < SIG & & >
: _Fun < SIG >
{ } ;
2012-06-28 03:42:48 +02:00
2017-03-19 02:07:18 +01:00
/** Specialisation to deal with member pointer to function */
template < class C , typename RET , typename . . . ARGS >
struct _Fun < RET ( C : : * ) ( ARGS . . . ) >
: _Fun < RET ( ARGS . . . ) >
{ } ;
2012-06-28 03:42:48 +02:00
2020-02-21 20:15:21 +01:00
/** Specialisation to deal with member pointer to noexcept function */
template < class C , typename RET , typename . . . ARGS >
struct _Fun < RET ( C : : * ) ( ARGS . . . ) noexcept >
: _Fun < RET ( ARGS . . . ) >
{ } ;
2017-03-19 02:07:18 +01:00
/** Specialisation to handle member pointer to const function;
* indirectly this specialisation also handles lambdas ,
* as redirected by the main template ( via ` decltype ` ) */
template < class C , typename RET , typename . . . ARGS >
struct _Fun < RET ( C : : * ) ( ARGS . . . ) const >
: _Fun < RET ( ARGS . . . ) >
{ } ;
2012-06-28 03:42:48 +02:00
2023-10-21 23:42:31 +02:00
/** allow also to probe _plain member fields,_ which may hold a functor */
template < class C , typename FUN >
struct _Fun < FUN ( C : : * ) >
: _Fun < FUN >
{ } ;
2012-06-28 03:42:48 +02:00
2025-06-07 18:04:59 +02:00
/**
* Build function types from given Argument types .
* As embedded typedefs , you ' ll find a std : : function # Func
* and the bare function signature # Sig
* @ param RET the function return type
* @ param ARGS a type sequence describing the arguments
*/
template < typename RET , typename ARGS >
struct BuildFunType ;
template < typename RET , typename . . . ARGS >
struct BuildFunType < RET , Types < ARGS . . . > >
{
using Sig = RET ( ARGS . . . ) ;
using Fun = _Fun < Sig > ;
using Func = function < Sig > ;
using Functor = Func ;
} ;
2023-11-22 22:11:59 +01:00
/** abbreviation for referring to a function's return type */
template < typename FUN >
2025-07-05 20:08:18 +02:00
using _FunRet = _Fun < FUN > : : Ret ;
2023-11-22 22:11:59 +01:00
namespace {
template < typename FUN >
struct _DetectSingleArgFunction
{
static_assert ( _Fun < FUN > ( ) , " something funktion-like required " ) ;
static_assert ( _Fun < FUN > : : ARITY = = 1 , " function with exactly one argument required " ) ;
2025-07-05 20:08:18 +02:00
using Sig = _Fun < FUN > : : Sig ;
using Arg = _Fun < Sig > : : Args : : List : : Head ;
2023-11-22 22:11:59 +01:00
} ;
}
/** abbreviation for referring to a function's single Argument type */
template < typename FUN >
2025-07-05 20:08:18 +02:00
using _FunArg = _DetectSingleArgFunction < FUN > : : Arg ;
2023-11-22 22:11:59 +01:00
2023-12-09 02:36:14 +01:00
template < typename FUN , uint a >
using has_Arity = std : : bool_constant < _Fun < FUN > : : ARITY = = a > ;
template < typename FUN >
using is_NullaryFun = has_Arity < FUN , 0 > ;
template < typename FUN >
using is_UnaryFun = has_Arity < FUN , 1 > ;
template < typename FUN >
using is_BinaryFun = has_Arity < FUN , 2 > ;
2024-12-15 23:25:01 +01:00
template < typename FUN >
using is_TernaryFun = has_Arity < FUN , 3 > ;
2023-12-09 02:36:14 +01:00
2023-11-22 22:11:59 +01:00
2023-05-04 12:35:23 +02:00
/**
* Meta - function to check that some _function like_ entity
* offers the expected signature
* @ tparam SIG signature type ( e . g . ` float ( int , bool ) ` )
* @ tparam FUN anything _function like_ ( class with function call operator
* or std : : function instance , or λ instance or language function
* reference or function pointer
*/
2023-10-21 23:42:31 +02:00
template < typename FUN , typename SIG , bool = _Fun < FUN > ( ) >
2023-05-04 12:35:23 +02:00
struct has_Sig
: std : : is_same < SIG , typename _Fun < FUN > : : Sig >
{ } ;
2023-10-21 23:42:31 +02:00
/** catch-all to prevent compilation failure for anything not function-like. */
template < typename FUN , typename X >
struct has_Sig < FUN , X , false >
: std : : false_type
{ } ;
2023-05-04 12:35:23 +02:00
/**
* Macro for a compile - time check to verify the given
* generic functors or lambdas expose some expected signature .
* @ remark typically used when configuring a template with custom adapters .
*/
# define ASSERT_VALID_SIGNATURE(_FUN_, _SIG_) \
static_assert ( lib : : meta : : has_Sig < _FUN_ , _SIG_ > : : value , \
" Function " STRINGIFY ( _FUN_ ) " unsuitable, expected signature: " STRINGIFY ( _SIG_ ) ) ;
2023-10-21 23:42:31 +02:00
/**
* Helper to pick up a member field for verification
* @ tparam SIG signature of the _function like_ entity expected
* @ tparam FUN address - or member - pointer , e . g . ` & Class : : member `
* @ return suitably parametrised \ ref has_Sig instance ( which is bool convertible )
* @ remark intended for use with generic types , when expecting a _somehow invokable_
* member , irrespective if a static function , member function or functor object
*/
template < typename SIG , typename FUN >
constexpr inline auto
isFunMember ( FUN )
{
return has_Sig < FUN , SIG > { } ;
}
/**
* Macro for a compile - time check to verify some member is present
* and comprises something invokable with a specific signature .
* @ remark typically used with _generic types_ or bindings
*/
# define ASSERT_MEMBER_FUNCTOR(_EXPR_, _SIG_) \
static_assert ( lib : : meta : : isFunMember < _SIG_ > ( _EXPR_ ) , \
" Member " STRINGIFY ( _EXPR_ ) " unsuitable, expect function signature: " STRINGIFY ( _SIG_ ) ) ;
2023-05-04 12:35:23 +02:00
2012-06-28 03:42:48 +02:00
2023-10-11 21:06:56 +02:00
/** Placeholder marker for a special argument position to be supplied later */
template < class TAR >
struct InstancePlaceholder { } ;
namespace { // Helper to inject instance-pointer instead of placeholder...
/**
* @ internal helper to detect an InstancePlaceholder
* @ return an instance - pointer , statically casted to the marked target type .
*/
template < class W , class TAR >
constexpr inline TAR *
maybeInject ( W & instance , InstancePlaceholder < TAR > )
{
return static_cast < TAR * > ( & instance ) ;
}
/** (default case: fall-through) */
template < class W , typename X >
constexpr inline X
maybeInject ( W & , X & & x )
{
return std : : move ( x ) ;
}
} //(End)Helper for lateBindInstance.
/**
* Fix - up the arguments for a member - function invocation ,
* allowing to inject the actual ` this ` instance into an existing argument sequence .
* @ remark invocation of a member function requires to supply the _object instance_ as
* first element in the argument list ; sometimes this poses a design challenge ,
* since the actual instance may not be known at the point where the other arguments
* are prepared . As a remedy , the position of the instance pointer can be marked with
* the \ ref InstancePlaceholder , allowing to splice in the actual pointer when known .
*/
template < class W , class TUP >
constexpr inline auto
lateBindInstance ( W & instance , TUP & & invocation )
{
auto splice = [ & instance ] ( auto & & . . . xs )
{
return std : : tuple { maybeInject ( instance , std : : move ( xs ) ) . . . } ;
} ;
return std : : apply ( splice , std : : forward < TUP > ( invocation ) ) ;
}
2011-12-03 02:56:50 +01:00
} } // namespace lib::meta
2009-06-16 11:39:36 +02:00
# endif