Library: improve the function-signature detector to work as guard with enable_if
This is a consequence of the experiments with generic lambdas. Up to now, lib::meta::_Fun<F> failed with a compilation error when passing the decltype of such a generic lambda. The new behaviour is to pick the empty specialisation (std::false_type) in such cases, allowing to guard explicit specialisations when no suitable functor type is passed
This commit is contained in:
parent
8bdd9e7d66
commit
3614085ff7
4 changed files with 77 additions and 88 deletions
|
|
@ -78,79 +78,8 @@ namespace meta{
|
|||
using Sig = Ret(ARGS...);
|
||||
};
|
||||
|
||||
template<typename FUN>
|
||||
class can_Invoke
|
||||
{
|
||||
template<typename FF,
|
||||
typename SEL = decltype(&FF::operator())>
|
||||
struct Probe
|
||||
{ };
|
||||
|
||||
template<class X>
|
||||
static Yes_t check(Probe<X> * );
|
||||
template<class>
|
||||
static No_t check(...);
|
||||
|
||||
public:
|
||||
static const bool value = (sizeof(Yes_t)==sizeof(check<FUN>(0)));
|
||||
};
|
||||
|
||||
|
||||
template<typename FUN, typename SEL =void>
|
||||
struct _FunT
|
||||
: std::false_type
|
||||
{ };
|
||||
|
||||
template<typename FUN>
|
||||
struct _FunT<FUN, enable_if<can_Invoke<FUN>> >
|
||||
: _FunT<decltype(&FUN::operator())>
|
||||
{ };
|
||||
|
||||
/** Specialisation for a bare function signature */
|
||||
template<typename RET, typename...ARGS>
|
||||
struct _FunT<RET(ARGS...)>
|
||||
: std::true_type
|
||||
{
|
||||
using Ret = RET;
|
||||
using Args = Types<ARGS...>;
|
||||
using Sig = RET(ARGS...);
|
||||
};
|
||||
/** Specialisation for using a function pointer */
|
||||
template<typename SIG>
|
||||
struct _FunT<SIG*>
|
||||
: _FunT<SIG>
|
||||
{ };
|
||||
|
||||
/** Specialisation when using a function reference */
|
||||
template<typename SIG>
|
||||
struct _FunT<SIG&>
|
||||
: _FunT<SIG>
|
||||
{ };
|
||||
|
||||
/** Specialisation for passing a rvalue reference */
|
||||
template<typename SIG>
|
||||
struct _FunT<SIG&&>
|
||||
: _FunT<SIG>
|
||||
{ };
|
||||
|
||||
/** Specialisation to deal with member pointer to function */
|
||||
template<class C, typename RET, typename...ARGS>
|
||||
struct _FunT<RET (C::*) (ARGS...)>
|
||||
: _FunT<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 _FunT<RET (C::*) (ARGS...) const>
|
||||
: _FunT<RET(ARGS...)>
|
||||
{ };
|
||||
|
||||
}}
|
||||
|
||||
using lib::meta::Types;
|
||||
using lib::meta::_FunT;
|
||||
using lib::meta::enable_if;
|
||||
|
||||
|
||||
|
|
@ -176,9 +105,9 @@ struct FunTrait
|
|||
};
|
||||
|
||||
template<typename FUN>
|
||||
struct FunTrait<FUN, enable_if<_FunT<FUN>> >
|
||||
struct FunTrait<FUN, enable_if<_Fun<FUN>> >
|
||||
{
|
||||
static string doIt() { return "Yeah FUN:" + lib::meta::typeStr<typename _FunT<FUN>::Sig>(); }
|
||||
static string doIt() { return "Yeah FUN:" + lib::meta::typeStr<typename _Fun<FUN>::Sig>(); }
|
||||
};
|
||||
|
||||
int
|
||||
|
|
@ -190,13 +119,13 @@ main (int, char**)
|
|||
SHOW_TYPE (decltype(lamb1));
|
||||
SHOW_TYPE (decltype(lamb2));
|
||||
|
||||
SHOW_EXPR ((_FunT<decltype(lamb1)>::value));
|
||||
SHOW_EXPR ((_FunT<decltype(lamb2)>::value));
|
||||
SHOW_EXPR ((_FunT<decltype(funny)>::value));
|
||||
SHOW_EXPR ((_FunT<decltype(&funny)>::value));
|
||||
SHOW_EXPR ((_Fun<decltype(lamb1)>::value));
|
||||
SHOW_EXPR ((_Fun<decltype(lamb2)>::value));
|
||||
SHOW_EXPR ((_Fun<decltype(funny)>::value));
|
||||
SHOW_EXPR ((_Fun<decltype(&funny)>::value));
|
||||
|
||||
auto funky = function<double(float)> (lamb2);
|
||||
SHOW_EXPR ((_FunT<decltype(funky)>::value));
|
||||
SHOW_EXPR ((_Fun<decltype(funky)>::value));
|
||||
|
||||
cout << FunTrait<decltype(lamb1)>::doIt() <<endl;
|
||||
cout << FunTrait<decltype(lamb2)>::doIt() <<endl;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@
|
|||
#define LIB_META_FUNCTION_H
|
||||
|
||||
#include "lib/meta/typelist.hpp"
|
||||
#include "lib/meta/util.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
|
@ -73,7 +74,11 @@ namespace meta{
|
|||
* 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()`.
|
||||
* 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.
|
||||
* The primary, catch-all case gets activated whenever on functor objects, 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()`
|
||||
|
|
@ -82,19 +87,35 @@ namespace meta{
|
|||
* 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.
|
||||
* @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*
|
||||
* 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.
|
||||
* @see FunctionSignature_test
|
||||
*
|
||||
* [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>
|
||||
template<typename FUN, typename SEL =void>
|
||||
struct _Fun
|
||||
: std::false_type
|
||||
{ };
|
||||
|
||||
/** Specialisation for function objects and lambdas */
|
||||
template<typename FUN>
|
||||
struct _Fun<FUN, enable_if<has_FunctionOperator<FUN>> >
|
||||
: _Fun<decltype(&FUN::operator())>
|
||||
{ };
|
||||
|
||||
/** Specialisation for a bare function signature */
|
||||
template<typename RET, typename...ARGS>
|
||||
struct _Fun<RET(ARGS...)>
|
||||
: std::true_type
|
||||
{
|
||||
using Ret = RET;
|
||||
using Args = Types<ARGS...>;
|
||||
|
|
@ -134,12 +155,6 @@ namespace meta{
|
|||
|
||||
|
||||
|
||||
template<typename FUN>
|
||||
struct is_Functor { static const bool value = false; };
|
||||
template<typename SIG>
|
||||
struct is_Functor<function<SIG> > { static const bool value = true; };
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
** Simple and lightweight helpers for metaprogramming and type detection.
|
||||
** This header is a collection of very basic type detection and metaprogramming utilities.
|
||||
** @warning indirectly, this header gets included into the majority of compilation units.
|
||||
** Avoid anything here which increases compilation times or adds much debugging info.
|
||||
** Avoid anything here which increases compilation times or adds much debugging info.
|
||||
**
|
||||
** @see MetaUtils_test
|
||||
** @see trait.hpp
|
||||
|
|
@ -173,11 +173,34 @@ namespace meta {
|
|||
template<class>
|
||||
static No_t check(...);
|
||||
|
||||
public:
|
||||
public:
|
||||
static const bool value = (sizeof(Yes_t)==sizeof(check<TY>(0)));
|
||||
};
|
||||
|
||||
|
||||
/** Trait template to detect presence of a simple function call operator
|
||||
* @note this metafunction fails to detect an overloaded or templated `operator()`
|
||||
*/
|
||||
template<typename FUN>
|
||||
class has_FunctionOperator
|
||||
{
|
||||
template<typename FF,
|
||||
typename SEL = decltype(&FF::operator())>
|
||||
struct Probe
|
||||
{ };
|
||||
|
||||
template<class X>
|
||||
static Yes_t check(Probe<X> * );
|
||||
template<class>
|
||||
static No_t check(...);
|
||||
|
||||
public:
|
||||
static const bool value = (sizeof(Yes_t)==sizeof(check<FUN>(0)));
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -213,6 +213,28 @@ namespace test {
|
|||
CHECK ("int (unsigned int)" == typeStr<_Fun<decltype(memfunP)>::Sig>());
|
||||
CHECK ("int (Functor&, unsigned int)" == typeStr<_Fun<decltype(fM)>::Sig >());
|
||||
CHECK ("int (unsigned int)" == typeStr<_Fun<decltype(fMF)>::Sig >());
|
||||
|
||||
|
||||
// _Fun<F> can be used for metaprogramming with enable_if
|
||||
CHECK ( _Fun<Func>::value); // yes : a Functor
|
||||
CHECK ( _Fun<int(long)>::value); // yes : a function type
|
||||
CHECK (not _Fun<int>::value); // no : a type without function call operator
|
||||
|
||||
auto lambda1 = [](int i) { return double(i) / (i*i); };
|
||||
auto lambda2 = [](auto i) { return double(i) / (i*i); };
|
||||
|
||||
using TLamb1 = decltype(lambda1);
|
||||
using TLamb2 = decltype(lambda2);
|
||||
|
||||
CHECK ( _Fun<TLamb1>::value); // yes : detect signature of lambda
|
||||
CHECK (not _Fun<TLamb2>::value); // no : can not detect signature of a generic lambda!
|
||||
|
||||
// but detection works, once the templated operator() has been instantiated to some fixed type
|
||||
auto stdFunction = function<double(float)> (lambda2);
|
||||
CHECK ( _Fun<decltype(stdFunction)>::value);
|
||||
|
||||
CHECK ("double (int)" == showSig (lambda1));
|
||||
CHECK ("double (float)" == showSig (stdFunction));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue