Passing generic function instance without explicitly enumerating template arguments - c++

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?

Related

std::function argument to receives any number of arguments

I've defined a template function that receives std::function. and I want to send a member function. that works fine (example: test2)
How can I rewrite it so std::function receives any number of argument? (test3)
another question - can this be done without the std::bind?
struct Cls
{
int foo() { return 11; }
};
template<typename T>
void test2(std::function<T()> func)
{}
template<typename T, typename... Args>
void test3(std::function<T(Args... args)> func, Args&&... args)
{}
int main()
{
Cls ccc;
test2<int>(std::bind(&Cls::foo, ccc)); // Ok
test3<int>(std::bind(&Cls::foo, ccc)); // does not compile!
}
This is the error I receive:
no matching function for call to ‘test3<int>(std::_Bind_helper<false, int (Cls::*)(), Cls&>::type)’ 34 | test3<int>(std::bind(&Cls::foo, ccc));
The issue is that std::bind doesn't return a std::function, it returns some unspecified callable type. Therefore the compiler can't deduce the correct template parameters for test3.
The way to work around that is to make your function accept any callable type instead of trying to accept a std::function:
template <typename Func, typename... Args>
void test3(Func&& func, Args&&... args)
{}
This will work with a std::bind result, a lambda, a raw function pointer, a std::function, etc.
can this be done without the std::bind?
Sort of. A pointer to a member function always requires an instance of the class to be called on. You can avoid needing to explicitly std::bind the instance into a wrapper by passing it as a parameter and using the std::invoke helper to call the function. For pointers to member functions, std::invoke treats the first parameter as the object to call the member on.
For example:
struct Cls
{
int foo() { return 11; }
};
template <typename Func, typename... Args>
void test3(Func&& func, Args&&... args)
{
std::invoke(std::forward<Func>(func), std::forward<Args>(args)...);
}
void bar(double, int) {}
int main()
{
Cls ccc;
test3(bar, 3.14, 42); // Works with a raw function pointer
test3(&Cls::foo, ccc); // Works with a pointer to member function
test3([](int){}, 42); // Works with other callable types, like a lambda
}
Demo
can this be done without the std::bind?
Yes this can be done without std::bind as shown below.
struct Cls
{
int foo() { return 11; }
int func(int, double)
{
return 4;
}
};
template<typename className, typename... Param,typename Ret, typename... Args>
void test4(Ret (className::*ptrFunc)(Param... param),className& Object, Args... args)
{
(Object.*ptrFunc)(args...);
//std::invoke(ptrFunc, Object, args...); //WITH C++17
}
int main()
{
Cls ccc;
test4(&Cls::foo, ccc); //works
test4(&Cls::func, ccc, 4,5); //works
}
Working demo
With C++17 we can use std::invoke to replace the call (Object.*ptrFunc)(args...); with:
std::invoke(ptrFunc, Object, args...); //WITH C++17
C++17 std::invoke demo

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);

Wrapper for templated functions

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);

Template variadic function in template class won't compile

