How to make a function that accepts another one with argument tuple - c++

I need a function My_func that works like this
auto f = [](const std::tuple<string, double>& t) { return std::get<0>(t); };
assert(My_func(f)("Hello", 8.5) == f({"Hello", 8.5}));
Now i have
template <class F>
constexpr auto My_func(F&& f) {
return [f](auto&& args...) { return std::forward<F>(f)(args); };
}
But it doesn't work.What should i fix?

First of all you need My_func to be syntactically valid. You have a pack args that is not expanded in the lambda. Then you need to mention tuple somewhere. C++ is not psychic.
Luckily, there exists std::forward_as_tuple that does exactly what you need here
template <class F>
constexpr auto My_func(F&& f) {
return [f = std::forward<F>(f)](auto&& args...) { return f(std::forward_as_tuple(args...)); };
}

template <class F>
constexpr auto My_func(F&& f)
{
return [f = std::forward<F>(f)](auto&&... args) {
return f(std::make_tuple(std::forward<decltype(args)>(args)...));
};
}

Related

Call lambda with other lambda result when its return type is void

The following function makes a lambda that calls the second callable with the first callable's result. If the first callable returns a tuple it will be applied to the second callable.
template<typename T>
struct is_tuple : std::false_type{};
template<typename... T>
struct is_tuple<std::tuple<T...>> : std::true_type{};
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source), std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
using source_return = decltype(source(args...));
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<decltype(args)>(args)...));
}
else
{
return target(source(std::forward<decltype(args)>(args)...));
}
};
}
However this does not compile when the source callable returns void, since it will try to call target with incomplete type void, so I tried the following:
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source), std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
using source_return = decltype(source(args...));
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<decltype(args)>(args)...));
}
else if constexpr(std::is_void_v<source_return>)
{
source(std::forward<decltype(args)>(args)...);
return target();
}
else
{
return target(source(std::forward<decltype(args)>(args)...));
}
};
}
But this doesn't seem to work somehow since it always takes the same as void branch, even when the source function cannot return void in any situation. I geuss there is something wrong with the decltype deducing the source_return. I tried to assign the result of source to a variable to decltype the that variable instead of decltype(source(args...)) but then it gives me the error that the variable is of incomplete type void in the cases it actually does return void, so I do have to check it before actually calling source.
Here is an example of pipeline usage that does not compile:
auto callable = pipeline([]{ return 10 },
[](size_t val){ return val * 10});
callable();
The reason it does not compile is because it takes the source_return is same as void branch for some reason. Anybody has any idea how I can figure out the return type of source when called with args... in a way that is more robust?
EDIT:
I got it to work by using a call_pipeline helper function. I still don't understand why this one would work and the other one doesn't though.
template<typename S, typename T, typename... Args>
constexpr decltype(auto) call_pipeline(const S& source, const T& target, Args&&... args)
{
using source_return = decltype(source(std::forward<Args>(args)...));
if constexpr(std::is_void_v<source_return>)
{
source(std::forward<Args>(args)...);
return target();
}
else
{
if constexpr(is_tuple<source_return>::value)
{
return std::apply(target, source(std::forward<Args>(args)...));
}
else
{
return target(source(std::forward<Args>(args)...));
}
}
}
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source_init, T&& target_init)
{
return [callables = std::tuple<S, T>(std::forward<S>(source_init),
std::forward<T>(target_init))]
(auto&&... args)
{
const auto&[source, target] = callables;
return call_pipeline(source, target, std::forward<decltype(args)>(args)...);
};
}
Not sure about isn't working your way but I propose the following alternative
template<typename S, typename T>
constexpr decltype(auto) pipeline(S&& source, T&& target)
{
return [callables = std::tuple<S, T>(std::forward<S>(source),
std::forward<T>(target))]
(auto&&... args)
{
const auto&[source, target] = callables;
auto tplr = [](auto s, auto && ... as)
{
using source_return
= decltype(s(std::forward<decltype(as)>(as)...));
if constexpr ( is_tuple<source_return>::value )
return s(std::forward<decltype(as)>(as)...);
else if constexpr ( std::is_void_v<source_return> )
{
s(std::forward<decltype(as)>(as)...);
return std::make_tuple();
}
else
return std::make_tuple(s(std::forward<decltype(as)>(as)...));
}(source, std::forward<decltype(args)>(args)...);
std::apply(target, tplr);
};
}
The idea is ever made target() called through std::apply with a std::tuple (maybe empty) of arguments.

C++ how to infer the Callable's type (arguments list & return value) in a template

