Friday, December 24, 2010

Append integer to string (c++)

In this blog article we'll be using operator overloading with strings to let us append integers to the end of them (something pretty useful which std::string doesn't do for us already! :/)

We will be using some code described in a previous article to do the actual conversion from int to string.

We will be overloading the "<<" operator so that we can append to a string, and we will use the "+" operator so that we return a copy of the result of appending a string.

So it will work as follows:

int main() {
string str0("hello");
string str1("hello");

cout << str0 << endl; // prints 'hello'
str0 << 123;
cout << str0 << endl; // prints 'hello123'

cout << str1 << endl; // prints 'hello'
str1 + 123; // does not modify str1 (effectively does nothing)
cout << str1 << endl; // prints 'hello'
cout << str1 + 123 << endl; // prints 'hello123'
return 0;
}


Note: The reason we're overloading '<<' instead of '+=', is because '+=' is not an operator that allows for global operator overloading. See this link for more details on that.

So how do we make the above code work?
We need to add the following above the 'main()' function:

template<typename T>
string toString(T t) {
stringstream s;
s << t;
return s.str();
}

string& operator<<(string& s, int i) {
return s.append(toString(i));
}

string operator+(string s, int i) {
return s.append(toString(i));
}


Using the above overloads, the code in the main() function compiles and prints what we wanted it to.

Notice that the '<<' operator overload takes a reference to a string as one of its parameters, this is done so that the actual string is modified. The '+' operator overload takes a copy of the string and then does the appending, so the original string is not modified.

We can extend the above code to work for floats, doubles, and any datatype that our toString() function accepts. One way we do that is to use templates on our operator overloads.

Here's the new code:

template<typename T>
string& operator<<(string& s, T i) {
return s.append(toString(i));
}

template<typename T>
string operator+(string s, T i) {
return s.append(toString(i));
}


Although the above code works and is cool, there is a downside to doing this.
It won't allow us to do stuff like: string("s") + "hello", anymore. If we try to do that the compiler will generate a ambiguity error because it doesn't know which overload to choose. The +(string, char) overload is already defined by standard strings and our above template operator overload also handles this case; so the compiler doesn't know which to use and generates an error.

Our solution then is to not use templates and just manually overload the operators for the specific types on the + operator, but to use templates for the << operator since the c++ standard doesn't overload the <<(string, char) operator.
This is the code I currently use in my projects:

template<typename T>
string& operator<<(string& s, T i) {
return s.append(toString(i));
}
string operator+(string s, int i) {
return s.append(toString(i));
}
string operator+(string s, float i) {
return s.append(toString(i));
}
string operator+(string s, double i) {
return s.append(toString(i));
}


So there you have it, you can now do stuff like:

double myDouble = 123.79;
string s("My double is ");
s << myDouble << ". My int is " << 12 << ".";
cout << s << endl; // prints 'My double is 123.79. My int is 12.'

// this prints the same thing as above but uses
// printf() and C-style strings (null-terminated char arrays)
printf("%s", s.c_str());

No comments:

Post a Comment