Optional argument after template parameter pack of supposedly known length - c++

I am trying to have a kind of "invoke" function with an optional argument at the end:
template <typename... T>
void foo(void func(T...), T... args, int opt = 0)
{
func(args...);
}
void bar(int, int);
int main()
{
foo(&bar, 1, 2, 3);
}
I would have expected this to work, since the parameter pack can be deduced from the first argument, but clearly the compilers have different ideas:
Clang for example gives:
<source>:11:5: error: no matching function for call to 'foo'
foo(&bar, 1, 2, 3);
^~~
<source>:2:6: note: candidate template ignored: deduced packs of different lengths for parameter 'T' (<int, int> vs. <>)
void foo(void func(T...), T... args, int opt = 0)
^
1 errors generated.
Compiler returned: 1
Why is it deducing a list of length 0? Can I force it to ignore args for the purposes of deduction? Or more generally, how can I make this work?

You could make it overloaded instead of having an optional argument. You'd need to move the "optional" to before the parameter pack though.
The second overload would then just forward the arguments to the first, with the "default" parameter set.
#include <iostream>
template <typename... T>
void foo(void(func)(T...), int opt, T... args)
{
std::cout << opt << '\n';
func(args...);
}
template <typename... T>
void foo(void(func)(T...), T... args)
{
return foo(func, 0, args...); // forwards with the default set
}
void bar(int, int) {}
int main()
{
foo(&bar, 1, 2); // prints 0
foo(&bar, 3, 1, 2); // prints 3
}
You might want to move the optional all the way to the first position to let the function and its parameters be together. It's a matter of taste.
Another option could be to exclude the optional parameter and only have the parameter pack and to extract the optional if it's present or use the default value if it's not.
This requires that you restrict the signature of func to match the function you aim to call.
#include <iostream>
#include <tuple>
template <class... T>
void foo(void func(int, int), T&&... args) {
int opt = [](T... args) {
if constexpr (sizeof...(T) > 2) return std::get<2>(std::tuple{args...});
else return 0; // use the default
}(args...);
std::cout << opt << '\n';
[&func](int a, int b, auto&&...) { func(a, b); }(args...);
}
void bar(int, int) {}
int main() {
foo(&bar, 1, 2); // prints 0
foo(&bar, 1, 2, 3); // prints 3
}
Building on the second version but giving a lot more freedom, you could introduce a separate parameter pack for func. If that pack has the same size as pack of arguments supplied, you need to pick a default value for opt. If it on the other hand contains more arguments than needed for the function, you can select which one of the extra arguments that should be used for opt. In the example below, I just picked the first extra parameter.
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
// a helper to split a tuple in two:
template <class... T, size_t... L, size_t... R>
auto split_tuple(std::tuple<T...> t,
std::index_sequence<L...>,
std::index_sequence<R...>)
{
return std::pair{
std::forward_as_tuple(std::get<L>(t)...),
std::forward_as_tuple(std::get<R+sizeof...(L)>(t)...)
};
}
template <class... A, class... T>
void foo(void func(A...), T&&... args) {
static_assert(sizeof...(T) >= sizeof...(A));
// separate the needed function arguments from the rest:
auto[func_args, rest] =
split_tuple(std::forward_as_tuple(std::forward<T>(args)...),
std::make_index_sequence<sizeof...(A)>{},
std::make_index_sequence<sizeof...(T)-sizeof...(A)>{});
int opt = [](auto&& rest) {
// if `rest` contains anything, pick the first one for `opt`
if constexpr(sizeof...(T) > sizeof...(A)) return std::get<0>(rest);
else return 0; // otherwise return a default value
}(rest);
std::cout << opt << '\n';
std::apply(func, func_args);
}
void bar(int a, int b) {
std::cout << a << ',' << b << '\n';
}
int main() {
foo(&bar, 1, 2); // prints 0 then 1,2
foo(&bar, 1, 2, 3, 4); // prints 3 then 1,2
}

how can I make this work?
You can put the function arguments in a std::tuple, to make them distinct from your optional parameter.
C++17 provides std::apply to unpack the tuple parameters for you.
#include <tuple>
template <typename... T>
void foo(void func(T...), std::tuple<T...> args, int opt = 0)
{
std::apply( func, args );
}
void bar(int, int);
int main()
{
foo(&bar, {1, 2}, 3);
// args ^^^^^^
}

Related

Acces parameters in parameter pack in c++ [duplicate]

