operator<< and toString

A

Andrea Crotti

I have many classes where I overloaded operator<< to get an easier
output...
But now I'm in the situation that I want to get a string representing
the object, so it doesn't work very well.

Should I rewrite everything in the form

string toString() {
return string_representation;
}

and then print with
cout << obj.toString()

or is there an even smarter way (for example if "cout" would always call
"toString()" on the object if available)?
 
A

AnonMail2005

I have many classes where I overloaded operator<< to get an easier
output...
But now I'm in the situation that I want to get a string representing
the object, so it doesn't work very well.

Should I rewrite everything in the form

string toString() {
       return string_representation;

}

and then print with
cout << obj.toString()

or is there an even smarter way (for example if "cout" would always call
"toString()" on the object if available)?

What you can do is implement one function in terms of the other.

I suggest you implement toString in terms of operator<<. Something
like this:
std::string toString() const
{
std::eek:stringstream oss;
oss << *this;
return oss.str();
}

I think this will be more efficient space-wise as opposed to
implementing operator<< in terms of toString since there won't be a
string created if you're outputting to, say a file or the console.

HTH
 
S

Stuart Golodetz

I have many classes where I overloaded operator<< to get an easier
output...
But now I'm in the situation that I want to get a string representing
the object, so it doesn't work very well.

Should I rewrite everything in the form

string toString() {
return string_representation;
}

and then print with
cout<< obj.toString()

or is there an even smarter way (for example if "cout" would always call
"toString()" on the object if available)?

You can just write operator<< normally and then use Boost's lexical_cast
if you want to do a conversion to string (lexical_cast makes use of the
operator<< you provide internally):

#include <iostream>
#include <string>

#include <boost/lexical_cast.hpp>

struct X
{
int i;

X(int i_) : i(i_) {}
};

std::eek:stream& operator<<(std::eek:stream& os, const X& x)
{
os << x.i;
return os;
}

int main()
{
std::string s = boost::lexical_cast<std::string>(X(23));
std::cout << s << '\n';
return 0;
}

That might be particularly helpful if you've already implemented
operator<< for a lot of classes, because it means you don't have to
rewrite anything.

Cheers,
Stu
 
G

Gert-Jan de Vos

I have many classes where I overloaded operator<< to get an easier
output...
But now I'm in the situation that I want to get a string representing
the object, so it doesn't work very well.

Should I rewrite everything in the form

string toString() {
       return string_representation;

}

and then print with
cout << obj.toString()

or is there an even smarter way (for example if "cout" would always call
"toString()" on the object if available)?

operator<<()is the idiomatic C++ way for a class to support output
streams. Keep it and you can add a free toString function:

template <typename T>
std::string toString(const T& t)
{
std::eek:stringstream os;
os << t;
return os.str();
}

void test()
{
std::string msg("Number ");
msg += toString(1); // msg = "Number 1"
}
 
A

Andrea Crotti

Gert-Jan de Vos said:
operator<<()is the idiomatic C++ way for a class to support output
streams. Keep it and you can add a free toString function:

template <typename T>
std::string toString(const T& t)
{
std::eek:stringstream os;
os << t;
return os.str();
}

void test()
{
std::string msg("Number ");
msg += toString(1); // msg = "Number 1"
}

Nice I like this solutioon thanks!
Those are the things that make me happy that I'm not using freaking java
:D (and will not use it hopefully for the rest of the life).

Another possibility would be maybe have a

class Printable {
virtual string toString() {
....
}
};

but it'a annoying to add another parent class to all the classes, if I
can avoid maybe it's better...
 
A

Andrea Crotti

By the way I'm using the template solution in the end and I have another
small question.

I want to print out vectors in the form "[x,y,z]"

And I use this code

template <typename T>
std::string vectorToString(const std::vector<T>& inp) {
// TODO: should be possible to be written better
std::string empty("[]");
if (inp.size() == 0)
return empty;

std::eek:stringstream os;
os << "[";
size_t i;
for (i=0; i < (inp.size()-1); ++i) {
os << inp << ",";
}
os << inp << "]";
return os.str();
}

but I don't like it that much.
Is there some way to write something like I would do in python

"[" + ",".join(vec) + "]"
for example?
 
G

Gert-Jan de Vos

By the way I'm using the template solution in the end and I have another
small question.

I want to print out vectors in the form "[x,y,z]"

And I use this code

    template <typename T>
    std::string vectorToString(const std::vector<T>& inp) {
        // TODO: should be possible to be written better
        std::string empty("[]");
        if (inp.size() == 0)
            return empty;

        std::eek:stringstream os;
        os << "[";
        size_t i;
        for (i=0; i < (inp.size()-1); ++i) {
            os << inp << ",";
        }
        os << inp << "]";
        return os.str();
    }

but I don't like it that much.
Is there some way to write something like I would do in python


Again following a two-step approach:

