For large objects it is often just plain more efficient to write directly to a stream, e.g. output, or write to disk, than it is to write first to a string and then print it.
But it is often nice to be able to take the string representation, and then manipulate it.
I.e. sometimes you want to write to a stream. Sometimes to a string. And, yes, I know about ostringstream. Also ostrstream. (The ostringstream/ostrstream thrashing in the C++ standard s one reason why this issue was not resolved long ago.)
Also... sometimes the streams << notation is easier to use than the string concatenation operarir +. Especially since << often has implicit operators to convert to numerucs etc. to string and then print, whereas you usually don't want to overload operator+(string,T) because that can cause bugs.
---
All of that boils down to: should you provide:
string& to_string(const T& val);
or
std::ostream& operator(ostream& ostr, const T& val);
With the additional caveat that one is often misled by
string& T::to_string() const;
whereas
class T {
public: friend std::ostream& operator<<(ostream& ostr,const T& val);
}
is less misleading.
---
Here's a better way that accomplishes both ends:
class T {
public:
class Formatter {
const T& m_val;
XXX m_extra_stuff; // ...extra paramters, like format directives
public:
friend::ostream& operator<<(std::stream&, Formatter);
Formatter(const T& arg_val, XXX arg_extra_stuff = default)
: m_val(arg_val), m_extra_stuff(arg_extra_stuf)
{}
};
Used
std::cout << Formatter(t_object) << "\n";
Or the like.
It's a little bit clunkier to get a string, not quite as elegant as
std::string s = t_object->to_string();
Something like:
std::string s;
std::ostringstream(s) << Formatter(t_object) << "\n";
But perhaps some creative overloading will allow
std::string s = Formatter(t_object) << "\n";
or similar.
---
Q: who do I give credit to?
---
For older C-style programming: print to stream, versus to string? Same issue, but the C++ streams syntax makes it more pleasant.
To String
class Foo {
public:
unsigned bazz;
public:
class Formatter {
private:
const Foo& m_objref;
public:
Formatter(const Foo& arg_objref) : m_objref(arg_objref) {}
friend std::ostream& operator<<(std::ostream& ostr, const Formatter f) {
ostr << std::hex;
ostr << f.m_objref.bazz;
ostr << std::dec;
return ostr;
}
operator std::string() const {
std::ostringstream os;
os << *this;
return os.str();
}
friend std::string operator+(const std::string& s, const Formatter f)
{
return s + (std::string)f;
}
friend std::string operator+(const Formatter f, const std::string& s)
{
return (std::string)f + s;
}
friend std::string operator+(const Formatter f1, const Formatter f2)
{
return (std::string)f1 + (std::string)f2;
}
};
};
int main()
{
Foo a, b;
a.bazz = 0xAAA;
b.bazz = 0xBBB;
std::cout << "stream: " << Foo::Formatter(a) << Foo::Formatter(b) << "\n";
{
std::string s;
std::ostringstream sostr(s);
sostr << "ostringstream: " << Foo::Formatter(a) << Foo::Formatter(b) << "\n";
std::cout << sostr.str();
}
{
std::string s = Foo::Formatter(a);
std::cout << "string = f: " << s << std::endl;
}
#if 1
{
std::ostringstream s;
s << Foo::Formatter(a);
s << Foo::Formatter(b);
std::cout << "string; <<; <<: p="p" s="s" std::endl="std::endl"> }
{
std::string s = Foo::Formatter(a) + Foo::Formatter(b);
std::cout << "string = f + f: " << s << std::endl;
}
#endif
{
std::string s = Foo::Formatter(a) + Foo::Formatter(b);
std::cout << "string = f + f: " << s << std::endl;
}
{
std::string s = "string = s + f: " + Foo::Formatter(b);
std::cout << s << std::endl;
}
{
std::string s = Foo::Formatter(b) + ": string = f + s";
std::cout << s << std::endl;
}
}
This is painful enough that I would like top make it a mixin.