I am a little confused about how can I read each argument from the tuple by using variadic templates.
Consider this function:
template<class...A> int func(A...args){
int size = sizeof...(A);
.... }
I call it from the main file like:
func(1,10,100,1000);
Now, I don't know how I have to extend the body of func to be able to read each argument separately so that I can, for example, store the arguments in an array.
You have to provide overrides for the functions for consuming the first N (usually one) arguments.
void foo() {
// end condition argument pack is empty
}
template <class First, class... Rest>
void foo(First first, Rest... rest) {
// Do something with first
cout << first << endl;
foo(rest...); // Unpack the arguments for further treatment
}
When you unpack the variadic parameter it finds the next overload.
Example:
foo(42, true, 'a', "hello");
// Calls foo with First = int, and Rest = { bool, char, char* }
// foo(42, Rest = {true, 'a', "hello"}); // not the real syntax
Then next level down we expand the previous Rest and get:
foo(true, Rest = { 'a', "hello"}); // First = bool
And so on until Rest contains no members in which case unpacking it calls foo() (the overload with no arguments).
Storing the pack if different types
If you want to store the entire argument pack you can use an std::tuple
template <class... Pack>
void store_pack(Pack... p) {
std::tuple<Pack...> store( p... );
// do something with store
}
However this seems less useful.
Storing the pack if it's homogeneous
If all the values in the pack are the same type you can store them all like this:
vector<int> reverse(int i) {
vector<int> ret;
ret.push_back(i);
return ret;
}
template <class... R>
vector<int> reverse(int i, R... r) {
vector<int> ret = reverse(r...);
ret.push_back(i);
return ret;
}
int main() {
auto v = reverse(1, 2, 3, 4);
for_each(v.cbegin(), v.cend(),
[](int i ) {
std::cout << i << std::endl;
}
);
}
However this seems even less useful.
If the arguments are all of the same type, you could store the arguments in an array like this (using the type of the first argument for the array):
template <class T, class ...Args>
void foo(const T& first, const Args&... args)
{
T arr[sizeof...(args) + 1] = { first, args...};
}
int main()
{
foo(1);
foo(1, 10, 100, 1000);
}
If the types are different, I suppose you could use boost::any but then I don't see how you are going to find out outside of the given template, which item is of which type (how you are going to use the stored values).
Edit:
If the arguments are all of the same type and you want to store them into a STL container, you could rather use the std::initializer_list<T>. For example, Motti's example of storing values in reverse:
#include <vector>
#include <iostream>
#include <iterator>
template <class Iter>
std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
{
return std::reverse_iterator<Iter>(it);
}
template <class T>
std::vector<T> reverse(std::initializer_list<T> const & init)
{
return std::vector<T>(make_reverse_iterator(init.end()), make_reverse_iterator(init.begin()));
}
int main() {
auto v = reverse({1, 2, 3, 4});
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << std::endl;
}
}
For sticking into an array if the arguments have different types, you can use also std::common_type<>
template<class ...A> void func(A ...args){
typedef typename std::common_type<A...>::type common;
std::array<common, sizeof...(A)> a = {{ args... }};
}
So for example, func(std::string("Hello"), "folks") creates an array of std::string.
If you need to store arguments in the array you could use array of boost::any as follows:
template<typename... A> int func(const A&... args)
{
boost::any arr[sizeof...(A)] = { args... };
return 0;
}

Can I change the template argument deduction order for a generic variadic lambda?

