Is an is_functor C++ trait class possible? - c++

How can I deduce statically if an argument is a C++ function object (functor)?
template <typename F>
void test(F f) {}
I tried is_function<F>::value, but this doesn't work. It also seems there is no is_functor trait, so perhaps it's not possible. I appear to be only looking for a specific member function, in this case the function call operator: F::operator().

It is possible to create such a trait, with two restrictions:
For the compiler, a free function is something fundamentally different from a class functor that overloads operator(). Thus we have to treat both cases seperately when implementing. This is not a problem for usage though, we can hide this implementation detail from the user.
We need to know the signature of the function you want to call. This is usually not a problem, and it does have the nice side effect that our trait is able to handle overloads pretty natively.
Step one: Free functions
Let's start with free functions, because they are little easier to detect. Our task is, when given a function pointer, to determine whether the signature of that function pointer matches the signature passed as the second template argument. To be able to compare those, we either need to get a grasp of the underlying function signature, or create a function pointer of our signature. I arbitrarily chose the latter:
// build R (*)(Args...) from R (Args...)
// compile error if signature is not a valid function signature
template <typename, typename>
struct build_free_function;
template <typename F, typename R, typename ... Args>
struct build_free_function<F, R (Args...)>
{ using type = R (*)(Args...); };
Now all that's left to do is to compare and we are done with the free function part:
// determine whether a free function pointer F has signature S
template <typename F, typename S>
struct is_function_with_signature
{
// check whether F and the function pointer of S are of the same
// type
static bool constexpr value = std::is_same<
F, typename build_free_function<F, S>::type
>::value;
};
Step two: Class functors
This one is a little more involved. We could easily detect with SFINAE whether a class defines an operator():
template <typename T>
struct defines_functor_operator
{
typedef char (& yes)[1];
typedef char (& no)[2];
// we need a template here to enable SFINAE
template <typename U>
static yes deduce(char (*)[sizeof(&U::operator())]);
// fallback
template <typename> static no deduce(...);
static bool constexpr value = sizeof(deduce<T>(0)) == sizeof(yes);
};
but that does not tell us whether one exists for our desired function signature! Luckily, we can use a trick here: pointers are valid template parameters. Thus we can first use the member function pointer of our desired signature, and check whether &T::operator() is of that type:
template <typename T, T> struct check;
Now check<void (C::*)() const, &C::operator()> will only be a valid template instantiation if C does indeed have a void C::operator()() const. But to do this we first have to combine C and the signature to a member function pointer. As we already have seen, we need to worry about two extra cases we did not have to care about for free functions: const and volatile functions. Besides that it's pretty much the same:
// build R (C::*)(Args...) from R (Args...)
// R (C::*)(Args...) const from R (Args...) const
// R (C::*)(Args...) volatile from R (Args...) volatile
// compile error if signature is not a valid member function signature
template <typename, typename>
struct build_class_function;
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...)>
{ using type = R (C::*)(Args...); };
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) const>
{ using type = R (C::*)(Args...) const; };
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) volatile>
{ using type = R (C::*)(Args...) volatile; };
Putting that and our findings concerning the check helper struct together, we get our check metafunction for functor objects:
// determine whether a class C has an operator() with signature S
template <typename C, typename S>
struct is_functor_with_signature
{
typedef char (& yes)[1];
typedef char (& no)[2];
// helper struct to determine that C::operator() does indeed have
// the desired signature; &C::operator() is only of type
// R (C::*)(Args...) if this is true
template <typename T, T> struct check;
// T is needed to enable SFINAE
template <typename T> static yes deduce(check<
typename build_class_function<C, S>::type, &T::operator()> *);
// fallback if check helper could not be built
template <typename> static no deduce(...);
static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes);
};
Step three: Putting the pieces together
We are almost done. Now we only need to decide when to use our free function, and when the class functor metafunctions. Luckily, C++11 provides us with a std::is_class trait that we can use for this. So all we have to do is specialize on a boolean parameter:
// C is a class, delegate to is_functor_with_signature
template <typename C, typename S, bool>
struct is_callable_impl
: std::integral_constant<
bool, is_functor_with_signature<C, S>::value
>
{};
// F is not a class, delegate to is_function_with_signature
template <typename F, typename S>
struct is_callable_impl<F, S, false>
: std::integral_constant<
bool, is_function_with_signature<F, S>::value
>
{};
So we can finally add the last piece of the puzzle, being our actual is_callable trait:
// Determine whether type Callable is callable with signature Signature.
// Compliant with functors, i.e. classes that declare operator(); and free
// function pointers: R (*)(Args...), but not R (Args...)!
template <typename Callable, typename Signature>
struct is_callable
: is_callable_impl<
Callable, Signature,
std::is_class<Callable>::value
>
{};
Now we clean up our code, put implementation details into anonymous namespaces so they are not acessible outside of our file, and have a nice is_callable.hpp to use in our project.
Full code
namespace // implementation detail
{
// build R (*)(Args...) from R (Args...)
// compile error if signature is not a valid function signature
template <typename, typename>
struct build_free_function;
template <typename F, typename R, typename ... Args>
struct build_free_function<F, R (Args...)>
{ using type = R (*)(Args...); };
// build R (C::*)(Args...) from R (Args...)
// R (C::*)(Args...) const from R (Args...) const
// R (C::*)(Args...) volatile from R (Args...) volatile
// compile error if signature is not a valid member function signature
template <typename, typename>
struct build_class_function;
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...)>
{ using type = R (C::*)(Args...); };
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) const>
{ using type = R (C::*)(Args...) const; };
template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) volatile>
{ using type = R (C::*)(Args...) volatile; };
// determine whether a class C has an operator() with signature S
template <typename C, typename S>
struct is_functor_with_signature
{
typedef char (& yes)[1];
typedef char (& no)[2];
// helper struct to determine that C::operator() does indeed have
// the desired signature; &C::operator() is only of type
// R (C::*)(Args...) if this is true
template <typename T, T> struct check;
// T is needed to enable SFINAE
template <typename T> static yes deduce(check<
typename build_class_function<C, S>::type, &T::operator()> *);
// fallback if check helper could not be built
template <typename> static no deduce(...);
static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes);
};
// determine whether a free function pointer F has signature S
template <typename F, typename S>
struct is_function_with_signature
{
// check whether F and the function pointer of S are of the same
// type
static bool constexpr value = std::is_same<
F, typename build_free_function<F, S>::type
>::value;
};
// C is a class, delegate to is_functor_with_signature
template <typename C, typename S, bool>
struct is_callable_impl
: std::integral_constant<
bool, is_functor_with_signature<C, S>::value
>
{};
// F is not a class, delegate to is_function_with_signature
template <typename F, typename S>
struct is_callable_impl<F, S, false>
: std::integral_constant<
bool, is_function_with_signature<F, S>::value
>
{};
}
// Determine whether type Callable is callable with signature Signature.
// Compliant with functors, i.e. classes that declare operator(); and free
// function pointers: R (*)(Args...), but not R (Args...)!
template <typename Callable, typename Signature>
struct is_callable
: is_callable_impl<
Callable, Signature,
std::is_class<Callable>::value
>
{};
Ideone example with some tests
http://ideone.com/7PWdiv

