Function-Tools: new improved function signature trait including lambda support (#994)

move the reworked solution in place,
replacing the existing workarounds, partial solutions and variations
This commit is contained in:
Fischlurch 2017-03-19 02:07:18 +01:00
parent e2c4dec015
commit efad48c831
4 changed files with 63 additions and 244 deletions

View file

@ -51,6 +51,7 @@ typedef unsigned int uint;
#include <functional>
#include <string>
using lib::meta::_Fun;
using std::function;
using std::placeholders::_1;
@ -59,60 +60,6 @@ using std::string;
using std::tuple;
using std::move;
////////////////############# Investigation of implementation variants
namespace lib {
namespace meta {
template<typename FUN>
struct _FuZ
: _FuZ<decltype(&FUN::operator())>
{ };
/** Specialisation for a bare function signature */
template<typename RET, typename...ARGS>
struct _FuZ<RET(ARGS...)>
{
using Ret = RET;
using Args = Types<ARGS...>;
using Sig = RET(ARGS...);
};
/** Specialisation for using a function pointer */
template<typename SIG>
struct _FuZ<SIG*>
: _FuZ<SIG>
{ };
/** Specialisation when using a function reference */
template<typename SIG>
struct _FuZ<SIG&>
: _FuZ<SIG>
{ };
/** Specialisation for passing a rvalue reference */
template<typename SIG>
struct _FuZ<SIG&&>
: _FuZ<SIG>
{ };
/** Specialisation to deal with member pointer to function */
template<class C, typename RET, typename...ARGS>
struct _FuZ<RET (C::*) (ARGS...)>
: _FuZ<RET(ARGS...)>
{ };
/** 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 _FuZ<RET (C::*) (ARGS...) const>
: _FuZ<RET(ARGS...)>
{ };
}}//namespace lib::meta
using lib::meta::_FuZ;
////////////////############# Investigation of implementation variants
@ -158,7 +105,7 @@ template<typename F>
void
showType (F)
{
using Sig = typename _FuZ<F>::Sig;
using Sig = typename _Fun<F>::Sig;
SHOW_TYPE (F);
SHOW_TYPE (Sig);
@ -168,7 +115,7 @@ template<typename F>
void
showRef (F&)
{
using Sig = typename _FuZ<F>::Sig;
using Sig = typename _Fun<F>::Sig;
SHOW_TYPE (F);
SHOW_TYPE (Sig);
@ -178,7 +125,7 @@ template<typename F>
void
showCRef (F&)
{
using Sig = typename _FuZ<F>::Sig;
using Sig = typename _Fun<F>::Sig;
SHOW_TYPE (F);
SHOW_TYPE (Sig);
@ -188,7 +135,7 @@ template<typename F>
void
showRRef (F&&)
{
using Sig = typename _FuZ<F>::Sig;
using Sig = typename _Fun<F>::Sig;
SHOW_TYPE (F);
SHOW_TYPE (Sig);
@ -258,18 +205,18 @@ main (int, char**)
SHOW_TYPE (decltype(&Funky::operator()));
SHOW_TYPE (decltype(lambda));
SHOW_TYPE (_FuZ<int(uint)>::Sig);
SHOW_TYPE (_FuZ<Fun&>::Sig);
SHOW_TYPE (_FuZ<Fun&&>::Sig);
SHOW_TYPE (_FuZ<Fun const&>::Sig);
SHOW_TYPE (_FuZ<Funky&>::Sig);
SHOW_TYPE (_FuZ<Funky&&>::Sig);
SHOW_TYPE (_FuZ<Funky const&>::Sig);
SHOW_TYPE (_Fun<int(uint)>::Sig);
SHOW_TYPE (_Fun<Fun&>::Sig);
SHOW_TYPE (_Fun<Fun&&>::Sig);
SHOW_TYPE (_Fun<Fun const&>::Sig);
SHOW_TYPE (_Fun<Funky&>::Sig);
SHOW_TYPE (_Fun<Funky&&>::Sig);
SHOW_TYPE (_Fun<Funky const&>::Sig);
using Siggy = _FuZ<Fun>::Sig;
SHOW_TYPE (_FuZ<Siggy&>::Sig);
SHOW_TYPE (_FuZ<Siggy&&>::Sig);
SHOW_TYPE (_FuZ<Siggy const&>::Sig);
using Siggy = _Fun<Fun>::Sig;
SHOW_TYPE (_Fun<Siggy&>::Sig);
SHOW_TYPE (_Fun<Siggy&&>::Sig);
SHOW_TYPE (_Fun<Siggy const&>::Sig);
cout << "\n.gulp.\n";

View file

@ -236,7 +236,7 @@ namespace diff{
class ChangeOperation
: public AttributeBindingBase<PAR>
{
using CloArgs = typename _ClosureType<CLO>::Args;
using CloArgs = typename lib::meta::_Fun<CLO>::Args;
using ValueType = typename lib::meta::Pick<CloArgs, 0>::Type;
using ID = idi::EntryID<ValueType>;

View file

@ -284,44 +284,14 @@ namespace diff{
namespace { // Mutator-Builder decorator components...
using lib::meta::Strip;
using lib::meta::Types;
using lib::meta::_Fun;
using std::forward;
using std::move;
/**
* Type rebinding helper to pick up the actual argument type.
* Works both for functors and for lambda expressions
* @remarks Solution proposed 10/2011 by \link http://stackoverflow.com/users/224671/kennytm user "kennytm" \endlink
* in this \link http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda/7943765#7943765
* answer on stackoverflow \endlink
* @todo this should be integrated into (\ref _Fun) //////////////////////////////////////TICKET #994
*/
template<typename FUN>
struct _ClosureType
: _ClosureType<decltype(&FUN::operator())>
{ };
template<class C, class RET, typename...ARGS>
struct _ClosureType<RET (C::*)(ARGS...) const>
{
using Args = typename Types<ARGS...>::Seq;
using Ret = RET;
using Sig = RET(ARGS...);
};
template<class RET, typename...ARGS>
struct _ClosureType<RET (*)(ARGS...)>
{
using Args = typename Types<ARGS...>::Seq;
using Ret = RET;
using Sig = RET(ARGS...);
};
template<typename FUN, typename SIG>
struct has_Sig
: std::is_same<SIG, typename _ClosureType<FUN>::Sig>
: std::is_same<SIG, typename _Fun<FUN>::Sig>
{ };
/** verify the installed functors or lambdas expose the expected signature */

View file

@ -26,7 +26,7 @@
** 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.
** This is a core task of functional programming, but sadly C++ in its current shape
** is still lacking in this area. (C++0X will significantly improve this situation).
** is still lacking in this area. (C++11 significantly improved this situation).
** As an \em pragmatic fix, we define here a collection of templates, specialising
** them in a very repetitive way for up to 9 function arguments. Doing so enables
** us to capture a function, access the return type and argument types as a typelist,
@ -36,7 +36,8 @@
** If the following code makes you feel like vomiting, please look away,
** and rest assured: you aren't alone.
**
** @todo rework to use variadic templates and integrage support for lambdas //////////////////////////////////////TICKET #994
** @todo get rid of the repetitive specialisations
** and use variadic templates to represent the arguments /////////////////////////////////TICKET #994
**
**
** @see control::CommandDef usage example
@ -215,13 +216,23 @@ namespace meta{
/**
* Helper to dissect an arbitrary function signature,
* irrespective if the parameter is given as function reference,
* function pointer, member function pointer or functor object.
* The base case assumes a (language) function reference.
* @todo the base case should be a _generic functor_ where we
* pick up the signature through `decltype`, which also
* works with lambdas. See (\ref _ClosureType) //////////////////////////////////////TICKET #994
* Helper for uniform access to function signature types.
* Extract the type information contained in a function or functor type,
* so it can be manipulated by metaprogramming. The embedded typedefs
* allow to pick up the return type, the sequence of argument types
* and the bare function signature type. This template works on
* anything _function like_, irrespective if the parameter is given
* as function reference, function pointer, member function pointer,
* functor object, `std::function` or lambda.
*
* The base case assumes a functor object, i.e. anything with an `operator()`.
* 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()`
* and was proposed 10/2011 by user "[kennytm]" in this [stackoverflow].
*
* [kennytm]: http://stackoverflow.com/users/224671/kennytm
* [stackoverflow] : http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda/7943765#7943765 "answer on stackoverflow"
*/
template<typename FUN>
struct _Fun
@ -232,151 +243,42 @@ namespace meta{
template<typename RET, typename...ARGS>
struct _Fun<RET(ARGS...)>
{
using Ret = typename FunctionSignature<function<RET(ARGS...)>>::Ret ;
using Args = typename FunctionSignature<function<RET(ARGS...)>>::Args;
using Ret = RET;
using Args = Types<ARGS...>;
using Sig = RET(ARGS...);
};
/** Specialisation for using a function pointer */
template<typename SIG>
struct _Fun<SIG*>
{
typedef typename FunctionSignature<function<SIG>>::Ret Ret;
typedef typename FunctionSignature<function<SIG>>::Args Args;
typedef SIG Sig;
};
: _Fun<SIG>
{ };
/** Specialisation when using a function reference */
template<typename SIG>
struct _Fun<SIG&>
{
typedef typename FunctionSignature<function<SIG>>::Ret Ret;
typedef typename FunctionSignature<function<SIG>>::Args Args;
typedef SIG Sig;
};
/** Specialisation for passing a functor */
: _Fun<SIG>
{ };
/** Specialisation for passing a rvalue reference */
template<typename SIG>
struct _Fun<function<SIG>>
{
typedef typename FunctionSignature<function<SIG>>::Ret Ret;
typedef typename FunctionSignature<function<SIG>>::Args Args;
typedef SIG Sig;
};
struct _Fun<SIG&&>
: _Fun<SIG>
{ };
/** Specialisations for member function pointers */
/* template<typename RET, class CLASS>
struct _Fun<RET (CLASS::*) (void) >
{
typedef RET Ret;
typedef Types<CLASS* const> Args;
typedef RET Sig(CLASS* const);
};
/** Specialisation to deal with member pointer to function */
template<class C, typename RET, typename...ARGS>
struct _Fun<RET (C::*) (ARGS...)>
: _Fun<RET(ARGS...)>
{ };
template< typename RET, class CLASS
, typename A1
>
struct _Fun<RET (CLASS::*) (A1) >
{
typedef RET Ret;
typedef Types<CLASS* const, A1> Args;
};
/** 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...)>
{ };
template< typename RET, class CLASS
, typename A1
, typename A2
>
struct _Fun<RET (CLASS::*) (A1,A2) >
{
typedef RET Ret;
typedef Types<CLASS* const, A1,A2> Args;
typedef RET Sig(CLASS* const, A1,A2);
};
template< typename RET, class CLASS
, typename A1
, typename A2
, typename A3
>
struct _Fun<RET (CLASS::*) (A1,A2,A3) >
{
typedef RET Ret;
typedef Types<CLASS* const, A1,A2,A3> Args;
typedef RET Sig(CLASS* const, A1,A2,A3);
};
template< typename RET, class CLASS
, typename A1
, typename A2
, typename A3
, typename A4
>
struct _Fun<RET (CLASS::*) (A1,A2,A3,A4) >
{
typedef RET Ret;
typedef Types<CLASS* const, A1,A2,A3,A4> Args;
typedef RET Sig(CLASS* const, A1,A2,A3,A4);
};
template< typename RET, class CLASS
, typename A1
, typename A2
, typename A3
, typename A4
, typename A5
>
struct _Fun<RET (CLASS::*) (A1,A2,A3,A4,A5) >
{
typedef RET Ret;
typedef Types<CLASS* const, A1,A2,A3,A4,A5> Args;
typedef RET Sig(CLASS* const, A1,A2,A3,A4,A5);
};
template< typename RET, class CLASS
, typename A1
, typename A2
, typename A3
, typename A4
, typename A5
, typename A6
>
struct _Fun<RET (CLASS::*) (A1,A2,A3,A4,A5,A6) >
{
typedef RET Ret;
typedef Types<CLASS* const, A1,A2,A3,A4,A5,A6> Args;
typedef RET Sig(CLASS* const, A1,A2,A3,A4,A5,A6);
};
template< typename RET, class CLASS
, typename A1
, typename A2
, typename A3
, typename A4
, typename A5
, typename A6
, typename A7
>
struct _Fun<RET (CLASS::*) (A1,A2,A3,A4,A5,A6,A7) >
{
typedef RET Ret;
typedef Types<CLASS* const, A1,A2,A3,A4,A5,A6,A7> Args;
typedef RET Sig(CLASS* const, A1,A2,A3,A4,A5,A6,A7);
};
template< typename RET, class CLASS
, typename A1
, typename A2
, typename A3
, typename A4
, typename A5
, typename A6
, typename A7
, typename A8
>
struct _Fun<RET (CLASS::*) (A1,A2,A3,A4,A5,A6,A7,A8) >
{
typedef RET Ret;
typedef Types<CLASS* const, A1,A2,A3,A4,A5,A6,A7,A8> Args;
typedef RET Sig(CLASS* const, A1,A2,A3,A4,A5,A6,A7,A8);
};
*/
template<typename FUN>