Take the following code, which is a simplified example:
template <typename F>
void foo(F f) {
//bool some = is_variadic_v<F>; // Scenario #1
bool some = true; // Scenario #2
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
A function takes a generic variadic lambda and calls it with a fixed set of arguments. This lambda itself then just calls another function/lambda with a matching prototype.
As one could expect, in scenario 2, when f is called inside foo, the compiler will deduce params... to be the parameter pack {1, 1}.
For scenario #1, I am using a code from another Q&A to deduce the arity of a callable object. If however such an object is callable with more than a pre-defined maximum amount of arguments, it is considered "variadic". In detail, is_variadic_v will employ a form of expression SFINAE where it is attempted to call the function object with a decreasing number of arguments having an "arbitrary type" that is implictly convertible to anything.
The problem is now that apparently, the compiler will deduce F (and along its argument pack) during this metacode, and if it is variadic (such as in this case), it deduces F as a lambda taking the dummy arguments, i.e. something like main()::lambda(<arbitrary_type<0>, arbitrary_type<1>, arbitrary_type<2>, ..., arbitrary_type<N>>) if N is the "variadic limit" from above. Now params... is deduced as arbitrary_type<1>, arbitrary_type<2>, ... and correspondingly, the call some(params...) will fail.
This behaviour can be demonstrated in this little code example:
#include <utility>
#include <type_traits>
#include <iostream>
constexpr int max_arity = 12; // if a function takes more arguments than that, it will be considered variadic
struct variadic_type { };
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hence, to 'simulate' an arbitrary function signature.
template <auto>
struct arbitrary_type {
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&&();
template <typename T>
operator T&();
};
template <
typename F, auto ...Ints,
typename = decltype(std::declval<F>()(arbitrary_type<Ints>{ }...))
>
constexpr auto test_signature(std::index_sequence<Ints...> s) {
return std::integral_constant<int, size(s)>{ };
}
template <auto I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{ })) {
return { };
}
template <auto I, typename F, typename = std::enable_if_t<(I > 0)>>
constexpr auto arity_impl(...) {
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, auto MaxArity>
constexpr auto arity_impl() {
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity+1, F>(0);
if constexpr (tmp == MaxArity+1)
return variadic_type{ }; // if that works, F is considered variadic
else return tmp; // if not, tmp will be the correct arity of F
}
template <typename F, auto MaxArity = max_arity>
constexpr auto arity(F&&) { return arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, auto MaxArity = max_arity>
constexpr auto arity_v = arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, auto MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_type>;
template <typename F>
void foo(F f) {
bool some = is_variadic_v<F>;
//bool some = true;
f(int(some), int(some));
}
int main() {
auto some = [](int i, int j) {
std::cout << i << " " << j << '\n';
};
foo([&some](auto... params) {
some(params...);
});
}
Can I prevent this behaviour? Can I force the compiler to re-deduce the parameter list?
EDIT:
An additional peculiarity is that the compiler seems to act kind of schizophrenic. When I change the contents of foo to
foo([&some](auto... params) {
// int foo = std::index_sequence<sizeof...(params)>{ };
std::cout << sizeof...(params) << '\n';
});
the compiler will create a program that will print 2 in this example. If however I include the commented line (which, as it makes no sense, should trigger a compiler diagnostic), I get confronted with
error: cannot convert 'std::index_sequence<13>' {aka 'std::integer_sequence<long unsigned int, 13>'} to 'int' in initialization
85 | int foo = std::index_sequence<sizeof...(params)>{ };
so does the compiler now deduces sizeof...(params) to be 2 and 13 at the same time? Or did he change his mind and chooses now 13 just because I added another statement into the lambda? Compilation will also fail if I instead choose a static_assert(2 == sizeof...(params));. So the compiler deduces sizeof...(params) == 2, except if I ask him whether he did deduce 2, because then he didn't.
Apparently, it is very decisive for the parameter pack deduction what is written inside the lambda. Is it just me or does this behaviour really look pathologic?

Storing boost::function object with variable number of arguments

What I'm trying to achieve is creating a struct which stores any kind of method. I can later call struct_object.run() to run the method I've stored.
This method can return any kind of value and, most importantly, use any amount of parameters; however, I can't get around the "any amount of parameters" issue.
Mind you, the following code doesn't even build, mostly because I have no clue on what the correct syntax would be like.
ApplicationPair.h
template<typename T, typename... Args>
struct ApplicationPair
{
ApplicationPair(boost::function<T()> func, Args... arguments )
{
_func = func(Args::arguments...);
}
ApplicationPair() = delete;
void run();
boost::function<T(Args...)> _func;
};
#endif
And then, what I'd like to do is the following:
main.cpp
template<typename T, typename... Args>
void ApplicationPair<T,Args...>::run()
{
this->_func;
}
//TEST
int counter = 0;
void HelloWorld()
{
std::cout << "HelloWorld\n";
}
void printNumber(int i)
{
std::cout << "Print: " << i << std::endl;
}
void increaseCounter(int x)
{
counter+=x;
}
int main()
{
ApplicationPair<void> p1(HelloWorld);
ApplicationPair<void> p2(printNumber, 5);
ApplicationPair<void> p3(increaseCounter, 10);
p1.run();
p2.run();
p3.run();
return 0;
}
Basically, the methods I want to store shouldn't be modified or adapted in any way: I want to be able to create any kind of method without caring about the fact that struct ApplicationPair will store it for its own personal use.
All I get with this though is a long string of errors like:
error: in declaration ‘typename boost::enable_if_c<(! boost::is_integral::value), boost::function&>::type boost::function::operator=(Functor)’
In the below line:
ApplicationPair<void> p2(printNumber, 5);
you have to specify all types in template arguments list, not only void as return type, int as argument of constructor should also be added. Now args... is empty. What is wrong. The same with p3.
Make constructor as templated method taking paramters pack as argument for your callable:
template<class F, class ... Args>
ApplicationPair(F&& func, Args... arguments )
{
_func = boost::bind(std::forward<F>(func),arguments...);
}
then args... can be deduced when invoking constructor. Your class template takes only a type for return value.
template<class Ret>
struct ApplicationPair {
template<class F, class ... Args>
ApplicationPair(F&& func, Args... arguments )
{
_func = boost::bind(std::forward<F>(func),arguments...);
}
ApplicationPair() = delete;
void run() {
this->_func();
}
boost::function<Ret()> _func;
};
In constructor boost::bind is used to bind passed parameters to callable. You don't store parameters anywhere, therefore they must be bound in functor created by boost::bind.
Uses:
ApplicationPair<void> p1(HelloWorld);
ApplicationPair<void> p2(printNumber, 5);
ApplicationPair<void> p3(increaseCounter, 10);
Demo
Don't use boost::bind, it is limited to handle only max 9 arguments.
You've already gotten an answer but here's a C++17 alternative capable of deducing the return value type as well as the argument types of the function using a deduction guide, making both the return type and argument types part of the ApplicationPair<> type. I've chosen to store the arguments separately in a std::tuple<Args...>.
boost::function can be replaced with std::function in this example in case you later decide to go with the standard:
#include <boost/function.hpp>
#include <iostream>
#include <type_traits>
#include <tuple>
template<typename T, typename... Args>
struct ApplicationPair {
ApplicationPair() = delete;
ApplicationPair(Func func, Args... args) :
_func(func),
// store the arguments for later use
arguments(std::make_tuple(std::forward<Args>(args)...))
{}
decltype(auto) run() { // I'd rename this: decltype(auto) operator()()
return std::apply(_func, arguments);
}
boost::function<T(Args...)> _func;
std::tuple<Args...> arguments;
};
// deduction guide
template<typename Func, typename... Args>
ApplicationPair(Func, Args...) ->
ApplicationPair<std::invoke_result_t<Func, Args...>, Args...>;
int counter = 0;
void HelloWorld()
{
std::cout << "HelloWorld\n";
}
void printNumber(int i)
{
std::cout << "Print: " << i << std::endl;
}
int increaseCounter(int x) // changed return type for demo
{
counter+=x;
return counter;
}
int main()
{
// full deduction using the deduction guide
ApplicationPair p1(HelloWorld);
ApplicationPair p2(printNumber, 5);
ApplicationPair p3(increaseCounter, 10);
p1.run();
p2.run();
std::cout << p3.run() << '\n';
std::cout << p3.run() << '\n';
}

Default initialized (with value initialization) parameter pack

Can I default initialize a parameter pack to the respective value initialization of each type ?
To elaborate a bit more, take the example of a simple function template
template<typename T>
void f(T arg = T())
{
// eg for T=int, arg is 0 (value initialization) when default initialized
}
Would it be possible to express its variadic counterpart, ie
template<typename... Args>
void F(Args... args /* how can I value initialize the parameter pack? */)
{
}
#include <iostream>
#include <utility>
#include <tuple>
#include <cstddef>
#include <type_traits>
template <typename... Args>
void F(Args... args)
{
// target function, arbitrary body
using expander = int[];
(void)expander{ 0, (void(std::cout << args << " "), 0)... };
std::cout << std::endl;
}
template <typename... Args, typename... Params, std::size_t... Is>
void F(std::index_sequence<Is...>, Params&&... params)
{
F<Args...>(std::forward<Params>(params)...
, std::decay_t<typename std::tuple_element<sizeof...(Params) + Is, std::tuple<Args...>>::type>{}...);
}
template <typename... Args, typename... Params>
auto F(Params&&... params)
-> std::enable_if_t<(sizeof...(Args) > sizeof...(Params))>
{
F<Args...>(std::make_index_sequence<sizeof...(Args) - sizeof...(Params)>{}
, std::forward<Params>(params)...);
}
Tests:
#include <string>
int main()
{
// F(int, char, float = float{}, double = double{})
F<int, char, float, double>(1, 'c');
// F(int = int{}, char = char{}, float = float{}, double = double{})
F<int, char, float, double>();
// F(const std::string&, const std::string& = std::string{})
F<const std::string&, const std::string&>("foo");
// F(int, int, int)
F(1, 2, 3);
}
Output:
1 'c' 0 0
0 '\0' 0 0
"foo" ""
1 2 3
DEMO
You can create two parameter packs, one representing the types corresponding to function parameters and one representing "defaulted parameters."
template< typename ... aux, typename ... arg >
void fn( arg ... a ) {
std::tuple< aux ... > more {}; // The tuple elements are value-initialized.
}
http://coliru.stacked-crooked.com/a/1baac4b877dce4eb
There is no way to explicitly mention the deduced template parameters for this function. Anything inside the angle braces of the call will go into aux, not arg.
Note, the initialization you get with {} is value-initialization, not default-initialization. Objects of fundamental type get zeroed, not left uninitialized.
It`s explicitly forbidden by C++ standard, you cannot do such thing.
N3376 8.3.6/3
A default argument shall be specified only in the
parameter-declaration-clause of a function declaration or in a
template-parameter (14.1); in the latter case, the initializer-clause
shall be an assignment-expression. A default argument shall not be
specified for a parameter pack.

read arguments from variadic template

I am a little confused about how can I read each argument from the tuple by using variadic templates.
Consider this function:
template<class...A> int func(A...args){
int size = sizeof...(A);
.... }
I call it from the main file like:
func(1,10,100,1000);
Now, I don't know how I have to extend the body of func to be able to read each argument separately so that I can, for example, store the arguments in an array.
You have to provide overrides for the functions for consuming the first N (usually one) arguments.
void foo() {
// end condition argument pack is empty
}
template <class First, class... Rest>
void foo(First first, Rest... rest) {
// Do something with first
cout << first << endl;
foo(rest...); // Unpack the arguments for further treatment
}
When you unpack the variadic parameter it finds the next overload.
Example:
foo(42, true, 'a', "hello");
// Calls foo with First = int, and Rest = { bool, char, char* }
// foo(42, Rest = {true, 'a', "hello"}); // not the real syntax
Then next level down we expand the previous Rest and get:
foo(true, Rest = { 'a', "hello"}); // First = bool
And so on until Rest contains no members in which case unpacking it calls foo() (the overload with no arguments).
Storing the pack if different types
If you want to store the entire argument pack you can use an std::tuple
template <class... Pack>
void store_pack(Pack... p) {
std::tuple<Pack...> store( p... );
// do something with store
}
However this seems less useful.
Storing the pack if it's homogeneous
If all the values in the pack are the same type you can store them all like this:
vector<int> reverse(int i) {
vector<int> ret;
ret.push_back(i);
return ret;
}
template <class... R>
vector<int> reverse(int i, R... r) {
vector<int> ret = reverse(r...);
ret.push_back(i);
return ret;
}
int main() {
auto v = reverse(1, 2, 3, 4);
for_each(v.cbegin(), v.cend(),
[](int i ) {
std::cout << i << std::endl;
}
);
}
However this seems even less useful.
If the arguments are all of the same type, you could store the arguments in an array like this (using the type of the first argument for the array):
template <class T, class ...Args>
void foo(const T& first, const Args&... args)
{
T arr[sizeof...(args) + 1] = { first, args...};
}
int main()
{
foo(1);
foo(1, 10, 100, 1000);
}
If the types are different, I suppose you could use boost::any but then I don't see how you are going to find out outside of the given template, which item is of which type (how you are going to use the stored values).
Edit:
If the arguments are all of the same type and you want to store them into a STL container, you could rather use the std::initializer_list<T>. For example, Motti's example of storing values in reverse:
#include <vector>
#include <iostream>
#include <iterator>
template <class Iter>
std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
{
return std::reverse_iterator<Iter>(it);
}
template <class T>
std::vector<T> reverse(std::initializer_list<T> const & init)
{
return std::vector<T>(make_reverse_iterator(init.end()), make_reverse_iterator(init.begin()));
}
int main() {
auto v = reverse({1, 2, 3, 4});
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << std::endl;
}
}
For sticking into an array if the arguments have different types, you can use also std::common_type<>
template<class ...A> void func(A ...args){
typedef typename std::common_type<A...>::type common;
std::array<common, sizeof...(A)> a = {{ args... }};
}
So for example, func(std::string("Hello"), "folks") creates an array of std::string.
If you need to store arguments in the array you could use array of boost::any as follows:
template<typename... A> int func(const A&... args)
{
boost::any arr[sizeof...(A)] = { args... };
return 0;
}