Initialize a class containing std :: function with lambda

I created a template class containing std::function as a member, as follows:

 template<typename Ret, typename... Args> class Foo { private: std::function<Ret(Args...)> _func; public: Foo(const std::function<Ret(Args...)>& func): _func(func) {} }; 

In order not to specify the arguments and return type of the function passed, I created several overloads of make_foo :

 template<typename Ret, typename... Args> auto make_foo(Ret (&func)(Args...)) -> Foo<Ret, Args...> { return { std::function<Ret(Args...)>(func) }; } template<typename Ret, typename... Args> auto make_foo(const std::function<Ret(Args...)>& func) -> Foo<Ret, Args...> { return { func }; } 

However, I was unable to create a make_foo overload that takes a lambda parameter as a parameter:

 template<typename Ret, typename... Args> auto make_foo(??? func) -> Foo<Ret, Args...> { return { std::function<Ret(Args...)>(func) }; } 

I just can't find a way to automatically get the return type and argument types from lambda. Is there an idiomatic way to solve such a problem?

+5
source share
2 answers

Okay, so I thought I was going to die, but I finally managed to do it Γ§_Γ§

Firstly, I used regular indexes. Since I don't have official ones, I used old indexes that I wrote a few months ago:

 template<std::size_t...> struct indices {}; template<std::size_t N, std::size_t... Ind> struct make_indices: make_indices<N-1, N-1, Ind...> {}; template<std::size_t... Ind> struct make_indices<0, Ind...>: indices<Ind...> {}; 

Then I used some function properties found somewhere in StackOverflow. They are nice, and I think they are equivalent to the Boost library associated with comments:

 template<typename T> struct function_traits: function_traits<decltype(&T::operator())> {}; template<typename C, typename Ret, typename... Args> struct function_traits<Ret(C::*)(Args...) const> { enum { arity = sizeof...(Args) }; using result_type = Ret; template<std::size_t N> using arg = typename std::tuple_element<N, std::tuple<Args...>>::type; }; 

Then I was able to write the correct make_foo function and its implementation function, since both should use indexes. Be careful, this is just ugly:

 template<typename Function, std::size_t... Ind> auto make_foo_(Function&& func, indices<Ind...>) -> Foo< typename function_traits<typename std::remove_reference<Function>::type>::result_type, typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...> { using Ret = typename function_traits<typename std::remove_reference<Function>::type>::result_type; return { std::function<Ret(typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...)>(func) }; } template<typename Function, typename Indices=make_indices<function_traits<typename std::remove_reference<Function>::type>::arity>> auto make_foo(Function&& func) -> decltype(make_foo_(std::forward<Function>(func), Indices())) { return make_foo_(std::forward<Function>(func), Indices()); } 

The code is somehow ugly and unreadable, but it definitely works. I hope that now he does not rely on any specific behavior. Also, thanks for your advice, it helped! :)

 int main() { auto lambda = [](int i, float b, long c) { return long(i*10+b+c); }; auto foo = make_foo(lambda); std::cout << foo(5, 5.0, 2) << std::endl; // 57, it works! } 

And here is a living example :)

+3
source

I have an example that works with mutable lambdas. I can’t figure out how to qualify for a CV.

Firstly, here is the following function template:

 #include <functional> template <typename R, typename ...Args> void foo(std::function<R(Args...)> f) { } 

Now we let the bar function template take an arbitrary lambda and call the correct version of foo by checking the type of lambda operator() :

 #include <type_traits> template <typename> struct remove_member; template <typename C, typename T> struct remove_member<TC::*> { using type = T; }; template <typename F> void bar(F f) { using ft = decltype(&F::operator()); foo(std::function<typename remove_member<ft>::type>(f)); } 

Example:

 int q; bar([&](int a, int b) mutable -> int { q = a + b; return q / b; }); 

You can use regular, const lambdas with this changed attribute, although I don't like to write the type of function:

 template <typename C, typename R, typename ...Args> struct remove_member<R (C::*)(Args...) const> { using type = R(Args...); }; 

I thought this might work with the source code if I use typename std::remove_cv<T>::type , but at least on GCC it doesn't work due to some weird __attribute__((const)) installed on the type of lambda operator that seems to interfere with the specialization pattern.

+2
source

Source: https://habr.com/ru/post/979761/


All Articles