The generic form that works with std::wostream
as well:
template <typename CharT, typename Traits>
std::basic_ostream<CharT, Traits> &
Print(std::basic_ostream<CharT, Traits> &out)
{
return out;
}
template <typename CharT, typename Traits, typename T>
std::basic_ostream<CharT, Traits> &
Print(std::basic_ostream<CharT, Traits> &out, T &&t)
{
return (out << std::forward<T>(t));
}
template <typename CharT, typename Traits, typename T, typename... Args>
std::basic_ostream<CharT, Traits> &
Print(std::basic_ostream<CharT, Traits> &out, T &&t, Args &&...args)
{
return Print( Print(out, std::forward<T>(t)), std::forward<Args>(args)... );
}
I couldn't make it work with with std::endl
in the common case (it is possible to handle std::endl
in specific cases like when it is the first or the last argument, but not in the common case, especially if there are multiple std::endl
in a single call). You can still use '\n'
instead or use std::endl
with template arguments specified if you really need std::endl
:
Print(std::cout, "hello world", std::endl<char, std::char_traits<char>>);
The differences between std::endl
and '\n'
- If the stream is working in binary mode then
'\n'
isn't converted to the line ending format of the platform the code compiled for (but in text mode it is still converted).
'\n'
doesn't flush the stream with std::flush
(but it still flushes the std::cout
if the program is running on a terminal)
So for me it is OK to use '\n'
, or maybe even preferred.
Using some other IO manipulators is still possible:
Print(std::cout, std::hex, 11, '\n');
I also implemented sprintf
counterpart that works with variadic templates and returns std::string
:
template <typename CharT = char, typename Traits = std::char_traits<CharT>, typename... Args>
std::basic_string<CharT, Traits>
SPrint(Args &&...args)
{
std::basic_stringstream<CharT, Traits> ss;
Print(ss, std::forward<Args>(args)...);
return std::move(ss.str());
}
Here is some demos.