Although this does not work for overloaded functions, for all other cases (free functions, classes implementing operator(), and lambdas) this short solutions works in C++11:
template <typename T, typename Signature>
struct is_callable: std::is_convertible<T,std::function<Signature>> { };
Note: std::is_invocable is available since C++17.

You can use the following concept in c++20
template<typename F>
concept FunctionObject = requires (F) {
&F::operator();
};

Is an is_functor C++ trait class possible?
Yes, it is possible to manually implement a validation for functors.
I tried is_function::value, but this doesn't work.
You are on the right path, it is possible to implement using std::function
Remember that std::function also accepts functions, function pointers and functor instances in its constructor.
Example:
struct Test {
public:
bool operator()(int){
return true;
}
};
void test(int){
}
void example(std::function<void(int)> ex){
cout << "Called" << endl;
};
int main()
{
example(test);
example(&test);
example(Test{});
}
That said, the logic that will be used to validate if a class has a function call overload operator (functor) is similar to the code above.
That is, if the std::function<void(int)> accepts an instance of the class Test{} means the class has a functor, otherwise it doesn't.
Example of an possible solution
Here is the source code:
//F: Test class
//Args...: The arguments, ex: int or none
template <typename F, typename... Args>
struct is_functor :
is_constructible <
function<void(Args ...)>, F
>
{};
Example usage:
is_functor<Test, int> -> True result
is_functor -> False result
Info about std::is_constructible
Is a trait class that identifies whether T is a constructible type with the set of argument types specified by Arg.
For this class, a constructible type is a type that can be constructed using a particular set of arguments.
is_constructible inherits from integral_constant as being either true_type or false_type, depending on whether T is constructible with the list of arguments Args.
In short, it checks if a given class has a constructor, for example:
struct Test2 {
Test(bool, int){}
};
std::is_constructible<Test2, bool, int> -> True result
std::is_constructible<Test2, float> -> False result
Example of implementation:
template <typename, typename, typename ...Args>
struct is_constructible_impl : false_type {};
template <typename T, typename ...Args>
struct is_constructible_impl <
void_t<decltype(T(std::declval<Args>()...))>, T, Args...
> : true_type {};
template <typename T, typename ...Args>
struct is_constructible : is_constructible_impl<void_t<>, T, Args...> {};
Final explanation
In the implementation of is_functor it was checked if std::function<void(int)> accepts an instance of Test{}, which is true.
References:
How is std::is_constructible<T, Args> implemented?
Can std::is_invocable be emulated within C++11?
https://replit.com/#LUCASP6/RowdyAlphanumericCode#main.cpp

