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


Non-throwing conversions

For the case where throwing exceptions is undesirable, Boost.JSON also provides a non-throwing version of value_to, the function template try_value_to. It returns result, a specialised variant that either holds a value or an error_code.

[Note] Note

There's no non-throwing equivalent for value_from. This is simply because we haven't yet encountered a situation where value_from needed to communicate an error to the caller.

The library provides non-throwing conversions for all the categories of types it supports with value_to. If a user wants to use it with custom types, an overload of tag_invoke of this form needs to be provided:

result_for<T, value>::type
tag_invoke( const try_value_to_tag< T >&, const value& );

For the class ip_address from the section Custom conversions this overload may look like this:

result_for< ip_address, value >::type
tag_invoke( const try_value_to_tag< ip_address >&, value const& jv )
    if( !jv.is_array() )
        return make_error_code( std::errc::invalid_argument );

    array const& arr = jv.get_array();
    if( arr.size() != 4 )
        return make_error_code( std::errc::invalid_argument );

    boost::system::result< unsigned char > oct1
        = try_value_to< unsigned char >( arr[0] );
    if( !oct1 )
        return make_error_code( std::errc::invalid_argument );

    boost::system::result< unsigned char > oct2
        = try_value_to< unsigned char >( arr[1] );
    if( !oct2 )
        return make_error_code( std::errc::invalid_argument );

    boost::system::result< unsigned char > oct3
        = try_value_to< unsigned char >( arr[2] );
    if( !oct3 )
        return make_error_code( std::errc::invalid_argument );

    boost::system::result< unsigned char > oct4
        = try_value_to< unsigned char >( arr[3] );
    if( !oct4 )
        return make_error_code( std::errc::invalid_argument );

    return ip_address{ *oct1, *oct2, *oct3, *oct4 };

The overload lets us use ip_address with try_value_to.

value jv = parse( R"([127,0,0,12])" );

boost::system::result< ip_address > addr
    = try_value_to< ip_address >( jv );
assert( addr.has_value() );

ip_address addr2{ 127, 0, 0, 12 };
    addr->begin(), addr->end(), addr2.begin() ));

// this fails without exception
addr = try_value_to< ip_address >( value() );
assert( addr.has_error() );

If try_value_to is used with a type, for which there's no tag_invoke overload of the form described in this section, but there is one of the form intended for value_to, then the library still tries to perform the conversion. It uses the throwing overload, and attempts to convert any thrown exception into an error_code. Note, though, that such approach will likely be slower then a dedicated overload.

The opposite is also true: if there's a tag_invoke overload intended for try_value_to, but not for value_to, then calling value_to will invoke the non-throwing overload, then construct a system_error from the error_code and throw it. Due to these fallbacks, it is recommended that users provide the overload from this section, rather then the other one, if they ever intend to use try_value_to.