1

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?

Ethouris
  • 1,791
  • 13
  • 18
  • Related: https://stackoverflow.com/q/24377892 – Ch3steR Mar 10 '22 at 10:44
  • You should use perfect forwarding in the recursive call `return Print(sout, std::forward(args)...);` out of a habit. It's not a big deal in this case, though. – j6t Mar 10 '22 at 10:58

2 Answers2

2

I can't explain the exact reason, but if I do something like this it doesn't throw an error:

endl<char, char_traits<char>>

My guess is that it doesn't seem to be able to do template type parameter inference.
The ostream is:

using ostream = basic_ostream<char, char_traits<char>>;
전민준
  • 51
  • 4
2

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

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Although I don't really like the solution with a pp macro, at least it gives me an idea to fix it. I checked also what happens, if I put `isprint` instead of `endl` and yes, it results with the same error. Thanks a log! – Ethouris Mar 11 '22 at 14:18
  • 1
    Unfortunately, when manipulating **name**, MACRO is the only way. (Fortunately, we don't have to manipulate name so often). – Jarod42 Mar 11 '22 at 14:44
  • Yeah, saw that. That's why I was also attempting to resolve it by overloading the comma operator ;) – Ethouris Mar 16 '22 at 08:46