template<typename T, typename Sign>
struct is_functor
{
typedef char yes[1];
typedef char no [2];
template <typename U, U> struct type_check;
template <typename _1> static yes &chk(type_check<Sign, &_1::operator()>*);
template <typename > static no &chk(...);
static bool const value = sizeof(chk<T>(nullptr)) == sizeof(yes);
};
Altered from this answer.
It could be used like...
template<typename T>
typename std::enable_if<is_functor<T, void(T::*)()>::value>::type func()
{
}

Related

How to extract lambda's Return Type and Variadic Parameters Pack back from general template<typename T>

I want to create a templated class or function, that receives a lambda, and puts it internally in std::function<>
Lambda could have any number of input parameters [](int a, float b, ...) std::function<> should correspond to the lambda's operator()'s type
template <typename T>
void getLambda(T t) {
// typedef lambda_traits::ret_type RetType; ??
// typedef lambda_traits::param_tuple --> somehow back to parameter pack Args...
std::function<RetType(Args...)> fun(t);
}
int main() {
int x = 0;
getLambda([&x](int a, float b, Person c){});
}
So I need to somehow extract the Return Type and Parameter Pack
Answer here suggests to use partial spec on lambda's :: operator()
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
enum { arity = sizeof...(Args) };
// arity is the number of arguments.
typedef ReturnType result_type;
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
// the i-th argument is equivalent to the i-th tuple element of a tuple
// composed of those arguments.
};
};
But I need a way to convert tuple<> back to parameters pack, to create a proper std::function<> instantiation
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
using result_type = ReturnType;
using arg_tuple = std::tuple<Args...>;
static constexpr auto arity = sizeof...(Args);
};
template <class F, std::size_t ... Is, class T>
auto lambda_to_func_impl(F f, std::index_sequence<Is...>, T) {
return std::function<typename T::result_type(std::tuple_element_t<Is, typename T::arg_tuple>...)>(f);
}
template <class F>
auto lambda_to_func(F f) {
using traits = function_traits<F>;
return lambda_to_func_impl(f, std::make_index_sequence<traits::arity>{}, traits{});
}
The code above should do what you want. The main idea, as you can see, is to create an integer pack. This is the non-type template equivalent of variadics. I don't know of any technique by which you can use such a pack without calling another function, so typically in these situations with tuples you'll see a nested "impl" function that does all the work. Once you have the integer pack, you expand it while accessing the tuple (works for getting the values too).
On a stylistic note: use using, not typename, especially in template heavy code as the former can alias templates too. And don't use that enum trick to store a static value without it using space; compilers will optimize this out anyhow and just using a static constexpr integer is much clearer.

