| Home | Libraries | People | FAQ | More

## Better Error Detection

> "Detection is, or ought to be, an exact science, ..." Sir Arthur Conan Doyle

    int i2 = convert<int>("not an int", cnv).value_or(-1); // after the call i2==-1

    if (i2 == -1) process_failure();

The code above is straightforward and self-explanatory but, unfortunately, strictly speaking, is not entirely deterministic as -1 might be the result of a conversion failure or the successful conversion of the "-1" string. Still, in reality "spare" values are quite often available outside the valid/sensible range to indicate conversion failures. If so, such simple deployment might be adequate without introducing non-deterministic behavior. Alternatively, it might be not that uncommon to ignore conversion failures altogether and to simply log the failure and to proceed with the supplied fallback value.

Applications outside these mentioned categories still require conversion failure reliably detected and processed accordingly. The `boost::lexical_cast`'s (only) answer is to throw on failure and Boost.Convert supports that behavior as well:

    try
    {
        int i1 = lexical_cast<int>(str);         // Throws if the conversion fails.
        int i2 = convert<int>(str, cnv).value(); // Throws if the conversion fails.
    }
    catch (...)
    {
        process_failure();
    }

However, to cater for a wider range of program-flow variations, Boost.Convert adds the flexibility of

- delaying the moment when the conversion-failure exception is actually thrown or
- avoiding the exception altogether.

    optional<int> r1 = convert<int>(str1, cnv); // Does not throw on conversion failure.
    optional<int> r2 = convert<int>(str2, cnv); // Does not throw on conversion failure.
    // ...
    try // Delayed processing of potential exceptions.
    {
        int i1 = r1.value(); // Will throw if conversion failed.
        int i2 = r2.value(); // Will throw if conversion failed.
    }
    catch (boost::bad_optional_access const&)
    {
        // Handle failed conversion.
    }

    // Exceptions are avoided altogether.
    int i1 = r1 ? r1.value() : fallback_value;
    int i2 = r2.value_or(fallback_value);
    int i3 = convert<int>(str3, cnv).value_or(fallback_value);
    int i4 = convert<int>(str3, cnv).value_or_eval(fallback_function);

Now `boost::optional` steps forward as the actual type returned by `boost::convert()` which until now we avoided by immediately calling its value-accessor methods:

    int i1 = boost::convert<int>(str1, cnv).value();
    int i2 = boost::convert<int>(str2, cnv).value_or(fallback_value);
    int i3 = boost::convert<int>(str3, cnv).value_or_eval(fallback_function);

| Note
The potential advantage of `value_or_eval(fallback_function)` over `value_or(fallback_value)` is that `fallback_function` is only called when/if conversion fails, i.e. the `fallback_value` (ultimately returned by `fallback_function`) is only calculated (which mght be expensive) when needed.

From the user perspective, `boost::lexical_cast` processes failure in a somewhat one-dimensional non-negotiable manner. `boost::convert` takes a more flexible approach, provides choice and leaves the decision to the user. It probably is not unimaginable that, on the library level, propagating the conversion-failure exception might be the only sensible response. On the application level though, in my personal experience, the choice has overwhelmingly been to handle conversion failures locally, i.e. avoiding conversion-failure exception propagation or, better still, avoiding exceptions altogether with program flows similar to:

    boost::optional<int> res = boost::convert<int>(str, cnv);

    if (!res) log("str conversion failed!");

    int i1 = res.value_or(fallback_value);

    // ...proceed

and

    struct fallback_func
    {
        int operator()() const { log("Failed to convert"); return 42; }
    };

    // Fallback function is called when failed
    int i2 = convert<int>(str, cnv).value_or_eval(fallback_func());
    int i3 = convert<int>(str, cnv, fallback_func());