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

This is the documentation for a snapshot of the master branch, built from commit 68cc668162.
PrevUpHomeNext

Avoiding physical dependency

Some users, particularly library authors, may wish to provide conversions between their types and value, but at the same time would prefer to avoid having their library depend on Boost.JSON. This is possible to achieve with the help of a few forward declarations.

namespace boost {
namespace json {

class value;

struct value_from_tag;

template< class T >
struct try_value_to_tag;

template< class T1, class T2 >
struct result_for;

template< class T >
void value_from( T&& t, value& jv );

template< class T >
typename result_for< T, value >::type
try_value_to( const value& jv );

}
}

Note that value_from is declared using an out-parameter, rather then returning its result. This overload is specifically designed for this use-case.

After that the definitions of tag_invoke overloads should be provided. These overloads have to be templates, since value is only forward-declared and hence is an incomplete type.

namespace user_ns
{

template< class JsonValue >
void tag_invoke(
    const boost::json::value_from_tag&, JsonValue& jv, const ip_address& addr )
{
    const unsigned char* b = addr.begin();
    jv = { b[0], b[1], b[2], b[3] };
}

template< class JsonValue >
typename boost::json::result_for< ip_address, JsonValue >::type
tag_invoke(
    const boost::json::try_value_to_tag< ip_address >&,
    const JsonValue& jv )
{
    using namespace boost::json;

    if( !jv.is_array() )
        return make_error_code( std::errc::invalid_argument );

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

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

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

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

    auto 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 };
}

}

As discussed previously, we prefer to define a non-throwing overload of tag_invoke for try_value_to, rather then the throwing overload for value_to, as the latter can fallback to the former without performance degradation.

Forward declarations of contextual conversions are done very similarly:

namespace boost {
namespace json {

class value;

struct value_from_tag;

template< class T >
struct try_value_to_tag;

template< class T1, class T2 >
struct result_for;

template< class T, class Context >
void value_from( T&& t, value& jv, const Context& ctx );

template< class T, class Context >
typename result_for< T, value >::type
try_value_to( const value& jv, const Context& ctx );

}
}
namespace user_ns
{

struct as_string
{ };

template< class JsonValue >
void
tag_invoke(
    const boost::json::value_from_tag&, JsonValue& jv,
    const ip_address& addr,
    const as_string& )
{
    auto& js = jv.emplace_string();
    js.resize( 4 * 3 + 3 + 1 ); // XXX.XXX.XXX.XXX\0
    auto it = addr.begin();
    auto n = std::sprintf(
        js.data(), "%hhu.%hhu.%hhu.%hhu", it[0], it[1], it[2], it[3] );
    js.resize(n);
}

template< class JsonValue >
typename boost::json::result_for< ip_address, JsonValue >::type
tag_invoke(
    const boost::json::try_value_to_tag< ip_address >&,
    const JsonValue& jv,
    const as_string& )
{
    const auto* js = jv.if_string();
    if( ! js )
        return make_error_code( std::errc::invalid_argument );

    unsigned char octets[4];
    int result = std::sscanf(
        js->data(), "%hhu.%hhu.%hhu.%hhu", octets, octets + 1, octets + 2, octets + 3 );
    if( result != 4 )
        return make_error_code( std::errc::invalid_argument );

    return ip_address( octets[0], octets[1], octets[2], octets[3] );
}

}

PrevUpHomeNext