PrevUpHomeNext

Basic Conversion-Failure Detection

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

boost::cnv::cstream cnv;

int i1 = lexical_cast<int>("123");              // Throws when conversion fails.
int i2 = convert<int>("123", cnv).value();      // Throws when conversion fails.
int i3 = convert<int>("uhm", cnv).value_or(-1); // Returns -1 when conversion fails.

BOOST_TEST(i1 == 123);
BOOST_TEST(i2 == 123);
BOOST_TEST(i3 == -1);

The above is translated to English as

The i1 and i2 deployments look sufficiently close and behave identically. Namely, with the user instructions silent about the conversion failure, those are treated as "exceptional" and throw.

The i3 specification, on the other hand, is explicit about conversion failures. The supplied fallback value is returned if the requested conversion fails.

That basic error detection and processing might be sufficient for a variety of conversion deployments. For example:

int i1 = convert<int>(s1, cnv(std::hex)).value_or(-1); // Read as hex
int i2 = convert<int>(s2, cnv(std::dec)).value_or(-1); // Read as decimal

if (i1 == -1) log("bad i1"), i1 = default_i1; // Log failure. Proceed with the default
if (i2 == -1) log("bad i2"), i2 = default_i2; // Log failure. Proceed with the default

// ... proceed

Or

int
fallback_fun(char const* msg, int fallback_value)
{
    // The principal advantage of a fallback_func over a fallback_value
    // is that the former is only called when the conversion request fails.
    // Consequently, the returned fallback_value is only calculated (which
    // potentially might be expensive) when it is absolutely necessary.
    log(msg); return fallback_value;
}

int i1 = convert<int>(s1).value_or_eval(std::bind(fallback_fun, "bad i1", default_i1));
int i2 = convert<int>(s2).value_or_eval(std::bind(fallback_fun, "bad i2", default_i2));
// ... proceed

Or, if we do not care about logging conversion failures:

int i1 = convert<int>(s1, cnv(std::hex)).value_or(default_i1); // If failed, proceed with the default
int i2 = convert<int>(s2, cnv(std::dec)).value_or(default_i2); // If failed, proceed with the default
// ... proceed

So far the deployment of boost::convert seems more flexible, more compact and natural (your mileage may vary) and potentially more efficient compared to boost::lexical_cast which achieves somewhat similar results with:

int i1 = default_i1;

try
{
    i1 = lexical_cast<int>(str);
}
catch (...)
{
    log("bad i1");
}

By design, this is boost::lexical_cast's only behavior -- straightforward and comprehensible, but limited. It makes quite a few legitimate process/program flows difficult and awkward to implement. Boost.Convert addresses that with additional functionality, flexibility and convenience.


PrevUpHomeNext