Capture function argument type from actual arguments

Is it possible to capture the type of a formal argument, having only the function name and the actual arguments?
I would need something similar to decltype, but it should return the function type instead of the return type of the function.
I found a half solution with C++11. If the function is not overloaded, you can use decltype to get the signature, specifying only the function name and template arguments (if any), but not the actual arguments:
template<class F>
struct my_function_traits;
// function pointer
template<class R, class Arg>
struct my_function_traits<R(*)(Arg)> : public my_function_traits<R(Arg)>
{
typedef Arg type;
};
template<class R, class Arg1>
struct my_function_traits<R(Arg1)>
{
typedef Arg1 type;
};
namespace itk
{
template <typename PixelType, unsigned Dimension>
class Image;
}
template <typename TPixel, unsigned VDimension>
void func2(itk::Image<TPixel, VDimension>* image) {}
int main()
{
typedef my_function_traits< decltype(func2<int, 2>) > Traits;
Traits::type var = 0;
return 0;
}
This works well, but if I add this other function, decltype will not able to resolve the type:
template <typename TPixel, unsigned VDimension>
void func2(const itk::Image<TPixel, VDimension>* image, int i) {}
What makes sense, because the function name is ambiguous and both versions have the same template arguments. But the compiler must be able to figure out the correct function from the static type of the actual arguments. The problem is that as soon as I specify them, decltype returns the type of the expression, i.e. the return type of the function.
itk::Image<int, 3>* image;
int a = 3;
...
typedef my_function_traits< decltype(func2<int, 2>(image, 3)) > Traits;
My main goal is to check the constness of the first argument.
Thanks for any help!
Assuming that you don't know the constness of the first pointer, and you know successive argument type, you may use the following:
template <typename T, typename ...Ts>
struct is_first_arg_const_ptr
{
template <typename Ret>
static std::false_type
is_const(Ret (&f)(T*, Ts...));
template <typename Ret>
static std::true_type
is_const(Ret (&f)(const T*, Ts...));
// And for methods
template <typename Ret, typename C>
static std::false_type
is_const (Ret (C::*) (T*, Ts...)) /* const volatile & && */;
template <typename Ret, typename C>
static std::true_type
is_const (Ret (C::*) (const T*, Ts...)) /* const volatile & && */;
// .. complete for all combinations of cv_qualifier and ref
};
And now, the overload is correctly chosen without ambiguity.
Live example.
For the macro, you may do something like:
template <typename T, typename ...Ts>
constexpr
auto make_is_first_arg_const_ptr(T&& t, Ts&&...)
-> is_first_arg_const_ptr<
typename std::decay<decltype(*t)>::type,
typename std::decay<Ts>::type...
>;
#define IS_FIRST_ARG_CONST_PTR(Name, ...) \
(decltype(make_is_first_arg_const_ptr(__VA_ARGS__).is_const(Name))::value)
And then use it as
static_assert(!IS_FIRST_ARG_CONST_PTR(func2, image), "");
static_assert(IS_FIRST_ARG_CONST_PTR(func2, image, a), "");
static_assert(IS_FIRST_ARG_CONST_PTR(&MyClass::func2, image, a), "");
Note that the macro will not work for some prototype of func as I deduce type from argument but you will get a compile error in this case (you may change std::decay by a more appropriate traits to match your need).
Boost does this. Have a look at boost's function_traits:
http://www.boost.org/doc/libs/1_55_0/boost/type_traits/function_traits.hpp
http://www.boost.org/doc/libs/1_55_0/libs/type_traits/doc/html/boost_typetraits/reference/function_traits.html

C++ compile-time predicate to test if a callable object of type F can be called with an argument of type T

