The Artima Developer Community
The C++ Source | C++ Community News | Discuss | Print | Email | First Page | Previous | Next
Sponsored Link

The C++ Source
Stream Thy Strings
by Bjorn Karlsson and Matthew Wilson
May 28, 2005

Page 1 of 3  >>

Advertisement

Summary
In this tutorial, Bjorn and Matthew show the proper use of 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.

A Look at ostringstream

In the C++ Standard Library there are a number of stream types; in fact, a separate portion of the Library is called IOStreams[1]. There is much more to be known about the library besides the well-known standard IOStream objects, cin, 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

Being Manipulative Isn't Always a Bad Thing

There are times when one needs more control over the formatting of the conversions. For example, you may need to convert an integer value to its hexadecimal string representation. This is where manipulators come into play. A manipulator (in the context of IOStreams) is a way of manipulating the behavior of the stream, for example to render output of floating point types in scientific notation. There are a number of predefined manipulators in IOStreams; we shall use the ones for selecting base. To use a manipulator, simply stream it like you would any other data. The following example shows how to switch between decimal, octal, and hexadecimal, output:
  #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].

Page 1 of 3  >>

The C++ Source | C++ Community News | Discuss | Print | Email | First Page | Previous | Next

Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us