diff --git a/research/try.cpp b/research/try.cpp index a60df815c..85f3567df 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -78,79 +78,8 @@ namespace meta{ using Sig = Ret(ARGS...); }; - template - class can_Invoke - { - template - struct Probe - { }; - - template - static Yes_t check(Probe * ); - template - static No_t check(...); - - public: - static const bool value = (sizeof(Yes_t)==sizeof(check(0))); - }; - - - template - struct _FunT - : std::false_type - { }; - - template - struct _FunT> > - : _FunT - { }; - - /** Specialisation for a bare function signature */ - template - struct _FunT - : std::true_type - { - using Ret = RET; - using Args = Types; - using Sig = RET(ARGS...); - }; - /** Specialisation for using a function pointer */ - template - struct _FunT - : _FunT - { }; - - /** Specialisation when using a function reference */ - template - struct _FunT - : _FunT - { }; - - /** Specialisation for passing a rvalue reference */ - template - struct _FunT - : _FunT - { }; - - /** Specialisation to deal with member pointer to function */ - template - struct _FunT - : _FunT - { }; - - /** Specialisation to handle member pointer to const function; - * indirectly this specialisation also handles lambdas, - * as redirected by the main template (via `decltype`) */ - template - struct _FunT - : _FunT - { }; - }} -using lib::meta::Types; -using lib::meta::_FunT; using lib::meta::enable_if; @@ -176,9 +105,9 @@ struct FunTrait }; template -struct FunTrait> > +struct FunTrait> > { - static string doIt() { return "Yeah FUN:" + lib::meta::typeStr::Sig>(); } + static string doIt() { return "Yeah FUN:" + lib::meta::typeStr::Sig>(); } }; int @@ -190,13 +119,13 @@ main (int, char**) SHOW_TYPE (decltype(lamb1)); SHOW_TYPE (decltype(lamb2)); - SHOW_EXPR ((_FunT::value)); - SHOW_EXPR ((_FunT::value)); - SHOW_EXPR ((_FunT::value)); - SHOW_EXPR ((_FunT::value)); + SHOW_EXPR ((_Fun::value)); + SHOW_EXPR ((_Fun::value)); + SHOW_EXPR ((_Fun::value)); + SHOW_EXPR ((_Fun::value)); auto funky = function (lamb2); - SHOW_EXPR ((_FunT::value)); + SHOW_EXPR ((_Fun::value)); cout << FunTrait::doIt() <::doIt() < @@ -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 + template struct _Fun + : std::false_type + { }; + + /** Specialisation for function objects and lambdas */ + template + struct _Fun> > : _Fun { }; /** Specialisation for a bare function signature */ template struct _Fun + : std::true_type { using Ret = RET; using Args = Types; @@ -134,12 +155,6 @@ namespace meta{ - template - struct is_Functor { static const bool value = false; }; - template - struct is_Functor > { static const bool value = true; }; - - diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 3dd474a51..a0236a0f2 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -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 static No_t check(...); - public: + public: static const bool value = (sizeof(Yes_t)==sizeof(check(0))); }; + /** Trait template to detect presence of a simple function call operator + * @note this metafunction fails to detect an overloaded or templated `operator()` + */ + template + class has_FunctionOperator + { + template + struct Probe + { }; + + template + static Yes_t check(Probe * ); + template + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check(0))); + }; + + + + diff --git a/tests/library/meta/function-signature-test.cpp b/tests/library/meta/function-signature-test.cpp index 59247f672..afee8756c 100644 --- a/tests/library/meta/function-signature-test.cpp +++ b/tests/library/meta/function-signature-test.cpp @@ -213,6 +213,28 @@ namespace test { CHECK ("int (unsigned int)" == typeStr<_Fun::Sig>()); CHECK ("int (Functor&, unsigned int)" == typeStr<_Fun::Sig >()); CHECK ("int (unsigned int)" == typeStr<_Fun::Sig >()); + + + // _Fun can be used for metaprogramming with enable_if + CHECK ( _Fun::value); // yes : a Functor + CHECK ( _Fun::value); // yes : a function type + CHECK (not _Fun::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::value); // yes : detect signature of lambda + CHECK (not _Fun::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 (lambda2); + CHECK ( _Fun::value); + + CHECK ("double (int)" == showSig (lambda1)); + CHECK ("double (float)" == showSig (stdFunction)); } };