...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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:
std::hex
,
std::setprecision
, std::skipws
,
etc.);
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);