The standard <sstream> library offers several advantages over the traditional <stdio.h> library of ANSI C, namely simplicity, type-safety, and extensibility. In this article, I'll show how to use the library effectively to perform safe and automatic type conversions.
If you’re used to <stdio.h> style conversions, you’re probably wondering why you should bother learning <sstream> based type conversions in the first place. Perhaps a quick reminder will convince you. Suppose you want to convert an int to a string using the sprintf() function. To accomplish this task correctly, you must ensure that the target buffer is large enough to contain the resulting string. You must also use the correct format flag. Using an incorrect flag will result in undefined behavior. Here’s an example:
sprintf (s, "%d", n); //s contains “10000\0”
So far so good. However, a slight change in the brittle code above and it breaks:
sprintf (s, "%f", n); //ouch! incorrect flag used
In this case, the programmer mistakenly used the %f format flag instead of %d. Consequently, s contains an indeterminate string after the sprintf() call. Wouldn’t it be better if the correct type were deduced automatically?
The compiler has enough information to determine which conversion is required, as the types of n and s are known at compile time. The standard classes declared in <sstream> take advantage of that and choose the desired conversion automatically. Furthermore, the conversion’s result is stored in a stringstream object’s internal buffer. So you don’t have to worry about buffer overflows either, since these objects automatically allocate storage as needed.
Does your compiler support <sstream>?
The <sstream> library was added to C++ relatively recently. (Don’t confuse <sstream> with the prestandard, deprecated <strstream> library of yore.) Therefore, older compilers, such as GCC 2.95, do not support it. If you happen to be using such a compiler and want to use <sstream>, be sure to upgrade it first.
The <sstream> library defines three classes: istringstream, ostringstream, and stringstream for stream input operations, stream output operations, and input and output stream operations, respectively. In addition, each of these classes has a matching wide-character version. To simplify matters, I will focus mostly on stringstream because each conversion involves both input and output operations. Listing A shows how to convert an int to a string using a stringstream object.
Notice that <sstream> allows you to use a string object rather than a char array. Consequently, you avoid the risk of a buffer overflow. Furthermore, the types of the inserted argument and the target object are deduced automatically, so there’s no danger of using an incorrect flag.
String to int conversion
To perform the opposite operation and convert a string to int, you simply reverse the insertion and extraction operations. For example:
string result= “10000”;
stream << result;
stream >> n; // n equals 10000
Recycling stringstream objects
If you intend to use the same stringstream object in multiple conversions, remember to call clear() before each conversion, as shown in Listing B.
The main advantage of recycling the same stringstream object in multiple conversions (rather than creating a new object each time) is performance. The construction and destruction of a stringstream object are costly in terms of CPU cycles.
Using templates in type conversions
You can easily define function templates that convert an arbitrary type to a certain target type. For example, if you need to convert various numeric values of types int, long, double, etc., to a string, use the to_string() function that takes a reference to a string and an arbitrary value t. The to_string() function converts t to a string and writes the result to it. Use the str() member function to obtain a copy of the stream's internal buffer:
template <class T>
void to_string(string & result, const T & t)
ostringstream oss; // create a stream
oss << t; // insert value to stream
result=oss.str(); // extract value and write it to result
This way, you can easily convert multiple numeric values to strings:
to_string(s1, 10.5); // double to string
to_string(s2, 123); // int to string
to_string(s3, true); // bool to string
You can take this one step further and define a generic conversion template that converts between arbitrary types. The function template convert() takes two template parameters, out_type and in_value, and converts in_value to out_type:
template <class out_type, class in_value>
out_type convert(const in_value & t)
stream << t; // insert value to stream
out_type result; // store conversion’s result here
stream >> result; // write value to result
You use convert() like this:
d=convert <double> (s); //d equals 12.56
salary=convert <string> (9000.0);//salary equals “9000”
Traditional <stdio.h> style conversions will remain with us a long time in legacy code and pure C applications. However, the type-safety and the overflow-free nature of stringstream-based conversions are compelling reasons to abandon it and switch to <sstream>, as I have shown. The <sstream> library offers another advantage—extensibility. You can overload to support conversions of user-defined types, as well. I'll discuss that feature in forthcoming articles.