Search code examples
c++c++11variadic-functionsrvalue-reference

Transparent passing C++ variadic call parameters to ostream output operator


I have written myself the following function:

template <class Stream>
inline Stream& Print(Stream& in) { return in;}

template <class Stream, class Arg1, class... Args>
inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args)
{
    sout << arg1;
    return Print(sout, args...);
}

It should make it useful to replace code like:

cout << "This took " << ns << " seconds with " << np << " packets.\n";

with

Print(cout, "This took ", ns, " seconds with ", np, " packets.\n");

And everything works fine, except that this function doesn't "tolerate" some manipulators. What is funny, only some of them. If you replace, for example, " packets.\n" with " packets.", endl, it will no longer compile. Although hex or setw(20) is fine. Where is the problem?


Solution

  • std::endl is a function template.

    So it cannot be deduced as for overloaded functions.

    static_cast<std::ostream& (*)(std::ostream&)>(&std::endl)
    

    would select correct overload.

    using Manipulator = std::ostream& (*)(std::ostream&);
    
    Print(std::cout, "This took ", ns, " seconds with ", np, " packets.", Manipulator(std::endl));
    

    Demo

    Since C++14, you might even not hard code type of stream with an helper:

    template <typename T>
    struct ManipulatorImpl
    {
        ManipulatorImpl(T t) : t(t) {}
    
        T t;
    };
    
    template <typename T>
    std::ostream& operator << (std::ostream& os, ManipulatorImpl<T> m)
    {
        return m.t(os);
    }
    
    template <typename T>
    ManipulatorImpl<T> make_ManipulatorImpl(T t) { return {t}; }
    
    #define Manipulator(name) make_ManipulatorImpl([](auto& os) -> decltype((name)(os)) { return (name)(os); })
    

    Demo