5

Consider the following (not working!) example:

#include <iostream>

template <typename type> void print(const type & item)
{
    std :: cout << item << std :: endl;
}

template <typename... types> void printall(const types & ... items)
{
    print(items)...;
}

int main()
{
    printall(1, 2, "hello");
}

Here I have a function print that simply prints out its argument, and a variadic function printall that accepts a pack of arguments. Now, what I would like to do is to simply have printall apply print to each element of the pack items. How can I get that done?

Note: I am not asking how to print a pack of values. I am aware of the existence of fold expressions and I know I could just throw the whole items in std::cout using them. Here print is just an example, could be any function.

How can I get that done? This sounds like something that should remarkably simple to do and yet I couldn't find any (reasonable) syntax to do it.

JeJo
  • 30,635
  • 6
  • 49
  • 88
Matteo Monti
  • 8,362
  • 19
  • 68
  • 114

1 Answers1

13

What I would like to do is to simply have printall apply print to each element of the pack items. How can I get that done?

Option - 1

As user @liliscent and user @max66 suggested in the comments, in C++11/ C++14 you can use the following hacky-way which act act like fold expressions in C++17.

SEE HERE

#include <iostream>

template <typename type> void print(const type& item)
{
    std::cout << item << '\n';
}

template <typename... types> 
void printall (const types&... items)
{
    using dummy = int[];
    (void)dummy { 0, (print(items), 0)... };
}

Option - 2

If the above does not look like good enough, provide a classical variadic templates overload as a wrapper/ helper between your printall() and print() functions, so that each template function arguments can be accessed in print().

SEE HERE

#include <iostream>

template <typename Type> void print(const Type& item)
{
    std::cout << item << '\n';  // print each argument
}

namespace helper 
{
    void printall() {}          // nontemplate overload for last call(i.e, no arguments call)

    template<typename FirstArg, typename... Types>
    void printall(const FirstArg& firstItem, Types&&... items)  
    {
        ::print(firstItem);                             // call print() for each argument
        helper::printall(std::forward<Types>(items)...);// calls the same wrapper::printalll()
    }
}

template <typename... Types> void printall(const Types& ... items)
{
    helper::printall(items...); // calls the wrapper::printall()
}

Option - 3

However, if you have access to the C++17, just use fold expressions. That provides a clean(non-hacky) and less amount of code.

SEE HERE

template <typename type> void print(const type& item)
{
    std::cout << item << '\n';
}

template <typename... types> void printall(const types&... items)
{
    (print(items),...);
}
JeJo
  • 30,635
  • 6
  • 49
  • 88
  • 3
    In C++11, a succinct way would be `using dummy = int[]; (void) dummy{(print(items), 0)...};`. – llllllllll Nov 13 '18 at 13:18
  • 1
    In C++17 you could simply write `template void printall (types const & ... items) { (std::cout << item << std::endl, ...); }` (also `'\n'` instead of `std::endl`, to avoid unnecessary flushing) – max66 Nov 13 '18 at 17:53
  • @max66 Yes that's exactly what OP do not want to have. He has mentioned it in the comments as **Note**. :) But for sure `'\n'`, which you will see in my first answer. I will add to the second too. ;) – JeJo Nov 13 '18 at 17:55
  • 1
    Sorry... not seen; anyway, in C++11/C++14 (as suggested by liliscent) you can try something as `template void printall (types const & ... items) { using dummy = int[]; (void)dummy { 0, (print(items), 0)... }; );` – max66 Nov 13 '18 at 17:59
  • @max66 Okay. That looks interesting... Couldn't understand when *liliscent* wrote that. Thanks for clearing the doubt. Let me try with that too. – JeJo Nov 13 '18 at 18:01
  • 1
    It's a common C++11/C++14 alternative to C++17 template folding (very useful in C++14 writing `constexpr` template functions). – max66 Nov 13 '18 at 18:04
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/183580/discussion-between-jejo-and-max66). – JeJo Nov 13 '18 at 18:04
  • Instead of `(void)dummy { 0, (print(items), 0)... };` you should use `(void)dummy { 0, ((void)print(items), 0)... };` in case `print` returns something funny which overloads the comma operator. – Henri Menke Nov 13 '18 at 20:23