I would like to create a compile-type function that, given any callable object f (function, lambda expression, function object, ...) and a type T, evaluates to true, if f can be called with an argument of type T, and false if it cannot.
Example:
void f1(int) { ... }
void f2(const std::string&) { ... }
assert( is_callable_with<int>(f1));
assert(!is_callable_with<int>(f2));
I'm thinking that a clever use of the SFINAE rule could achieve this. Possibly somehow like this:
template<typename T, typename F>
constexpr bool is_callable_with(F&&, typename std::result_of<F(T)>::type* = nullptr) {
return true;
}
template<typename T, typename F>
constexpr bool is_callable_with(F&&) {
return false;
}
But this doesn't work, because if F is callable with T, both overloads participate in the overload resolution and there is an ambiguity. I'd like to rewrite it so in the positive case, the first overload would be picked by the overload resolution over the second one. Not sure if I'm even on the right track here though.
A variant of Paul's answer, but following the standard SFINAE test pattern. Again a generic trait with arbitrary parameter types A...:
struct can_call_test
{
template<typename F, typename... A>
static decltype(std::declval<F>()(std::declval<A>()...), std::true_type())
f(int);
template<typename F, typename... A>
static std::false_type
f(...);
};
template<typename F, typename... A>
using can_call = decltype(can_call_test::f<F, A...>(0));
Then a constexpr function as you requested:
template<typename T, typename F>
constexpr bool is_callable_with(F&&) { return can_call<F, T>{}; }
Check live example.
This will work with functions, lambda expressions, or function objects with arbitrary number of arguments, but for (pointers to) member functions you'll have to use std::result_of<F(A...)>.
UPDATE
Below, can_call has the nice "function signature" syntax of std::result_of:
template<typename F, typename... A>
struct can_call : decltype(can_call_test::f<F, A...>(0)) { };
template<typename F, typename... A>
struct can_call <F(A...)> : can_call <F, A...> { };
to be used like this
template<typename... A, typename F>
constexpr can_call<F, A...>
is_callable_with(F&&) { return can_call<F(A...)>{}; }
where I've also made is_callable_with variadic (I can't see why it should be limited to one argument) and returning the same type as can_call instead of bool (thanks Yakk).
Again, live example here.
I would make a type trait first:
template<class X = void>
struct holder
{
typedef void type;
};
template<class F, class T, class X = void>
struct is_callable_with_trait
: std::false_type
{};
template<class F, class T>
struct is_callable_with_trait<F, T, typename holder<
decltype(std::declval<F>()(std::declval<T>()))
>::type>
: std::true_type
{};
And then if you want, you can turn it into a function:
template<typename T, typename F>
constexpr bool is_callable_with(F&&)
{
return is_callable_with_trait<F&&, T>::value;
}
template<class F, class T, class = void>
struct is_callable_with_impl : std::false_type {};
template<class F, class T>
struct is_callable_with_impl<F,T,
typename std::conditional<
true,
void,
decltype( std::declval<F>() (std::declval<T>()) ) >::type
> : std::true_type {};
template<class T, class F>
constexpr bool is_callable_with(F &&)
{
return is_callable_with_impl< F, T >::value;
}
It is basically the same solution as the one posted by Paul, I just prefer to use conditional<true, void, decltype( ... ) > instead of an holder class to avoid namespace pollution.

Arbitrary type transformations of functor arguments and results

