Wrapper for templated functions - c++

What I am trying to accomplish is the following:
// or any templated function
template <typename... Args>
void function(Args... args) {}
// wrapper
void launch(???) { ??? }
int main()
{
// first option
launch(function, 1, 2, 3, 4);
// second option
launch<function>(1, 2, 3, 4);
}
As far as I can tell, the first option is impossibile since I would have to pass the specialized template function (which I'm trying to avoid).
For the second option I don't know if it's possible, I came up with the following not working implementation:
template <template <typename...> class Function, typename... Args>
void launch(Args... args)
{
Function<Args...>(args...);
}
which ends up giving me:
main.cpp:18:5: error: no matching function for call to 'launch'
launch<function>(1, 2, 3, 4);
^~~~~~~~~~~~~~~~
main.cpp:9:6: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Function'
void launch(Args... args)
^
1 error generated.
So, is something like this even possible?

You basically cannot do anything with function templates except call them (and let arguments get deduced) or instantiate them (by manually specifying template arguments).
I believe there are also niche situations where template arguments may be deduced and a specific instantiation chosen without an actual call, but they don't help here AMA's answer shows how to do that!
Generic lambdas may or may not help you solve your problem, but you need one such forwarding lambda per function template you want to make "passable":
#include <functional>
// or any templated function
template <typename Arg1, typename Arg2>
void function(Arg1 arg1, Arg2 arg2) {}
int main()
{
auto wrapper = [](auto arg1, auto arg2) {
return function(arg1, arg2);
};
std::invoke(wrapper, 1, 2);
}
Demo
(Perfect-forwarding to a variadic function with a variadic lambda would be more complicated.)
So you might as well write function templates in the form of functor structs in the first place, or in the form of lambdas returned from non-template functions.

How about:
template <typename ... Args>
void launch(void(*func)(Args...), Args&&... args) {
func(std::forward<Args>(args)...);
}
calling launch:
launch(function, 1, 2, 3, 4);
Live example

The idiomatic way would be to infer the type of the callable, as if it was any type, and not care about the template-ness of the thing:
template <typename F, typename ... Args>
auto launch(F f, Args&&... args) -> decltype(auto) {
return f(std::forward<Args>(args)...);
}
It also will forward the return value of the function.
Then, to send your templated function, you must lift the function into a lambda:
auto function_lift = [](auto&&... args)
noexcept(noexcept(function(std::forward<decltype(args)>(args)...)))
-> decltype(function(std::forward<decltype(args)>(args)...))
{
return function(std::forward<decltype(args)>(args)...);
};
// also works with defaulted parameters.
launch(function_lift, 1, 2, 3, 4);
Creating those lifted function is very verbose. The answer to verbose-ness in this case is of course a macro:
#define LIFT(lift_function) [](auto&&... args) \
noexcept(noexcept(lift_function(std::forward<decltype(args)>(args)...))) \
-> decltype(lift_function(std::forward<decltype(args)>(args)...)) \
{ \
return lift_function(std::forward<decltype(args)>(args)...); \
}
Now you can call your wrapper:
launch(LIFT(function), 5, 4, 3, 2);

Related

Passing generic function instance without explicitly enumerating template arguments

I have the following code:
#include <functional>
using FooFn = void(int, int, int);
template<class... Args>
void foo(Args&&... args) {
// Do something
}
void bar(std::function<FooFn> fooFn) {
fooFn(1, 2, 3);
}
I am able to pass foo with explicit template arguments:
bar(&foo<int, int, int>);
I also can wrap foo in a generic lambda and pass it to a modified version of bar:
template<class F>
void bar(F fooFn) {
fooFn(1, 2, 3);
}
bar([](auto... args){ foo(args...); });
But it decreases code readability.
Since the number of arguments to FooFn can be quite large, I want to avoid enumerating arguments (possibly using FooFn alias). How do I do that?

How to pass a template function to another function and apply it

// I can't change this function!
template<typename Function, typename... Args>
void RpcWriteKafka(Function func, Args&&... args) {
func(std::forward<Args>(args)...);
}
// I can change this one if necessary.
template<typename FUNC, typename... Args, typename CALLBACK, typename... CArgs>
void doJob(std::tuple<CALLBACK, CArgs&&...> tp, FUNC func, Args&&... args) {
// SetValues(std::forward<Args>(args)...);
std::apply(func, tp);
}
int main() {
doJob(std::make_tuple([](int i){}, 1), RpcWriteKafka, 1);
return 0;
}
As you see, some library provided the template function RpcWriteKafka. It needs the parameter about a callback function(func) and its parameters(args...).
I want to define my own function doJob, which allows me to call it like this: doJob(std::make_tuple([](int i){}, 1), RpcWriteKafka, 1);. I'm expecting that the first parameter, which is a std::tuple, could be passed to the second parameter RpcWriteKafka.
Why do I used std::tuple: How to pass a function with parameter pack to a function with parameter pack
For now, it can't be compiled.
The compiler generated two errors:
mismatched types 'CArgs&&' and 'int', which comes from the 1 in that tuple;
the second parameter RpcWriteKafka is unresolved overloaded function type.
So how to solve the two issues? Is it possible to define such a function doJob so that I can call it easily as shown in the main above?
First, the first parameter type of doJob should be std::tuple<CALLBACK, CArgs...> instead of std::tuple<CALLBACK, CArgs&&...> since CArgs&&
cannot be deduced in such context.
Second, since RpcWriteKafka is a function template, you can't pass it to doJob like this, instead, you need to wrap it with lambda, so this should work (I omit the Args&&... because it is not used)
template<typename CALLBACK, typename... CArgs, typename FUNC>
void doJob(std::tuple<CALLBACK, CArgs...>&& tp, FUNC func) {
std::apply(func, tp);
}
int main() {
doJob(std::make_tuple([](int i){}, 1),
[](auto... args) { RpcWriteKafka(args...); });
}

I can't pass template funtion to std::apply directly but I can through lambda

Based on: How do I expand a tuple into variadic template function's arguments?
#include <string>
#include <iostream>
#include <tuple>
template <typename... Args>
void print_all(const Args &... args) {
((std::cout << " " << args), ...) << std::endl;
}
int main()
{
// Create a tuple
auto values = std::make_tuple(1, 2, 3.4f, 4.5, "bob");
// Need to pass the tuple through the lambda for template type deduction and to pass param to template function?
std::apply([](auto &&... args) { print_all(args...); }, values);
// This does not work - other then there is no parameter I can't see how this does not work
// and how the lambda does work as it has the same (roughly) param list
std::apply(print_all(), values);
return 0;
}
can someone explain why one works and the other does not?
Behind the scences, this lambda expression [](auto &&... args) { print_all(args...); } is roughly of type:
struct [unnamed] {
template <typename ...Args>
void operator()(Args&&...args) { ... };
};
It is a type with a templated operator(), ie overload resolution and template argument dedcution only take place once the operator() is actually called. print_all on the other hand is a template, hence you cannot pass it to std::apply.
In other words, no matter what Args... is, the lambda is always of same type, but print_all isn't. You would need to instantiate it before you can get a pointer to the function. As mentioned by Scheff, this is fine:
std::apply(&print_all<int, int, float, double, const char*>, values);

Deducing function overload in a templated function

I'm writing my own std::async analogue (has to work back to Intel13/gcc 4.4 STL), and this works fine:
template <typename Func, typename ...Args>
struct return_value {
template <typename T>
using decayed = typename std::decay<T>::type;
using type = typename std::result_of<decayed<Func>(decayed<Args>...)>::type;
};
template <typename Func, typename ...Args>
typename return_value<Func,Args...>::type async(Func &&func, Args&&... args) {
return func(args...);
}
void run(int a, double b) {
printf("a: %i b: %f\n", a, b);
}
int main() {
async(run, 1, 3.14);
}
But if I add an overload for run:
void run() {
printf("no args\n");
}
Then it can't properly resolve:
<source>: In function 'int main()':
<source>:27:23: error: no matching function for call to 'async(<unresolved overloaded function type>, int, double)'
async(run, 1, 3.14);
^
<source>:14:43: note: candidate: 'template<class Func, class ... Args> typename return_value<Func, Args>::type async(Func&&, Args&& ...)'
typename return_value<Func,Args...>::type async(Func &&func, Args&&... args) {
^~~~~
<source>:14:43: note: template argument deduction/substitution failed:
<source>:27:23: note: couldn't deduce template parameter 'Func'
async(run, 1, 3.14);
^
Compiler returned: 1
How can I take a function as a template parameter and properly deduce the overload given the arguments?
I personally don't see a way to disambiguate overloads unless you know the return type. You could assume return type void most common and to this then: (I am simplifying your example for brevity)
template <class F, class... Args>
auto async(F f, Args... args)
{
return f(args...);
}
template <class... Args>
auto async(void (*f)(Args...), Args... args)
{
return f(args...);
}
void run();
void run(int, double);
auto test()
{
async(run); // calls run();
async(run, 1, 2.); // calls run(int, double);
}
This does seem kind of fishy and confusing to the user. Why does it work when the function passed returns void and it doesn't if it returns int? So I don't recommend it.
So really the only thing you could do is let it in the hands of the user to figure it out.
So some solutions for the caller of your function:
The good (and ugly) old way: use cast to disambiguate the overload:
async(static_cast<int(*)(int, double)>(run), 1, 2.);
I personally don't like this approach at all. I don't like the verbosity of it and most of all I don't like that I have to be explicit about something that should really be implicit.
The lambda way
async([] { return run(1, 2.); });
I like this. It's not half bad. Still a little bit verbose, but way way better than other alternatives.
The macro way
Yes, macros, in C++. Without further ado, there it is (perfect forwarding omitted for brevity):
#define OVERLOAD(foo) [] (auto... args) { return foo(args...); }
async(OVERLOAD(run), 1, 2.);
I am not going to comment on this one. I leave each and every one of you to judge this macro.

C++ parameter pack expansion

The code below doesn't compile (see error below the code). Can you please explain why?
template <class F, class... Arg>
void for_each_argument(F f, Arg&&... arg)
{
f(std::forward<Arg>(arg...));
}
int main()
{
for_each_argument(
[](const auto& a){std::cout<< a;}, "Aa", 3, 4);
return 0;
}
Here is an error message:
7:4: error: expression contains unexpanded parameter pack
'Arg'
f(std::forward(arg...));
You have several issues in your code. First of, your original line
f(std::forward<Arg>(arg...));
Is not correct syntax at all - you are expanding arg without properly expanding Arg in template. Now, you can fix at least that by
f(std::forward<Arg>(arg)...);
This would be better, but still wrong - you will call your lambda once with 3 arguments, while it only accepts one - and instead, you want to call lambda 3 times with a single argument.
There are several ways to do this. First, and the least preferred, is to call the function recursively, as other answer suggests. This prompts ugly syntax, and also adds burden on compiler for recursive template instantiation. Much better solution is to expand the argument using array trick, for example (ignoring the forward for simplicity):
auto lam = [&f](const auto& a) { f(a); return true;}
bool arr[] = { lam(std::forward<ARG>(arg))... };
(void)arr;
In C++ 17 you can use fold expression to achieve even more neat syntax:
(f(std::forward<ARG>(arg)), ...);
Expanding a parameter pack works in contexts that expect a comma separated list.
That is, your code:
f(std::forward<Arg>(arg...));
It attempting to expand into:
f( "Aa", 3, 4 );
And the lambda you have supplied does not support such a call.
To expand a parameter pack into multiple function calls, use a recursive function.
template <class F>
void for_each_argument(F f)
{
// (No args)
}
template <class F, class FirstArg, class... MoreArgs>
void for_each_argument(F f, FirstArg&& first_arg, MoreArgs&&... more_args)
{
f( std::forward<FirstArg>(first_arg) );
for_each_argument( f, std::forward<MoreArgs>(more_args)... );
}