I would like to overwrite the operator<< such that:
double d = 3.0;
mycustomstringstream << "Hello World " << d << "what a nice day.";
std::cout << mystream.str() << std::endl;
will produce the output:
Hello World (double)(3.00000000000)what a nice day.
Remark to avoid confusion: Every double shall be printed this way then, with a "(double)(" in front and a ")" behind, whenever a stream implementing my operator is used. The stream should come at least with all the functionality of string-stream, and I prefer to not have to fully re-implement the streamby myself. The purpose of the question is not for exercise but to save time/tediousness/errors from otherwise having to wrap a function call double_to_string around every streamed double d.
What I tried
Overriding
Since I would be fine for doubles to be printed like so, I boldly implemented:
std::ostream& operator<<(std::ostream& o, double const& d){
o<<"(double)("<< d << ")";
return o;
}
That does not work because the compiler explains about ambiguity (since that operator is already defined).
Inheriting
So as in the beginning, I am fine to inherit from std::stringstream and just replace the definition of that operator for my customstringstream:
#include <sstream>
class MyCustomStringStream: public std::stringstream{};
MyCustomStringStream& operator<<(MyCustomStringStream& o, double const& d){
o<<"(double)("<< ( (std::stringstream)(o) << d ) << ")";
return o;
}
Here is the error:
error: use of deleted function 'std::__cxx11::basic_stringstream<_CharT, _Traits, _Alloc>::basic_stringstream(const std::__cxx11::basic_stringstream<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'
So then instead:
#include <iostream>
#include <sstream>
class MyStringStream: public std::stringstream{
std::stringstream aux;
public:
MyStringStream& operator<<(double const& d){
aux.str() = "";
aux << std::scientific << "((double)(" << d << ")";
*this << aux.str();
return *this;
}
};
int main() {
double d = 12.3;
MyStringStream s;
s << "Hello World " << d << "what a nice day.";
std::cout << s.str() << std::endl;
}
But this still prints:
Hello World 12.3what a nice day.
Streams are designed to support this sort of customization, though they way they support it may not initially be as obvious as you'd like (and implementing it correctly is kind of a pain too).
When you write a number to a stream, the formatting is delegated to the stream's locale. That locale contains (among other things) a
num_putfacet, which is what actually formats a number as desired for the stream.The num_put facet has overloads of
do_putfor each of the standard numeric types, includingdouble. Since you (apparently) want to leave other types formatted normally, and apply your special formatting only todoubles, you override only thedo_putoverload fordouble.To format a number with this, you imbue your stream with a locale that contains an instance of that
num_putfacet:Then write a
doubleto the stream:For a proof of concept, your
do_putcan use a stringstream to convert thedoubleto a string, then copy the prefix, string, and suffix to the output sequence. Moving beyond proof of concept is a bit more work though.To format the number correctly, we need to get all the formatting information from the
ios_basethat's passed todo_put, and set the formatting for our temporary stringstream accordingly. For example:Figuring out everything that needs to be copied over to do all formatting correctly is left as an exercise for the interested reader. Here's a quick test program with semi-complete code as described above:
The result I get is about as you'd apparently hope/expect:
This should work with essentially any stream, not just a
stringstream. And once you've imbued a stream with this locale, you don't have to make any further changes to other code for alldoubles you write to that stream to be formatted this way.For the moment, I've left the
precisionandwidthapplying only to the width and precision used for the number itself, so when you write something out withstd::setw(10), the output will be 10 + the size of prefix and suffix you use. Depending on your viewpoint, it may well make more sense to subtract the size of your prefix+suffix from the width applied to the number itself, so the width will be the total field size, not just the size of the number part.So, while I'd still consider it a proof of concept rather than finished, fully-tested code, I'd say this does give a basic idea of an approach that should be able to accomplish your goal (and depending on how lofty a goal you've setting, may already be adequate).