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