Let us suppose a function that must receive a random number of values to display them on the screen, separating said values with a certain separator.
The idea is to take advantage of the features of the new C++ standards, so the solution should take advantage of variadic templates .
The first attempt is promising, the template is able to print a sequence of numbers:
template <typename... Args>
void print(Args... args)
{
(std::cout << ... << args) << '\n';
}
int main()
{
print(1,2,3,4,5);
}
Now I try to insert the separator and that's when the problems start:
template <typename... Args>
void print(Args... args)
{
(std::cout << ... << ',' << args) << '\n';
}
Bug details:
prog.cc:8:29: error: expression not permitted as operand of fold expression
(std::cout << ... << ',' << args) << '\n';
~~~~^~~~~~~
( )
How could the separator be added to the sequence? The idea is to obtain a sequence such that:
1,2,3,...,N
Here are some other ways to solve this problem, although they are not the only ones:
template overload
By default the variadic version of the template will be called and the non- varidic version will only be called when there is only one element left to print.
The variadic version , being called whenever there are 2 or more elements, can safely assume that it must insert a separator after the first element.
Using lambdas (v1)
A stateful lambda is ideal for controlling separator printing.
When exploiting the arguments, numerous calls to the lambda will be made, which will print a separator in each of them except the first.
Using lambdas (v2)
Identical to the previous version, it only changes the declaration of the boolean variable, which becomes a property of the template to avoid a warning at compile time.
Using if constexpr
Since the compiler already knows the exact number of arguments that the function receives, we can make use of
if constexpr
so that a recursive call is made only while there is more than one element to print. This code could be understood as an improvement of the first example.I had an answer on point just before the edit where you make it clear that the expected output is
1,2,3,...,N
and not1,2,3,...,N,
as I originally thought. For the first case it can be valid simply:By not wanting the comma after the last element, things get complicated...
I don't know if it will be the best option, but it can be proposed in the following way, modifying my initial idea:
We print the first argument and then use a conditional to see if there is an argument
args
to unpack into or not.Departure:
Test online at Ideone
I think you can expand the sequence that receives the template to another function to which you pass the args
TestIdeone
The explanation of "Expansion Pack" packages, as I understand it, can only be expanded in contexts of expansion of the same "if perhaps it is a bit confusing, I am sorry I cannot explain myself better" to quote some Braced initialization or this one on which the initializations example is based of array.
info some info:
but it shows how you are using it, I don't know if that part will be useful for you, but in the package definitions "pack" "init" maybe it will help you.
I think this works as you want.
TestIdeone
After your update if it is without the line break use:
changing
std::cout << ',' << '\n'
bystd::cout << ','
Continuing with the previous approach, it could be simplified as follows and dispensing with the
print_test
TestIdeone
Note: maybe it's a botch and maybe there is a better way, but I was curious after your comment, and this is what came out, I hope it helps you.