Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

boost::cnv::stream Converter
PrevUpHomeNext

The purpose of the converter is to provide conversion-related formatting and locale support not available with boost::lexical_cast. Advantages of deploying a std::stream-based conversion engine are:

  • availability and maturity;
  • formatting and locale support;
  • familiar interface and deployment;
  • instant re-use of available standard manipulators (std::hex, std::setprecision, std::skipws, etc.);
  • extendibility via custom manipulators (see Stream Storage for Private Use: iword, pword, and xalloc by Rogue Wave Software).

The converter might be deployed as follows:

#include <boost/convert.hpp>
#include <boost/convert/stream.hpp>
#include <boost/detail/lightweight_test.hpp>

using std::string;
using boost::convert;

struct boost::cnv::by_default : boost::cnv::cstream {};

int    i2 = convert<int>("123").value();      // Throws when fails.
int    i3 = convert<int>("uhm").value_or(-1); // Returns -1 when fails.
string s2 = convert<string>(123).value();

BOOST_TEST(i2 == 123);
BOOST_TEST(i3 == -1);
BOOST_TEST(s2 == "123");

Formatting support is provided by the underlying std::stringstream. Consequently, the API heavily borrows formatting metaphors from this underlying component. One such metaphor is the manipulator represented by std::hex, std::dec, std::uppercase, std::scientific, etc.

The following code demonstrates how char and wchar_t strings can be read in the std::hex or std::dec format:

boost::cnv::cstream ccnv;
boost::cnv::wstream wcnv;

int v01 = convert<int>("  FF", ccnv(std::hex)(std::skipws)).value_or(0);
int v02 = convert<int>(L"  F", wcnv(std::hex)(std::skipws)).value_or(0);
int v03 = convert<int>("  FF", ccnv(std::dec)(std::skipws)).value_or(-5);
int v04 = convert<int>(L"  F", wcnv(std::dec)(std::skipws)).value_or(-5);

BOOST_TEST(v01 == 255); // "FF"
BOOST_TEST(v02 ==  15); // L"F"
BOOST_TEST(v03 ==  -5); // Failed to convert "FF" as decimal.
BOOST_TEST(v04 ==  -5); // Failed to convert L"F" as decimal.

For batch-processing it might be more efficient to configure the converter once:

ccnv(std::showbase)(std::uppercase)(std::hex);

BOOST_TEST(convert<string>(255, ccnv, "bad") == "0XFF");
BOOST_TEST(convert<string>( 15, ccnv, "bad") ==  "0XF");

An alternative (generic) formatting interface is currently being extended and explored:

namespace cnv = boost::cnv;
namespace arg = boost::cnv::parameter;

ccnv(arg::base = cnv::base::dec)
    (arg::uppercase = true)
    (arg::notation = cnv::notation::scientific);

is equivalent to the following std::manipulator-based variant:

ccnv(std::dec)(std::uppercase)(std::scientific);

using std::string;
using std::wstring;
using boost::convert;

The following example demonstrates the deployment of std::dec, std::oct std::hex manipulators:

boost::cnv::cstream ccnv;

BOOST_TEST(convert<int>( "11", ccnv(std::hex)).value_or(0) == 17); // 11(16) = 17(10)
BOOST_TEST(convert<int>( "11", ccnv(std::oct)).value_or(0) ==  9); // 11(8)  = 9(10)
BOOST_TEST(convert<int>( "11", ccnv(std::dec)).value_or(0) == 11);

BOOST_TEST(convert<string>( 18, ccnv(std::hex)).value_or("bad") == "12"); // 18(10) = 12(16)
BOOST_TEST(convert<string>( 10, ccnv(std::oct)).value_or("bad") == "12"); // 10(10) = 12(8)
BOOST_TEST(convert<string>( 12, ccnv(std::dec)).value_or("bad") == "12");
BOOST_TEST(convert<string>(255, ccnv(arg::base = boost::cnv::base::oct)).value_or("bad") == "377");
BOOST_TEST(convert<string>(255, ccnv(arg::base = boost::cnv::base::hex)).value_or("bad") ==  "ff");
BOOST_TEST(convert<string>(255, ccnv(arg::base = boost::cnv::base::dec)).value_or("bad") == "255");