I have a class something like this:
template <typename T>
struct operation {
typedef T result_type;
typedef ::std::shared_ptr<operation<T> > ptr_t;
};
I have a functor that would match this ::std::function type:
::std::function<int(double, ::std::string)>
I want to create a functor that has a signature something like this:
operation<int>::ptr_t a_func(operation<double>::ptr_t, operation< ::std::string>::ptr_t);
I want to do this in an automated fashion so I can create a similar functor for any given ::std::function type.
Lastly, I would like to put this wrinkle in. This:
::std::function<int(operation<double>::ptr_t, ::std::string)>
should result in this:
operation<int>::ptr_t a_func(operation<double>::ptr_t, operation< ::std::string>::ptr_t);
Because if a functor already accepts an operation<T>::ptr_t that means it understands what they are and is willing to deal with their asynchronous nature itself.
How would I do this? I have a naive and partially working attempt here:
template <typename argtype>
struct transform_type {
typedef typename operation<argtype>::ptr_t type;
};
template <typename ResultType, typename... ArgTypes>
::std::function<typename transform_type<ResultType>::type(typename transform_type<ArgTypes...>::type)>
make_function(::std::function<ResultType(ArgTypes...)>)
{
   return nullptr;
}
It doesn't detect arguments that are already of type std::shared_ptr<operation<T> > though. And this specialization of transform_type fails to compile:
template <typename argtype>
struct transform_type<typename operation<argtype>::ptr_t>
{
typedef typename stub_op<argtype>::ptr_t type;
};
template<template<typename...> class F, typename Sig>
struct transform;
template<template<typename...> class F, typename R, typename... A>
struct transform<F, R(A...)> {
using type = typename F<R>::ptr_t(typename F<A>::ptr_t...);
};
Usage looks like:
template<typename Sig>
void foo(std::function<Sig> f)
{
using transformed_type = typename transform<operation, Sig>::type;
std::function<transformed_type> g;
}
As for the specialization to avoid transforming types that are already in the desired form:
template<typename T>
struct operation<std::shared_ptr<T>> {
using ptr_t = std::shared_ptr<T>;
using result_type = ptr_t; // Or perhaps this needs to be T, you haven't said
};
I believe I have figured it out with R. Martinho Fernandez's help:
template <typename T>
struct is_op_ptr {
private:
// Returns false_type, which has a ::value that is false.
template <class AT>
static constexpr std::false_type is_it_a_ptr(...);
// Returns true_type (if enable_if allows it to exist).
template <class AT>
static constexpr typename ::std::enable_if<
::std::is_same<
AT,
typename operation<typename AT::element_type::result_type>::ptr_t>::value,
std::true_type>::type // note the true_type return
is_it_a_ptr(int); // no definition needed
public:
// do everything unevaluated
static constexpr bool value = decltype(is_it_a_ptr<T>(0))::value;
};
template <typename T>
struct transform_type
: ::std::conditional< is_op_ptr<T>::value, T, typename operation<T>::ptr_t>
{
};
This also allows me to query whether or not a type will be transformed in the construction of the wrapper function.

Specializing a template on a lambda in C++0x

