Here's a small C++ 'strprint' function that seems to mostly work:
#include <sstream>
#include <iostream>
// send_to_stream: send all arguments to the specified stream.
inline void send_to_stream(std::ostream &os) {}
template <typename ARG, typename... REST>
inline void send_to_stream(std::ostream &os, const ARG &arg, const REST & ... rest) {
os << arg;
send_to_stream(os, rest...);
}
// strprint: convert all arguments to a string by sending them to a stringstream.
template <typename... ARGS>
std::string strprint(const ARGS & ... args) {
std::ostringstream oss;
send_to_stream(oss, args...);
return oss.str();
}
int main(int argc, char **argv) {
std::string s1 = strprint("five:", 5, "\n");
std::cout << "S1=" << s1 << "." << std::endl;
// std::string s2 = strprint("five:", 5, std::endl);
// std::cout << "S2=" << s1 << "." << std::endl;
};
This program, when run, produces the output:
five:5
.
which is exactly what I would expect. But when I uncomment the 's2' lines, I get a compile error:
foo.cpp: In function ‘int main(int, char**)’:
foo.cpp:25:53: error: too many arguments to function ‘std::string strprint(const ARGS& ...) [with ARGS = {}; std::string = std::__cxx11::basic_string<char>]’
25 | std::string s2 = strprint("five:", 5, std::endl);
| ^
foo.cpp:15:13: note: declared here
15 | std::string strprint(const ARGS & ... args) {
| ^~~~~~~~
This makes no sense. Too many arguments to a function that takes an unlimited number of arguments? It doesn't seem to like passing 'std::endl' through a variadic list. Why not? What am I missing?
std::endl
is a function template:
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );
Admittedly, the error message is rather confusing. The problem is that the type of std::endl
cannot be deduced, because you would need to instantitate the function first:
std::string s2 = strprint("five:", 5, std::endl<std::ostream::char_type,std::ostream::traits_type>);
std::cout << "S2=" << s1 << "." << std::endl;
The usual
std::cout << std::endl;
works due to overload 20 here https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt. Generally all io manipulators are functions that take the stream as parameter. And generall all io manipulators that are only defined for input or output streams, respectively, are function templates.
Of course you do not want to explicitly provide the char_type
and traits_type
. As pointed out by Remy Lebeau, you can use a (non-capturing) lambda:
auto endl = [](std::ostream os&){ os << std::endl; };
std::string s2 = strprint("five:", 5, endl);
This will call overload 18 (from avove link).