Search code examples
c++c++11variadic-templates

Iterating a parameter pack with variadic templates in c++


As part of a logging library, I would like to be able to iterate a parameter pack, writing each value to a stream. My first attempt doesn't compile however. The first error is "error C2144: syntax error : 'int' should be preceded by '}'".

#include <sstream>
#include <ostream>
#include <iomanip>
#include <fstream>

template <typename ...Args>
std::ostream & Write(std::ostream & o, std::initializer_list<Args...> list) 
{
    size_t size = list.size();

    if(list.size() > 0)
    {
        for(size_t i = 0; i < (size - 1); i++)
            o << list[i] << ", ";

        o << list[i];
    }

    return o;
}

template<typename ...Args>
std::ostream & Write(std::ostream & o, Args...)
{
    return Write(o, { Args... });
}

int main(int argc, wchar_t * argv[])
{
    std::ostringstream o;

    Write(o, 1, "Hello", 2, "World", 3, 1.4857);

    // o should contain the string of characters "1, Hello, 2, World, 3, 1.4857"

    return 0;
}

How do I iterate each item in ... and send it to the stream?


Solution

  • Recursion is one option:

    template<typename Arg>
    std::ostream & Write(std::ostream & o, Arg&& arg) { 
        return o << std::forward<Arg>(arg); 
    }
    
    template<typename Arg, typename ...Args>
    std::ostream & Write(std::ostream & o, Arg&& arg, Args&&... args)
    {
        o << std::forward<Arg>(arg) << ", ";
        return Write(o, std::forward<Args>(args)...);
    }
    

    Demo.

    Alternatively, the pack expansion trick still works, with a little tweak - you need to special-case the first item in the list:

    template<typename Arg, typename ...Args>
    std::ostream & Write(std::ostream & o, Arg&& arg, Args&&... args)
    {
        o << std::forward<Arg>(arg);
    
        using expander = int[];
        (void) expander{ (o << ", " << std::forward<Args>(args), void(), 0)... };
    
        return o;
    }
    

    Demo.