I've written a traits class that lets me extract information about the arguments and type of a function or function object in C++0x (tested with gcc 4.5.0). The general case handles function objects:
template <typename F>
struct function_traits {
template <typename R, typename... A>
struct _internal { };
template <typename R, typename... A>
struct _internal<R (F::*)(A...)> {
// ...
};
typedef typename _internal<decltype(&F::operator())>::<<nested types go here>>;
};
Then I have a specialization for plain functions at global scope:
template <typename R, typename... A>
struct function_traits<R (*)(A...)> {
// ...
};
This works fine, I can pass a function into the template or a function object and it works properly:
template <typename F>
void foo(F f) {
typename function_traits<F>::whatever ...;
}
int f(int x) { ... }
foo(f);
What if, instead of passing a function or function object into foo, I want to pass a lambda expression?
foo([](int x) { ... });
The problem here is that neither specialization of function_traits<> applies. The C++0x draft says that the type of the expression is a "unique, unnamed, non-union class type". Demangling the result of calling typeid(...).name() on the expression gives me what appears to be gcc's internal naming convention for the lambda, main::{lambda(int)#1}, not something that syntactically represents a C++ typename.
In short, is there anything I can put into the template here:
template <typename R, typename... A>
struct function_traits<????> { ... }
that will allow this traits class to accept a lambda expression?
I think it is possible to specialize traits for lambdas and do pattern matching on the signature of the unnamed functor. Here is the code that works on g++ 4.5. Although it works, the pattern matching on lambda appears to be working contrary to the intuition. I've comments inline.
struct X
{
float operator () (float i) { return i*2; }
// If the following is enabled, program fails to compile
// mostly because of ambiguity reasons.
//double operator () (float i, double d) { return d*f; }
};
template <typename T>
struct function_traits // matches when T=X or T=lambda
// As expected, lambda creates a "unique, unnamed, non-union class type"
// so it matches here
{
// Here is what you are looking for. The type of the member operator()
// of the lambda is taken and mapped again on function_traits.
typedef typename function_traits<decltype(&T::operator())>::return_type return_type;
};
// matches for X::operator() but not of lambda::operator()
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...)>
{
typedef R return_type;
};
// I initially thought the above defined member function specialization of
// the trait will match lambdas::operator() because a lambda is a functor.
// It does not, however. Instead, it matches the one below.
// I wonder why? implementation defined?
template <typename R, typename... A>
struct function_traits<R (*)(A...)> // matches for lambda::operator()
{
typedef R return_type;
};
template <typename F>
typename function_traits<F>::return_type
foo(F f)
{
return f(10);
}
template <typename F>
typename function_traits<F>::return_type
bar(F f)
{
return f(5.0f, 100, 0.34);
}
int f(int x) { return x + x; }
int main(void)
{
foo(f);
foo(X());
bar([](float f, int l, double d){ return f+l+d; });
}
The void_t trick can help. How does `void_t` work?
Unless you have C++17, you'll need to include the definition of void_t:
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
Add an extra template argument to the original template, defaulted to void:
template <typename T, typename = void>
struct function_traits;
The traits object for simple functions is the same as you already have:
template <typename R, typename... A>
struct function_traits<R (*)(A...)>
{
using return_type = R;
using class_type = void;
using args_type = std:: tuple< A... >;
};
For non-const methods:
template <typename R, typename... A>
struct function_traits<R (C::*)(A...)>
{
using return_type = R;
using class_type = void;
using args_type = std:: tuple< A... >;
};
Don't forget const methods:
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...) const> // const
{
using return_type = R;
using class_type = C;
using args_type = std:: tuple< A... >;
};
Finally, the important trait. Given a class type, including lambda types, we want to forward from T to decltype(&T::operator()). We want to ensure that this trait is only available for types T for which ::operator() is available, and this is what void_t does for us. To enforce this constraint, we need to put &T::operator() into the trait signature somewhere, hence template <typename T> struct function_traits<T, void_t< decltype(&T::operator())
template <typename T>
struct function_traits<T, void_t< decltype(&T::operator()) > >
: public function_traits< decltype(&T::operator()) >
{
};
The operator() method in (non-mutable, non-generic) lambdas is const, which explains why we need the const template above.
But ultimately this is very restrictive. This won't work with generic lambdas, or objects with templated operator(). If you reconsider your design, you find find a different approach that is more flexible.
By delegating some of the work to a series of function templates instead of a class template, you can extract the relevant info.
First though, I should say that the relevant method is a const method, for a lambda (for a non-capturing, non-generic, non-mutable lambda). So you will not be able to tell the difference between a true lambda and this:
struct {
int operator() (int) const { return 7; }
} object_of_unnamed_name_and_with_suitable_method;
Therefore, I must assume that you don't want "special treatment" for lambdas, and you don't want to test if a type is a lambda type, and that instead you want to simply extract the return type, and the type of all arguments, for any object which is simple enough. By "simple enough" I mean, for example, that the operator() method is not itself a template. And, for bonus information, a boolean to tell us if an operator() method was present and used, as opposed to a plain old function.
// First, a convenient struct in which to store all the results:
template<bool is_method_, bool is_const_method_, typename C, typename R, typename ...Args>
struct function_traits_results {
constexpr static bool is_method = is_method_;
constexpr static bool is_const_method = is_const_method_;
typedef C class_type; // void for plain functions. Otherwise,
// the functor/lambda type
typedef R return_type;
typedef tuple<Args...> args_type_as_tuple;
};
// This will extract all the details from a method-signature:
template<typename>
struct intermediate_step;
template<typename R, typename C, typename ...Args>
struct intermediate_step<R (C::*) (Args...)> // non-const methods
: public function_traits_results<true, false, C, R, Args...>
{
};
template<typename R, typename C, typename ...Args>
struct intermediate_step<R (C::*) (Args...) const> // const methods
: public function_traits_results<true, true, C, R, Args...>
{
};
// These next two overloads do the initial task of separating
// plain function pointers for functors with ::operator()
template<typename R, typename ...Args>
function_traits_results<false, false, void, R, Args...>
function_traits_helper(R (*) (Args...) );
template<typename F, typename ..., typename MemberType = decltype(&F::operator()) >
intermediate_step<MemberType>
function_traits_helper(F);
// Finally, the actual `function_traits` struct, that delegates
// everything to the helper
template <typename T>
struct function_traits : public decltype(function_traits_helper( declval<T>() ) )
{
};