955 lines
23 KiB
C++
955 lines
23 KiB
C++
//
|
|
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
|
|
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
|
|
// Copyright (c) 2021 Dmitry Arkhipov (grisumbras@gmail.com)
|
|
//
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
//
|
|
// Official repository: https://github.com/boostorg/json
|
|
//
|
|
|
|
#ifndef BOOST_JSON_DETAIL_VALUE_TO_HPP
|
|
#define BOOST_JSON_DETAIL_VALUE_TO_HPP
|
|
|
|
#include <boost/json/value.hpp>
|
|
#include <boost/json/conversion.hpp>
|
|
#include <boost/json/result_for.hpp>
|
|
#include <boost/describe/enum_from_string.hpp>
|
|
|
|
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
|
# include <optional>
|
|
#endif
|
|
|
|
namespace boost {
|
|
namespace json {
|
|
|
|
namespace detail {
|
|
|
|
template<class T>
|
|
using has_reserve_member_helper = decltype(std::declval<T&>().reserve(0));
|
|
template<class T>
|
|
using has_reserve_member = mp11::mp_valid<has_reserve_member_helper, T>;
|
|
template<class T>
|
|
using reserve_implementation = mp11::mp_cond<
|
|
is_tuple_like<T>, mp11::mp_int<2>,
|
|
has_reserve_member<T>, mp11::mp_int<1>,
|
|
mp11::mp_true, mp11::mp_int<0>>;
|
|
|
|
template<class T>
|
|
error
|
|
try_reserve(
|
|
T&,
|
|
std::size_t size,
|
|
mp11::mp_int<2>)
|
|
{
|
|
constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
|
|
if ( N != size )
|
|
return error::size_mismatch;
|
|
return error();
|
|
}
|
|
|
|
template<typename T>
|
|
error
|
|
try_reserve(
|
|
T& cont,
|
|
std::size_t size,
|
|
mp11::mp_int<1>)
|
|
{
|
|
cont.reserve(size);
|
|
return error();
|
|
}
|
|
|
|
template<typename T>
|
|
error
|
|
try_reserve(
|
|
T&,
|
|
std::size_t,
|
|
mp11::mp_int<0>)
|
|
{
|
|
return error();
|
|
}
|
|
|
|
|
|
// identity conversion
|
|
template< class Ctx >
|
|
system::result<value>
|
|
value_to_impl(
|
|
value_conversion_tag,
|
|
try_value_to_tag<value>,
|
|
value const& jv,
|
|
Ctx const& )
|
|
{
|
|
return jv;
|
|
}
|
|
|
|
template< class Ctx >
|
|
value
|
|
value_to_impl(
|
|
value_conversion_tag, value_to_tag<value>, value const& jv, Ctx const& )
|
|
{
|
|
return jv;
|
|
}
|
|
|
|
// object
|
|
template< class Ctx >
|
|
system::result<object>
|
|
value_to_impl(
|
|
object_conversion_tag,
|
|
try_value_to_tag<object>,
|
|
value const& jv,
|
|
Ctx const& )
|
|
{
|
|
object const* obj = jv.if_object();
|
|
if( obj )
|
|
return *obj;
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_object);
|
|
return ec;
|
|
}
|
|
|
|
// array
|
|
template< class Ctx >
|
|
system::result<array>
|
|
value_to_impl(
|
|
array_conversion_tag,
|
|
try_value_to_tag<array>,
|
|
value const& jv,
|
|
Ctx const& )
|
|
{
|
|
array const* arr = jv.if_array();
|
|
if( arr )
|
|
return *arr;
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_array);
|
|
return ec;
|
|
}
|
|
|
|
// string
|
|
template< class Ctx >
|
|
system::result<string>
|
|
value_to_impl(
|
|
string_conversion_tag,
|
|
try_value_to_tag<string>,
|
|
value const& jv,
|
|
Ctx const& )
|
|
{
|
|
string const* str = jv.if_string();
|
|
if( str )
|
|
return *str;
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_string);
|
|
return ec;
|
|
}
|
|
|
|
// bool
|
|
template< class Ctx >
|
|
system::result<bool>
|
|
value_to_impl(
|
|
bool_conversion_tag, try_value_to_tag<bool>, value const& jv, Ctx const& )
|
|
{
|
|
auto b = jv.if_bool();
|
|
if( b )
|
|
return *b;
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_bool);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
// integral and floating point
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
number_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
|
|
{
|
|
system::error_code ec;
|
|
auto const n = jv.to_number<T>(ec);
|
|
if( ec.failed() )
|
|
return {boost::system::in_place_error, ec};
|
|
return {boost::system::in_place_value, n};
|
|
}
|
|
|
|
// null-like conversion
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
null_like_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& )
|
|
{
|
|
if( jv.is_null() )
|
|
return {boost::system::in_place_value, T{}};
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_null);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
// string-like types
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
string_like_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& )
|
|
{
|
|
auto str = jv.if_string();
|
|
if( str )
|
|
return {boost::system::in_place_value, T(str->subview())};
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_string);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
// map-like containers
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
map_like_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
object const* obj = jv.if_object();
|
|
if( !obj )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_object);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
T res;
|
|
error const e = detail::try_reserve(
|
|
res, obj->size(), reserve_implementation<T>());
|
|
if( e != error() )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL( ec, e );
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
auto ins = detail::inserter(res, inserter_implementation<T>());
|
|
for( key_value_pair const& kv: *obj )
|
|
{
|
|
auto elem_res = try_value_to<mapped_type<T>>( kv.value(), ctx );
|
|
if( elem_res.has_error() )
|
|
return {boost::system::in_place_error, elem_res.error()};
|
|
*ins++ = value_type<T>{
|
|
key_type<T>(kv.key()),
|
|
std::move(*elem_res)};
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// all other containers
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
sequence_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
array const* arr = jv.if_array();
|
|
if( !arr )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_array);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
T result;
|
|
error const e = detail::try_reserve(
|
|
result, arr->size(), reserve_implementation<T>());
|
|
if( e != error() )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL( ec, e );
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
auto ins = detail::inserter(result, inserter_implementation<T>());
|
|
for( value const& val: *arr )
|
|
{
|
|
auto elem_res = try_value_to<value_type<T>>( val, ctx );
|
|
if( elem_res.has_error() )
|
|
return {boost::system::in_place_error, elem_res.error()};
|
|
*ins++ = std::move(*elem_res);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// tuple-like types
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
try_make_tuple_elem(value const& jv, Ctx const& ctx, system::error_code& ec)
|
|
{
|
|
if( ec.failed() )
|
|
return {boost::system::in_place_error, ec};
|
|
|
|
auto result = try_value_to<T>( jv, ctx );
|
|
ec = result.error();
|
|
return result;
|
|
}
|
|
|
|
template <class T, class Ctx, std::size_t... Is>
|
|
system::result<T>
|
|
try_make_tuple_like(
|
|
array const& arr, Ctx const& ctx, boost::mp11::index_sequence<Is...>)
|
|
{
|
|
system::error_code ec;
|
|
auto items = std::make_tuple(
|
|
try_make_tuple_elem<
|
|
typename std::decay<tuple_element_t<Is, T>>::type >(
|
|
arr[Is], ctx, ec)
|
|
...);
|
|
if( ec.failed() )
|
|
return {boost::system::in_place_error, ec};
|
|
|
|
return {
|
|
boost::system::in_place_value, T(std::move(*std::get<Is>(items))...)};
|
|
}
|
|
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
tuple_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
system::error_code ec;
|
|
|
|
array const* arr = jv.if_array();
|
|
if( !arr )
|
|
{
|
|
BOOST_JSON_FAIL(ec, error::not_array);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
|
|
if( N != arr->size() )
|
|
{
|
|
BOOST_JSON_FAIL(ec, error::size_mismatch);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
return try_make_tuple_like<T>(
|
|
*arr, ctx, boost::mp11::make_index_sequence<N>());
|
|
}
|
|
|
|
template< class Ctx, class T, bool non_throwing = true >
|
|
struct to_described_member
|
|
{
|
|
using Ds = described_members<T>;
|
|
|
|
using result_type = mp11::mp_eval_if_c<
|
|
!non_throwing, T, system::result, T>;
|
|
|
|
result_type& res;
|
|
object const& obj;
|
|
std::size_t count;
|
|
Ctx const& ctx;
|
|
|
|
template< class I >
|
|
void
|
|
operator()(I)
|
|
{
|
|
if( !res )
|
|
return;
|
|
|
|
using D = mp11::mp_at<Ds, I>;
|
|
using M = described_member_t<T, D>;
|
|
|
|
auto const found = obj.find(D::name);
|
|
if( found == obj.end() )
|
|
{
|
|
BOOST_IF_CONSTEXPR( !is_optional_like<M>::value )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::unknown_name);
|
|
res = {boost::system::in_place_error, ec};
|
|
}
|
|
return;
|
|
}
|
|
|
|
#if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
|
|
# pragma GCC diagnostic push
|
|
# pragma GCC diagnostic ignored "-Wunused"
|
|
# pragma GCC diagnostic ignored "-Wunused-variable"
|
|
#endif
|
|
auto member_res = try_value_to<M>( found->value(), ctx );
|
|
#if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
|
|
# pragma GCC diagnostic pop
|
|
#endif
|
|
if( member_res )
|
|
{
|
|
(*res).* D::pointer = std::move(*member_res);
|
|
++count;
|
|
}
|
|
else
|
|
res = {boost::system::in_place_error, member_res.error()};
|
|
}
|
|
};
|
|
|
|
// described classes
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
described_class_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
BOOST_STATIC_ASSERT( std::is_default_constructible<T>::value );
|
|
system::result<T> res;
|
|
|
|
auto* obj = jv.if_object();
|
|
if( !obj )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_object);
|
|
res = {boost::system::in_place_error, ec};
|
|
return res;
|
|
}
|
|
|
|
to_described_member< Ctx, T > member_converter{ res, *obj, 0u, ctx };
|
|
|
|
using Ds = typename decltype(member_converter)::Ds;
|
|
constexpr std::size_t N = mp11::mp_size<Ds>::value;
|
|
mp11::mp_for_each< mp11::mp_iota_c<N> >(member_converter);
|
|
|
|
if( !res )
|
|
return res;
|
|
|
|
if( member_converter.count != obj->size() )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::size_mismatch);
|
|
res = {boost::system::in_place_error, ec};
|
|
return res;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// described enums
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
described_enum_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& )
|
|
{
|
|
T val = {};
|
|
(void)jv;
|
|
#ifdef BOOST_DESCRIBE_CXX14
|
|
system::error_code ec;
|
|
|
|
auto str = jv.if_string();
|
|
if( !str )
|
|
{
|
|
BOOST_JSON_FAIL(ec, error::not_string);
|
|
return {system::in_place_error, ec};
|
|
}
|
|
|
|
if( !describe::enum_from_string(str->data(), val) )
|
|
{
|
|
BOOST_JSON_FAIL(ec, error::unknown_name);
|
|
return {system::in_place_error, ec};
|
|
}
|
|
#endif
|
|
|
|
return {system::in_place_value, val};
|
|
}
|
|
|
|
// optionals
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
optional_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx)
|
|
{
|
|
using Inner = value_result_type<T>;
|
|
if( jv.is_null() )
|
|
return {};
|
|
else
|
|
return try_value_to<Inner>(jv, ctx);
|
|
}
|
|
|
|
// variants
|
|
template< class T, class V, class I >
|
|
using variant_construction_category = mp11::mp_cond<
|
|
std::is_constructible< T, variant2::in_place_index_t<I::value>, V >,
|
|
mp11::mp_int<2>,
|
|
#ifndef BOOST_NO_CXX17_HDR_VARIANT
|
|
std::is_constructible< T, std::in_place_index_t<I::value>, V >,
|
|
mp11::mp_int<1>,
|
|
#endif // BOOST_NO_CXX17_HDR_VARIANT
|
|
mp11::mp_true,
|
|
mp11::mp_int<0> >;
|
|
|
|
template< class T, class I, class V >
|
|
T
|
|
initialize_variant( V&& v, mp11::mp_int<0> )
|
|
{
|
|
T t;
|
|
t.template emplace<I::value>( std::move(v) );
|
|
return t;
|
|
}
|
|
|
|
template< class T, class I, class V >
|
|
T
|
|
initialize_variant( V&& v, mp11::mp_int<2> )
|
|
{
|
|
return T( variant2::in_place_index_t<I::value>(), std::move(v) );
|
|
}
|
|
|
|
#ifndef BOOST_NO_CXX17_HDR_VARIANT
|
|
template< class T, class I, class V >
|
|
T
|
|
initialize_variant( V&& v, mp11::mp_int<1> )
|
|
{
|
|
return T( std::in_place_index_t<I::value>(), std::move(v) );
|
|
}
|
|
#endif // BOOST_NO_CXX17_HDR_VARIANT
|
|
|
|
struct locally_prohibit_exceptions
|
|
{};
|
|
|
|
template< class Ctx >
|
|
Ctx const&
|
|
make_locally_nonthrowing_context(Ctx const& ctx) noexcept
|
|
{
|
|
return ctx;
|
|
}
|
|
|
|
template< class... Ctxes >
|
|
std::tuple<Ctxes...> const&
|
|
make_locally_nonthrowing_context(std::tuple<Ctxes...> const& ctx) noexcept
|
|
{
|
|
return ctx;
|
|
}
|
|
|
|
template< class... Ctxes >
|
|
std::tuple<locally_prohibit_exceptions, allow_exceptions, Ctxes...>
|
|
make_locally_nonthrowing_context(std::tuple<allow_exceptions, Ctxes...> const& ctx)
|
|
noexcept
|
|
{
|
|
return std::tuple_cat(std::make_tuple( locally_prohibit_exceptions() ), ctx);
|
|
}
|
|
|
|
template< class Ctx >
|
|
Ctx const&
|
|
remove_local_exception_prohibition(Ctx const& ctx) noexcept
|
|
{
|
|
return ctx;
|
|
}
|
|
|
|
template< class T, class... Ts, std::size_t... Is>
|
|
std::tuple<Ts...>
|
|
remove_local_exception_prohibition_helper(
|
|
std::tuple<T, Ts...> const& tup,
|
|
mp11::index_sequence<Is...>) noexcept
|
|
{
|
|
return std::tuple<Ts...>( std::get<Is + 1>(tup)... );
|
|
}
|
|
|
|
template< class... Ctxes >
|
|
std::tuple<Ctxes...>
|
|
remove_local_exception_prohibition(
|
|
std::tuple<locally_prohibit_exceptions, Ctxes...> const& ctx) noexcept
|
|
{
|
|
return remove_local_exception_prohibition_helper(
|
|
ctx, mp11::index_sequence_for<Ctxes...>() );
|
|
}
|
|
|
|
template< class T, class Ctx >
|
|
struct alternative_converter
|
|
{
|
|
system::result<T>& res;
|
|
value const& jv;
|
|
Ctx const& ctx;
|
|
|
|
template< class I >
|
|
void operator()( I ) const
|
|
{
|
|
if( res )
|
|
return;
|
|
|
|
auto&& local_ctx = make_locally_nonthrowing_context(ctx);
|
|
using V = mp11::mp_at<T, I>;
|
|
auto attempt = try_value_to<V>(jv, local_ctx);
|
|
if( attempt )
|
|
{
|
|
using cat = variant_construction_category<T, V, I>;
|
|
res = initialize_variant<T, I>( std::move(*attempt), cat() );
|
|
}
|
|
}
|
|
};
|
|
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
variant_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx)
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::exhausted_variants);
|
|
|
|
using Is = mp11::mp_iota< mp11::mp_size<T> >;
|
|
|
|
system::result<T> res = {system::in_place_error, ec};
|
|
mp11::mp_for_each<Is>( alternative_converter<T, Ctx>{res, jv, ctx} );
|
|
return res;
|
|
}
|
|
|
|
template< class T, class Ctx >
|
|
system::result<T>
|
|
value_to_impl(
|
|
path_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
|
|
{
|
|
auto str = jv.if_string();
|
|
if( !str )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_string);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
|
|
string_view sv = str->subview();
|
|
return {boost::system::in_place_value, T( sv.begin(), sv.end() )};
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// User-provided conversions; throwing -> throwing
|
|
template< class T, class Ctx >
|
|
mp11::mp_if< mp11::mp_valid<has_user_conversion_to_impl, T>, T >
|
|
value_to_impl(
|
|
user_conversion_tag, value_to_tag<T> tag, value const& jv, Ctx const&)
|
|
{
|
|
return tag_invoke(tag, jv);
|
|
}
|
|
|
|
template<
|
|
class T,
|
|
class Ctx,
|
|
class Sup = supported_context<Ctx, T, value_to_conversion>
|
|
>
|
|
mp11::mp_if<
|
|
mp11::mp_valid< has_context_conversion_to_impl, typename Sup::type, T>, T >
|
|
value_to_impl(
|
|
context_conversion_tag,
|
|
value_to_tag<T> tag,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
return tag_invoke( tag, jv, Sup::get(ctx) );
|
|
}
|
|
|
|
template<
|
|
class T,
|
|
class Ctx,
|
|
class Sup = supported_context<Ctx, T, value_to_conversion>
|
|
>
|
|
mp11::mp_if<
|
|
mp11::mp_valid<
|
|
has_full_context_conversion_to_impl, typename Sup::type, T>,
|
|
T>
|
|
value_to_impl(
|
|
full_context_conversion_tag,
|
|
value_to_tag<T> tag,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
return tag_invoke( tag, jv, Sup::get(ctx), ctx );
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// User-provided conversions; throwing -> nonthrowing
|
|
template< class T, class Ctx >
|
|
mp11::mp_if_c< !mp11::mp_valid<has_user_conversion_to_impl, T>::value, T>
|
|
value_to_impl(
|
|
user_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& )
|
|
{
|
|
auto res = tag_invoke(try_value_to_tag<T>(), jv);
|
|
if( res.has_error() )
|
|
throw_system_error( res.error() );
|
|
return std::move(*res);
|
|
}
|
|
|
|
template<
|
|
class T,
|
|
class Ctx,
|
|
class Sup = supported_context<Ctx, T, value_to_conversion>
|
|
>
|
|
mp11::mp_if_c<
|
|
!mp11::mp_valid<
|
|
has_context_conversion_to_impl, typename Sup::type, T>::value,
|
|
T>
|
|
value_to_impl(
|
|
context_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& ctx )
|
|
{
|
|
auto res = tag_invoke( try_value_to_tag<T>(), jv, Sup::get(ctx) );
|
|
if( res.has_error() )
|
|
throw_system_error( res.error() );
|
|
return std::move(*res);
|
|
}
|
|
|
|
template< class Ctx >
|
|
std::tuple<allow_exceptions, Ctx>
|
|
make_throwing_context(Ctx const& ctx)
|
|
{
|
|
return std::tuple<allow_exceptions, Ctx>(allow_exceptions(), ctx);
|
|
}
|
|
|
|
template< class... Ctxes >
|
|
std::tuple<allow_exceptions, Ctxes...>
|
|
make_throwing_context(std::tuple<Ctxes...> const& ctx)
|
|
{
|
|
return std::tuple_cat(std::make_tuple( allow_exceptions() ), ctx);
|
|
}
|
|
|
|
template< class... Ctxes >
|
|
std::tuple<allow_exceptions, Ctxes...> const&
|
|
make_throwing_context(std::tuple<allow_exceptions, Ctxes...> const& ctx)
|
|
noexcept
|
|
{
|
|
return ctx;
|
|
}
|
|
|
|
template<
|
|
class T,
|
|
class Ctx,
|
|
class Sup = supported_context<Ctx, T, value_to_conversion>
|
|
>
|
|
mp11::mp_if_c<
|
|
!mp11::mp_valid<
|
|
has_full_context_conversion_to_impl, typename Sup::type, T>::value,
|
|
T>
|
|
value_to_impl(
|
|
full_context_conversion_tag,
|
|
value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
auto res = tag_invoke(
|
|
try_value_to_tag<T>(),
|
|
jv,
|
|
Sup::get(ctx),
|
|
make_throwing_context(ctx));
|
|
if( res.has_error() )
|
|
throw_system_error( res.error() );
|
|
return std::move(*res);
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// User-provided conversions; nonthrowing -> nonthrowing
|
|
template< class T, class Ctx >
|
|
mp11::mp_if<
|
|
mp11::mp_valid<
|
|
has_nonthrowing_user_conversion_to_impl, T>, system::result<T> >
|
|
value_to_impl(
|
|
user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
|
|
{
|
|
return tag_invoke(try_value_to_tag<T>(), jv);
|
|
}
|
|
|
|
template<
|
|
class T,
|
|
class Ctx,
|
|
class Sup = supported_context<Ctx, T, value_to_conversion>
|
|
>
|
|
mp11::mp_if<
|
|
mp11::mp_valid<
|
|
has_nonthrowing_context_conversion_to_impl, typename Sup::type, T>,
|
|
system::result<T> >
|
|
value_to_impl(
|
|
context_conversion_tag,
|
|
try_value_to_tag<T> tag,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
return tag_invoke( tag, jv, Sup::get(ctx) );
|
|
}
|
|
|
|
template<
|
|
class T,
|
|
class Ctx,
|
|
class Sup = supported_context<Ctx, T, value_to_conversion>
|
|
>
|
|
mp11::mp_if<
|
|
mp11::mp_valid<
|
|
has_nonthrowing_full_context_conversion_to_impl,
|
|
typename Sup::type,
|
|
T>,
|
|
system::result<T> >
|
|
value_to_impl(
|
|
full_context_conversion_tag,
|
|
try_value_to_tag<T> tag,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
return tag_invoke( tag, jv, Sup::get(ctx), ctx );
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// User-provided conversions; nonthrowing -> throwing
|
|
|
|
template< class Ctx >
|
|
struct does_allow_exceptions : std::false_type
|
|
{ };
|
|
|
|
template< class... Ctxes >
|
|
struct does_allow_exceptions< std::tuple<allow_exceptions, Ctxes...> >
|
|
: std::true_type
|
|
{ };
|
|
|
|
template< class T, class... Args >
|
|
system::result<T>
|
|
wrap_conversion_exceptions( std::true_type, value_to_tag<T>, Args&& ... args )
|
|
{
|
|
return {
|
|
boost::system::in_place_value,
|
|
tag_invoke( value_to_tag<T>(), static_cast<Args&&>(args)... )};
|
|
}
|
|
|
|
template< class T, class... Args >
|
|
system::result<T>
|
|
wrap_conversion_exceptions( std::false_type, value_to_tag<T>, Args&& ... args )
|
|
{
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
try
|
|
{
|
|
#endif
|
|
return wrap_conversion_exceptions(
|
|
std::true_type(),
|
|
value_to_tag<T>(),
|
|
static_cast<Args&&>(args)... );
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
}
|
|
catch( std::bad_alloc const&)
|
|
{
|
|
throw;
|
|
}
|
|
catch( system::system_error const& e)
|
|
{
|
|
return {boost::system::in_place_error, e.code()};
|
|
}
|
|
catch( ... )
|
|
{
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::exception);
|
|
return {boost::system::in_place_error, ec};
|
|
}
|
|
#endif
|
|
}
|
|
|
|
template< class T, class Ctx >
|
|
mp11::mp_if_c<
|
|
!mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
|
|
system::result<T> >
|
|
value_to_impl(
|
|
user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
|
|
{
|
|
return wrap_conversion_exceptions(
|
|
does_allow_exceptions<Ctx>(), value_to_tag<T>(), jv);
|
|
}
|
|
|
|
template<
|
|
class T,
|
|
class Ctx,
|
|
class Sup = supported_context<Ctx, T, value_to_conversion>
|
|
>
|
|
mp11::mp_if_c<
|
|
!mp11::mp_valid<
|
|
has_nonthrowing_context_conversion_to_impl,
|
|
typename Sup::type,
|
|
T>::value,
|
|
system::result<T> >
|
|
value_to_impl(
|
|
context_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
return wrap_conversion_exceptions(
|
|
does_allow_exceptions<Ctx>(), value_to_tag<T>(), jv, Sup::get(ctx) );
|
|
}
|
|
|
|
template<
|
|
class T,
|
|
class Ctx,
|
|
class Sup = supported_context<Ctx, T, value_to_conversion>
|
|
>
|
|
mp11::mp_if_c<
|
|
!mp11::mp_valid<
|
|
has_nonthrowing_full_context_conversion_to_impl,
|
|
typename Sup::type,
|
|
T>::value,
|
|
system::result<T> >
|
|
value_to_impl(
|
|
full_context_conversion_tag,
|
|
try_value_to_tag<T>,
|
|
value const& jv,
|
|
Ctx const& ctx )
|
|
{
|
|
return wrap_conversion_exceptions(
|
|
does_allow_exceptions<Ctx>(),
|
|
value_to_tag<T>(),
|
|
jv,
|
|
Sup::get(ctx),
|
|
remove_local_exception_prohibition(ctx) );
|
|
}
|
|
|
|
// no suitable conversion implementation
|
|
template< class T, class Ctx >
|
|
T
|
|
value_to_impl( no_conversion_tag, value_to_tag<T>, value const&, Ctx const& )
|
|
{
|
|
static_assert(
|
|
!std::is_same<T, T>::value,
|
|
"No suitable tag_invoke overload found for the type");
|
|
}
|
|
|
|
// generic wrapper over non-throwing implementations
|
|
template< class Impl, class T, class Ctx >
|
|
T
|
|
value_to_impl( Impl impl, value_to_tag<T>, value const& jv, Ctx const& ctx )
|
|
{
|
|
return value_to_impl(
|
|
impl, try_value_to_tag<T>(), jv, make_throwing_context(ctx) ).value();
|
|
}
|
|
|
|
template< class Ctx, class T >
|
|
using value_to_category = conversion_category<
|
|
Ctx, T, value_to_conversion >;
|
|
|
|
} // detail
|
|
|
|
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
|
inline
|
|
system::result<std::nullopt_t>
|
|
tag_invoke(
|
|
try_value_to_tag<std::nullopt_t>,
|
|
value const& jv)
|
|
{
|
|
if( jv.is_null() )
|
|
return std::nullopt;
|
|
system::error_code ec;
|
|
BOOST_JSON_FAIL(ec, error::not_null);
|
|
return ec;
|
|
}
|
|
#endif
|
|
|
|
} // namespace json
|
|
} // namespace boost
|
|
|
|
#endif
|