template <typename It>
std::eek:stream& sequenceToStream(It begin, It end, std::eek:stream& os)
{
os << "[";
if (begin != end)
std::copy(begin, end, std::eek:stream_iterator(os, ","));
os << "]";
}

template <typename T>
std::string toString(const std::vector<T>& v)
{
std::eek:stringstream os;
sequenceToStream(v.begin(), v.end(), os);
return os.str();
}

Gert-Jan
 
J

Jeff Flinn

Gert-Jan de Vos said:
By the way I'm using the template solution in the end and I have another
small question.

I want to print out vectors in the form "[x,y,z]"

And I use this code

template <typename T>
std::string vectorToString(const std::vector<T>& inp) {
// TODO: should be possible to be written better
std::string empty("[]");
if (inp.size() == 0)
return empty;

std::eek:stringstream os;
os << "[";
size_t i;
for (i=0; i < (inp.size()-1); ++i) {
os << inp << ",";
}
os << inp << "]";
return os.str();
}

but I don't like it that much.
Is there some way to write something like I would do in python


Again following a two-step approach:

template <typename It>
std::eek:stream& sequenceToStream(It begin, It end, std::eek:stream& os)
{
os << "[";
if (begin != end)
std::copy(begin, end, std::eek:stream_iterator(os, ","));
os << "]";
}


The if (begin != end) is redundant and unneeded, the following give the
same result:
> template <typename It>
> std::eek:stream& sequenceToStream(It begin, It end, std::eek:stream& os)
> {
> os << "[";
> std::copy(begin, end, std::eek:stream_iterator(os, ","));
> os << "]";
> }

Note that in either case you will get the trailing ',':

[x,y,z,]

Jeff
 
G

Gert-Jan de Vos

Gert-Jan de Vos said:
By the way I'm using the template solution in the end and I have another
small question.
I want to print out vectors in the form "[x,y,z]"
And I use this code
    template <typename T>
    std::string vectorToString(const std::vector<T>& inp) {
        // TODO: should be possible to be written better
        std::string empty("[]");
        if (inp.size() == 0)
            return empty;
        std::eek:stringstream os;
        os << "[";
        size_t i;
        for (i=0; i < (inp.size()-1); ++i) {
            os << inp << ",";
        }
        os << inp << "]";
        return os.str();
    }
but I don't like it that much.
Is there some way to write something like I would do in python

Again following a two-step approach:
template <typename It>
std::eek:stream& sequenceToStream(It begin, It end, std::eek:stream& os)
{
   os << "[";
   if (begin != end)
           std::copy(begin, end, std::eek:stream_iterator(os, ","));
   os << "]";
}

The if (begin != end) is redundant and unneeded, the following give the
same result:

 > template <typename It>
 > std::eek:stream& sequenceToStream(It begin, It end, std::eek:stream& os)
 > {
 >   os << "[";
 >   std::copy(begin, end, std::eek:stream_iterator(os, ","));
 >   os << "]";
 > }

Note that in either case you will get the trailing ',':

    [x,y,z,]

Jeff


You are right of course. I meant to suppress the trailing ',' like
this:

template <typename It>
std::eek:stream& sequenceToStream(It begin, It end, std::eek:stream& os)
{
os << "[";
if (begin != end)
{
os << *begin;
while (++begin != end)
os << ',' << *begin;
}
os << "]";
}
 
J

James Kanze

By the way I'm using the template solution in the end and I have another
small question.
I want to print out vectors in the form "[x,y,z]"
And I use this code
template <typename T>
std::string vectorToString(const std::vector<T>& inp) {
// TODO: should be possible to be written better
std::string empty("[]");
if (inp.size() == 0)
return empty;
std::eek:stringstream os;
os << "[";
size_t i;
for (i=0; i < (inp.size()-1); ++i) {
os << inp << ",";
}
os << inp << "]";
return os.str();
}

but I don't like it that much.
Is there some way to write something like I would do in python
"[" + ",".join(vec) + "]"
for example?

What's wrong with the standard solution:
for (typename std::vector<T>::const_iterator iter = inp.begin();
iter != inp.end();
++ iter) {
if (iter != inp.begin()) {
os << ", ";
}
os << *iter;
}
This is a common pattern -- more often used when you need to
introduce line breaks (with an additional inLineCount variable),
but applicable generally whenever you need separators, etc.

Another alternative that I've used is:
std::string separator = "";
for (typename std::vector<T>::const_iterator iter = inp.begin();
iter != inp.end();
++ iter) {
os << separator << *iter;
separator = ", ";
}
In your case, this can be extended as:
std::string prefix = "[";
for (typename std::vector<T>::const_iterator iter = inp.begin();
iter != inp.end();
++ iter) {
os << prefix << *iter;
prefix = ", ";
}
os << "]";

(I generally prefer the first alternative, however. It offers
no end of possibilities with regards to adjusting separators
according to context.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top