ccnv(std::showbase);

BOOST_TEST(convert<string>(18, ccnv(std::hex)).value_or("bad") == "0x12");
BOOST_TEST(convert<string>(10, ccnv(std::oct)).value_or("bad") ==  "012");

ccnv(std::uppercase);

BOOST_TEST(convert<string>(18, ccnv(std::hex)).value_or("bad") == "0X12");

A more generic interface is also supported:

namespace cnv = boost::cnv;
namespace arg = boost::cnv::parameter;

BOOST_TEST(convert<int>("11", ccnv(arg::base = cnv::base::hex)).value_or(0) == 17);
BOOST_TEST(convert<int>("11", ccnv(arg::base = cnv::base::oct)).value_or(0) ==  9);
BOOST_TEST(convert<int>("11", ccnv(arg::base = cnv::base::dec)).value_or(0) == 11);

boost::cnv::cstream cnv;

boost::optional<string> s01 = convert<string>(12, cnv(std::setw(4)));
boost::optional<string> s02 = convert<string>(12, cnv(std::setw(5))(std::setfill('*')));
boost::optional<string> s03 = convert<string>(12, cnv(std::setw(5))(std::setfill('*'))(std::left));

BOOST_TEST(s01 && s01.value() == "  12");  // Field width = 4.
BOOST_TEST(s02 && s02.value() == "***12"); // Field width = 5, filler = '*'.
BOOST_TEST(s03 && s03.value() == "12***"); // Field width = 5, filler = '*', left adjustment

It needs to be remembered that boost::cnv::stream converter uses std::stream as its underlying conversion engine. Consequently, formatting-related behavior are driven by the std::stream. Namely, after every operation is performed, the default field width is restored. The values of the fill character and the adjustment remain unchanged until they are modified explicitly.

// The fill and adjustment remain '*' and 'left'.
boost::optional<string> s11 = convert<string>(12, cnv(arg::width = 4));
boost::optional<string> s12 = convert<string>(12, cnv(arg::width = 5)
                                                     (arg::fill = ' ')
                                                     (arg::adjust = cnv::adjust::right));

BOOST_TEST(s11 && s11.value() == "12**");  // Field width was set to 4.
BOOST_TEST(s12 && s12.value() == "   12"); // Field width was set to 5 with the ' ' filler.

using std::string;
using std::wstring;
using boost::convert;

    boost::cnv::cstream    ccnv;
    char const* const cstr_good = "  123";
    char const* const  cstr_bad = "  123 "; // std::skipws only affects leading spaces.

    ccnv(std::skipws);        // Ignore leading whitespaces
//  ccnv(arg::skipws = true); // Ignore leading whitespaces. Alternative interface

    BOOST_TEST(convert<int>(cstr_good, ccnv).value_or(0) == 123);
    BOOST_TEST(convert<string>("  123", ccnv).value_or("bad") == "123");

    BOOST_TEST(!convert<int>(cstr_bad, ccnv));

    ccnv(std::noskipws);       // Do not ignore leading whitespaces
//  ccnv(arg::skipws = false); // Do not ignore leading whitespaces. Alternative interface

    // All conversions fail.
    BOOST_TEST(!convert<int>(cstr_good, ccnv));
    BOOST_TEST(!convert<int>( cstr_bad, ccnv));

using std::string;
using std::wstring;
using boost::convert;

BOOST_TEST(convert<string>( true, cnv(std::boolalpha)).value_or("bad") ==  "true");
BOOST_TEST(convert<string>(false, cnv(std::boolalpha)).value_or("bad") == "false");

BOOST_TEST(convert<bool>( "true", cnv(std::boolalpha)).value_or(false) ==  true);
BOOST_TEST(convert<bool>("false", cnv(std::boolalpha)).value_or( true) == false);

BOOST_TEST(convert<string>( true, cnv(std::noboolalpha)).value_or("bad") == "1");
BOOST_TEST(convert<string>(false, cnv(std::noboolalpha)).value_or("bad") == "0");

BOOST_TEST(convert<bool>("1", cnv(std::noboolalpha)).value_or(false) ==  true);
BOOST_TEST(convert<bool>("0", cnv(std::noboolalpha)).value_or( true) == false);


PrevUpHomeNext