I'm trying to write a function for a template class which takes in a parameter that is a function pointer for a member class inside the private data of the big class. When you call that member, it calls that function on smaller class. (Confusing right?) To demonstrate, I have a non-working example here:
#include <vector>
#include <iostream>
using namespace std;
template <typename T, typename C>
struct MyClass {
template <typename F, typename... A>
auto call_me(F func, A... args) { // pass in the function we want to call
return (mContainer.*func) (args...); // call the function supplied by
// the parameter on the private member data
}
C mContainer; // this will be private in my actual code
};
int main() {
MyClass<int, std::vector<int> > test;;
cout << test.call_me(&std::vector<int>::size) << endl; // works
test.call_me(&std::vector<int>::insert, test.mContainer.begin(), 4); // doesn't work
return 0;
}
Please note that this isn't my actual code but a small example of what I'm trying to do. As you can see, I'm trying to call the size member function of the 'Private' (I have kept it public here for demonstration) vector class inside MyClass. This only works whenever I have no parameters for the compiler to unpack, but when I try to do the insert function (which has parameters to unpack), the compiler gives me an error of:
.\template.cpp: In function 'int main()':
.\template.cpp:24:71: error: no matching function for call to 'MyClass<int, std::vector<int> >::call_me(<unresolved overloaded function type>, std::vector<int>::iterator, int)'
test.call_me(&std::vector<int>::insert, test.mContainer.begin(), 4);
^
.\template.cpp:10:10: note: candidate: template<class F, class ... A> auto MyClass<T, C>::call_me(F, A ...) [with F = F; A = {A ...}; T = int; C = std::vector<int>]
auto call_me(F func, A... args) { // pass in the function we want to call
^~~~~~~
.\template.cpp:10:10: note: template argument deduction/substitution failed:
.\template.cpp:24:71: note: couldn't deduce template parameter 'F'
test.call_me(&std::vector<int>::insert, test.mContainer.begin(), 4);
This is the same error I'm getting in my actual production code, calling the variadic function with no parameters to unpack works, but if I give more than that, I get the same error message. This is my first real attempt to use Variadic templates, so any recommendation and help will be appreciated.
The problem here is that insert is an overloaded function. The compiler is not doing to try and resolve what overload you want in template argument deduction as there is no way for it to know. You have to cast the function to the type of the overload you want to use in order to give it a type. That would look like
using insert_func_t = std::vector<int>::iterator(std::vector<int>::*)(std::vector<int>::const_iterator, const int&);
test.call_me(static_cast<insert_func_t>(&std::vector<int>::insert), test.mContainer.begin(), 4);
In general it is
static_cast<return_type(class_name::*)(function_parameters)>(&class_name::function_name)
Another option would be to change the function a little and take a lambda that expresses what you want done. That would look like
template <typename T, typename C>
struct MyClass {
template <typename F, typename... A>
auto call_me(F func, A... args) { // pass in the function we want to call
return func(mContainer, args...); // call the function supplied by
// the parameter on the private member data
}
C mContainer; // this will be private in my actual code
};
int main() {
MyClass<int, std::vector<int> > test;;
test.call_me([](auto& container, auto... args){ container.insert(args...); }, test.mContainer.begin(), 4);
return 0;
}
Basically you cannot take address of an unresolved overloaded function, because the compiler won't be able to choose the right function entry point address. During normal function call the compiler resolves overloaded function, but with templates like yours or std::bind() this won't work, because the parameters are used to call the template function, not the function you want to take address of.
You can manually resolve the overload like this:
using ftype = std::vector<int>::iterator(std::vector<int>::*)
(std::vector<int>::const_iterator, const std::vector<int>::value_type&);
test.call_me((ftype)(&std::vector<int>::insert), test.mContainer.begin(), 4); // works
It's easier to deal in function objects when doing this kind of thing. It offloads the problem of method overloads to the compiler.
Lambdas also work (they're function objects):
#include <vector>
#include <iostream>
template <typename T, typename C>
struct MyClass {
template <typename F, typename... A>
auto call_me(F func, A&&... args) -> decltype(auto)
{ // pass in the function we want to call
return func(mContainer, std::forward<A>(args)...); // call the function supplied by
// the parameter on the private member data
}
C mContainer; // this will be private in my actual code
};
/*
* It's often easier to deal in function objects
*/
struct insert
{
template<class Container, class...Args>
decltype(auto) operator()(Container& cont, Args&&...args) const
{
return cont.insert(std::forward<Args>(args)...);
}
};
struct size
{
template<class Container, class...Args>
decltype(auto) operator()(Container& cont) const
{
return cont.size();
}
};
int main() {
MyClass<int, std::vector<int> > test;;
std::cout << test.call_me(size()) << std::endl; // works
test.call_me(insert(), test.mContainer.begin(), 4); // doesn't work
// or lambdas
auto insert2 = [](auto& container, auto&&...args) -> decltype(auto)
{
return container.insert(std::forward<decltype(args)>(args)...);
};
test.call_me(insert2, test.mContainer.begin(), 5);
return 0;
}

Removing excess parameter pack parameters with minimal overhead

I want to make a function object that allows me to bind a function that has less parameters than the function object.
So for example:
int SomeFunction(int i) { return i * i; }
Function<int, int, float> function(&SomeFunction);
Calling function(5, 3.14f); would call the function with 5 as the parameter, and 3.14f would simply be ignored.
The goal is to create something similar to Qt's signal and slot mechanism in standard C++.
What kind of template magic can I use to achieve this?
You may use std::tuple and std::function:
template<typename Ret, typename... Ts>
class Function{
public:
template<typename...Us>
Function(Ret(*f)(Us...)) : Function(std::index_sequence_for<Us...>{}, f) {}
template <typename ... Us>
Ret call(Us&&... args)
{
return mF(std::forward<Us>(args)...);
}
private:
template<std::size_t... Is, typename...Us>
Function(std::index_sequence<Is...>, Ret(*f)(Us...))
{
mF = [f](Ts... args)
{
return f(std::get<Is>(std::forward_as_tuple(args...))...);
};
}
private:
std::function<Ret(Ts...)> mF;
};
Live Demo