|
|
|
Sponsored Link •
|
|
Advertisement
|
std::stringstream, and extol the virtues
of making your classes streamable.
Often, one finds oneself needing to convert arbitrary types to strings. Be it to present a message to a user, to write information to a log file, or to serialize data for subsequent deserialization, tools for string conversions are kept right at the front of the shed—and because they are used so frequently, these tools rarely tarnish. But perhaps one multipurpose power- tool would suffice?
In this article, we will take a look at how to
convert arbitrary types to strings using tools from the C++ Standard
Library. For ages, C++ programmers have resorted to unsafe conversions
from built-in types using printf. Although we know of
exactly two people who never make mistakes with printf (we
can give no names, you understand, but Bjorn is definitely not one
of them), the rest of the C++ world could use a safer alternative. One
such alternative is a Boost library called Boost.Format, which
offers type-safe formatting functionality, including formatting of
user-defined types. However, in this installment we shall not wander off
the premises of the C++ Standard Library.
ostringstreamcin, cout, cerr,
clog, and their wide-character companions, wcin,
wcout, wcerr, and wclog. Of
specific interest for this installment is std::ostringstream,
which is actually a typedeffor
basic_ostringstream<char>, and is a stream whose
underlying storage is in the form of a string (as opposed to an
(abstraction of an) input/output device). It is defined in the standard
header file called <sstream>, which you will need to
include in your applications. You use it like any other stream: you
stream things to it just as you would with std::cout. To
retrieve the resulting string (of type std::string), you call
the member function str(). Here's a simple, but complete,
example of how to use it:
#include <sstream>
#include <iostream>
int main()
{
std::ostringstream stm;
// Output an int
stm << 31;
// Output a char
stm << ' ';
// Output a double
stm << 9.87654;
// Retrieve the resulting string
std::cout << stm.str() << '\n';
}
First, we created an object of type std::ostringstream called
stm. Just like with other output streams, we were able to
output data to it - here, an int, followed by a
char, and finally a float. Extracting the
stored string is easy with the ostringstream member function
str()— in the example we simply printed that string to
std::cout.
For a number of applications, using ostringstreamthis way
works like a charm for the conversion problems one encounters. A more
practical example than what you just saw would be to mix numeric and text
data and store it in a string. Let's assume that you need to format a
string that describes the maximum and minimum values for the data type
float:
#include <sstream>
#include <limits>
#include <iostream>
int main()
{
std::ostringstream stm;
stm <<
"Maximum value for float: " <<
std::numeric_limits<float>::max() <<
'\n' << "Minimum value for float: " <<
std::numeric_limits<float>::min();
std::string values=stm.str();
}
In a series of output operations to stm, both text and
numeric data is written to the string values and is formatted
the way that the user chooses. (for example, values might
look like this when running the program:
Maximum value for float: 3.40282e+038 Minimum value for float: 1.17549e-038
#include <sstream>
#include <cassert>
#include <limits>
#include <iomanip>
int main()
{
std::ostringstream stm;
// Use a manipulator to select hexadecimal
// output of integer values
stm << std::hex;
// Output an int and a space
stm << 31 << ' '; // "1f "
// Use a manipulator to select octal output of integer values
stm << std::oct;
// Output an int and a space
stm << 31 << ' '; // "37 "
// Use a manipulator to select decimal output of integer values
stm << std::dec;
// Output an int and a space
stm << 31 << ' '; // "31 "
// Use a manipulator to select hexadecimal
// output of integer values,
// and make it uppercase!
stm << std::hex << std::uppercase << 31 << ' '; // "1F "
// To avoid confusion, show the base!
stm << std::showbase <<
std::hex << 31 << ' ' << // "0X1F"
std::oct << 31 << ' ' << // "037"
std::dec << 31; // "31"
// Retrieve the resulting string
assert(stm.str()=="1f 37 31 1F 0X1F 037 31");
}
The example demonstrates how the manipulators std::hex,
std::oct, and std::dec, are used. The
manipulators are provided for syntactical convenience; it's also possible
to achieve the effect of these manipulators by explicitly setting the
correct format flags for the stream. For example, the manipulator
std::hex performs the equivalent of this code that uses the
stream member function setf():
#include <sstream>
#include <cassert>
#include <limits>
int main()
{
std::ostringstream stm;
// Default output
stm << 31 << ' '; // "31 "
// Use a manipulator to select hexadecimal output
// of integer values
std::ios_base::fmtflags flags=
stm.setf(std::ios_base::hex,std::ios_base::basefield);
// Output an int
stm << 31; // "1f"
// Restore the formatting flags
stm.setf(flags,std::ios_base::basefield);
stm << ' ' << 31; // " 31"
assert(stm.str()=="31 1f 31");
}
As you can see, manipulators offer a syntactically convenient way of
manipulating the format flags of streams. Besides a number of predefined
manipulators in the IOStreams library, you can define your own
and have them work with any kind of stream. We've already seen
hex, oct, dec, and
uppercase; there are plenty more where those came from [2,3].
|
Sponsored Links
|