Basically what I want to do, is to make a function template, that takes any Callable (function type / lambda / Functor) and returns a lambda-taking-the-similar-args-list and returning the type of original's return type
#include <iostream>
int func(int a,float b) {
return a+b;
}
struct callable {
int operator() (int a, float b) {
return a+b;
}
};
template <typename RV, typename... Args>
auto getLambdaFromCallable(RV(&func)(Args...)) {
auto l = [&](Args... args) -> RV {
return func(args...);
};
return l;
}
int main() {
auto f = getLambdaFromCallable(func);
std::cout << f(1,2.f);
std::cout << " " << typeid(f).name();
auto f2 = getLambdaFromCallable(callable{}); // doesn't work
callable{}(1,2); // works
auto lambdaTest = [](int a, float b) -> int {
return a+b;
};
auto f3 = getLambdaFromCallable(lambdaTest);
}
You can change getLambdaFromCallable to:
template <typename F>
auto getLambdaFromFunction(const F& func) {
auto l = [&](auto&&... args)
-> decltype(func(std::forward<decltype(args)>(args)...)) {
return func(std::forward<decltype(args)>(args)...);
};
return l;
}
The reasoning behind this is that since you cannot get an exhaustive list of argument you can call a function object with (there might be multiple overloads in the first place), you might as well use a generic lambda which accepts everything and forward it to the callable.
To elaborate more about how this works:
The auto&&... part gets converted to a template argument list on the lambda's call operator.
F is deduced to whatever you called the getLambdaFromFunction with (without the const and reference but that can be changed if needed).
decltype(args) is just there to use std::forward which in turn is there to correctly forward both lvalue and rvalue references, see std::forward for more details.
The generated lambda object will look like this:
template <typename F>
class generatedLambda
{
public:
template <typename... Args>
auto operator()(Args&&... args) -> decltype(func(std::forward<decltype(args)>(args)...))
{
return func(std::forward<decltype(args)>(args)...);
}
private:
F func;
};
If you can use C++17, std::invoke is the right tool:
#include <functional>
template <typename OBJ>
auto getLambdaFromCallable(OBJ&& obj)
{
return [&](const auto&... args) {
return std::invoke(std::forward<OBJ>(obj), args...);
};
}
You can compile it with g++ -std=c++17 or clang++-5.0 -std=c++1z and this is working out of the box.

std::result_of parameter type

I've been having this question for some time. Suppose we have a contrived function:
template<typename F>
std::result_of_t<std::decay_t<F>(???)> transform(F&& f)
{
static const int num = 42;
return std::forward<F>(f)(num);
}
The thing I'm not sure of is whether I should use int or const int& for the ??? part. Similarly, for this function:
template<typename F>
std::result_of_t<std::decay_t<F>(???)> transform(F&& f)
{
ExpensiveType foo;
return std::forward<F>(f)(std::move(foo));
}
Should I use ExpensiveType or ExpensiveType&& for the ??? part?
Use auto!
C++14:
template < typename F >
auto transform(F&& f)
{
constexpr auto num = 42;
return std::forward<F>(f)(num);
}
C++11:
template < typename F >
auto transform(F&& f) -> decltype(std::forward<F>(f)(42))
{
// ... same body
}

Return the same type as a lambda expression passed as argument

I want to create a function that accepts both function pointers and lambda expressions. The return type of the function should be the same as the return type of the function pointer/lambda expression.
The following is a minimal example of a function that works as intended but for function pointers only. Can I use templates to accept lambda expressions as well?
template <typename R>
R foo(R (*func)())
{
return func();
}
The following works for both function pointers and lambda expressions but only accepts bool.
bool foo(std::function<bool()> func)
{
return func();
}
bool a = foo( [](){ return true; } );
I tried to make it generic using templates but I get a compiler error when it's called (no matching function)
template <typename R>
R foo(std::function<R()> func)
{
return func();
}
bool a = foo( [](){ return true; } );
You may use deduced return type:
template <typename F>
auto foo(F f) -> decltype(f())
{
return f();
}
And if F takes some arguments :
template <typename F, typename ... Args>
auto foo(F f, Args&&...args) -> decltype(f(std::forward<Args>(args)...))
{
return f(std::forward<Args>(args)...);
}
That's a job for decltype :
template <typename F>
auto foo(F f) -> decltype(f())
{
}

Deduce template argument when lambda passed in as a parameter

I am trying to write function func so that compiler can deduce template argument, it works when I pass in std::function, but does not work with lambdas:
template<typename TResult>
TResult func(std::function<TResult()> f)
{
return TResult();
}
int main()
{
// Visual Studio 2013
int result = func([]() { // error: 'TResult func(std::function<TResult(void)>)' : could not deduce template argument for 'std::function<TResult(void)>' from 'main::<lambda_d9d7854806072a2cb711f56185602ccb>'
return 100;
});
std::function<int()> testFunc = []() {
return 100;
};
int result2 = func(testFunc); // this works
return 0;
}
Is it possible to deduce template argument for lambda so that this line compiles? Instead of writing func<int>([](){ return 100; }); I want to write func([](){ return 100; });
I'm late a little bit :)
There is a solution without std::function
template<typename Class, typename R, typename... Args>
R resultType(R (Class::*)(Args...) const)
{
return R();
}
template<typename Class, typename R, typename... Args>
R resultType(R (Class::*)(Args...))
{
return R();
}
template<typename Functor>
auto func(Functor f) -> decltype(resultType(&Functor::operator()))
{
return resultType(&Functor::operator());
}
I can't see how to do it immediately but you can do it with in an indirection:
template <typename TResult>
TResult func(std::function<TResult()> f) {
return TResult();
}
template <typename Fun>
auto func(Fun f) -> decltype(f()) {
return func(std::function<decltype(f())>(f));
}
At least if I understand the intent, I believe you can do things this way:
template <class F>
auto foo(F &&f) -> decltype(f()) {
typedef decltype(f()) ret_type;
return ret_type();
}
...or if you prefer to do without the typedef:
template <class F>
auto foo(F &&f) -> decltype(f()) {
return decltype(f())();
}
Complete program, comparing usage and results:
#include <functional>
#include <iostream>
template <class F>
auto foo(F &&f) -> decltype(f()) {
return decltype(f())();
}
template<typename TResult>
TResult func(std::function<TResult()> f) {
return TResult();
}
int main() {
std::cout << foo([]() { return 100; })<<"\n";
std::function<int()> testFunc=[]() { return 100; };
std::cout << func(testFunc) <<"\n"; // this works
return 0;
}
Results:
0
0
No you can't use std::function here, the compiler can't deduce its type arguments.
You should just pass the parameter as TFunction.