File: C:/Users/fred/anaconda3/Library/include/boost/json/detail/value_to.hpp
//
// Copyright (c) 2019 Vinnie Falco ([email protected])
// Copyright (c) 2020 Krystian Stasiowski ([email protected])
// Copyright (c) 2021 Dmitry Arkhipov ([email protected])
//
// 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/describe/enum_from_string.hpp>
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
# include <optional>
#endif
namespace boost {
namespace json {
template<class T, class U,
typename std::enable_if<
! std::is_reference<T>::value &&
std::is_same<U, value>::value>::type>
T value_to(U const&);
template<class T>
typename result_for<T, value>::type
try_value_to(const value& jv);
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_code
try_reserve(
T&,
std::size_t size,
mp11::mp_int<2>)
{
error_code ec;
constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
if ( N != size )
{
BOOST_JSON_FAIL(ec, error::size_mismatch);
}
return ec;
}
template<typename T>
error_code
try_reserve(
T& cont,
std::size_t size,
mp11::mp_int<1>)
{
cont.reserve(size);
return error_code();
}
template<typename T>
error_code
try_reserve(
T&,
std::size_t,
mp11::mp_int<0>)
{
return error_code();
}
template<class T>
using has_push_back_helper
= decltype(std::declval<T&>().push_back(std::declval<value_type<T>>()));
template<class T>
using has_push_back = mp11::mp_valid<has_push_back_helper, T>;
template<class T>
using inserter_implementation = mp11::mp_cond<
is_tuple_like<T>, mp11::mp_int<2>,
has_push_back<T>, mp11::mp_int<1>,
mp11::mp_true, mp11::mp_int<0>>;
template<class T>
iterator_type<T>
inserter(
T& target,
mp11::mp_int<2>)
{
return target.begin();
}
template<class T>
std::back_insert_iterator<T>
inserter(
T& target,
mp11::mp_int<1>)
{
return std::back_inserter(target);
}
template<class T>
std::insert_iterator<T>
inserter(
T& target,
mp11::mp_int<0>)
{
return std::inserter(target, end(target));
}
// identity conversion
inline
result<value>
value_to_impl(
try_value_to_tag<value>,
value const& jv,
value_conversion_tag)
{
return jv;
}
inline
value
value_to_impl(
value_to_tag<value>,
value const& jv,
value_conversion_tag)
{
return jv;
}
// object
inline
result<object>
value_to_impl(
try_value_to_tag<object>,
value const& jv,
object_conversion_tag)
{
object const* obj = jv.if_object();
if( obj )
return *obj;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_object);
return ec;
}
// array
inline
result<array>
value_to_impl(
try_value_to_tag<array>,
value const& jv,
array_conversion_tag)
{
array const* arr = jv.if_array();
if( arr )
return *arr;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_array);
return ec;
}
// string
inline
result<string>
value_to_impl(
try_value_to_tag<string>,
value const& jv,
string_conversion_tag)
{
string const* str = jv.if_string();
if( str )
return *str;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_string);
return ec;
}
// bool
inline
result<bool>
value_to_impl(
try_value_to_tag<bool>,
value const& jv,
bool_conversion_tag)
{
auto b = jv.if_bool();
if( b )
return *b;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_bool);
return {boost::system::in_place_error, ec};
}
// integral and floating point
template<class T>
result<T>
value_to_impl(
try_value_to_tag<T>,
value const& jv,
number_conversion_tag)
{
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>
result<T>
value_to_impl(
try_value_to_tag<T>,
value const& jv,
null_like_conversion_tag)
{
if( jv.is_null() )
return {boost::system::in_place_value, T{}};
error_code ec;
BOOST_JSON_FAIL(ec, error::not_null);
return {boost::system::in_place_error, ec};
}
// string-like types
template<class T>
result<T>
value_to_impl(
try_value_to_tag<T>,
value const& jv,
string_like_conversion_tag)
{
auto str = jv.if_string();
if( str )
return {boost::system::in_place_value, T(str->subview())};
error_code ec;
BOOST_JSON_FAIL(ec, error::not_string);
return {boost::system::in_place_error, ec};
}
// map-like containers
template<class T>
result<T>
value_to_impl(
try_value_to_tag<T>,
value const& jv,
map_like_conversion_tag)
{
error_code ec;
object const* obj = jv.if_object();
if( !obj )
{
BOOST_JSON_FAIL(ec, error::not_object);
return {boost::system::in_place_error, ec};
}
T res;
ec = detail::try_reserve(res, obj->size(), reserve_implementation<T>());
if( ec.failed() )
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());
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;
}
template<class T>
T
value_to_impl(
value_to_tag<T>,
value const& jv,
map_like_conversion_tag)
{
error_code ec;
object const* obj = jv.if_object();
if( !obj )
{
BOOST_JSON_FAIL(ec, error::not_object);
throw_system_error( ec );
}
T result;
ec = detail::try_reserve(result, obj->size(), reserve_implementation<T>());
if( ec.failed() )
throw_system_error( ec );
auto ins = detail::inserter(result, inserter_implementation<T>());
for( key_value_pair const& kv: *obj )
*ins++ = value_type<T>{
key_type<T>(kv.key()),
value_to<mapped_type<T>>(kv.value())};
return result;
}
// all other containers
template<class T>
result<T>
value_to_impl(
try_value_to_tag<T>,
value const& jv,
sequence_conversion_tag)
{
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};
}
T result;
ec = detail::try_reserve(result, arr->size(), reserve_implementation<T>());
if( ec.failed() )
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);
if( elem_res.has_error() )
return {boost::system::in_place_error, elem_res.error()};
*ins++ = std::move(*elem_res);
}
return result;
}
template<class T>
T
value_to_impl(
value_to_tag<T>,
value const& jv,
sequence_conversion_tag)
{
error_code ec;
array const* arr = jv.if_array();
if( !arr )
{
BOOST_JSON_FAIL(ec, error::not_array);
throw_system_error( ec );
}
T result;
ec = detail::try_reserve(result, arr->size(), reserve_implementation<T>());
if( ec.failed() )
throw_system_error( ec );
auto ins = detail::inserter(result, inserter_implementation<T>());
for( value const& val: *arr )
*ins++ = value_to<value_type<T>>(val);
return result;
}
// tuple-like types
template <class T>
result<T>
try_make_tuple_elem(value const& jv, error_code& ec)
{
if( ec.failed() )
return {boost::system::in_place_error, ec};
auto result = try_value_to<T>(jv);
ec = result.error();
return result;
}
template <class T, std::size_t... Is>
result<T>
try_make_tuple_like(array const& arr, boost::mp11::index_sequence<Is...>)
{
error_code ec;
auto items = std::make_tuple(
try_make_tuple_elem<tuple_element_t<Is, T>>(
arr[Is], 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, std::size_t... Is>
T
make_tuple_like(array const& arr, boost::mp11::index_sequence<Is...>)
{
return T(value_to<tuple_element_t<Is, T>>(arr[Is])...);
}
template <class T>
result<T>
value_to_impl(
try_value_to_tag<T>,
value const& jv,
tuple_conversion_tag)
{
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, boost::mp11::make_index_sequence<N>());
}
template <class T>
T
value_to_impl(
value_to_tag<T>,
value const& jv,
tuple_conversion_tag)
{
error_code ec;
array const* arr = jv.if_array();
if( !arr )
{
BOOST_JSON_FAIL(ec, error::not_array);
throw_system_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);
throw_system_error( ec );
}
return make_tuple_like<T>(
*arr, boost::mp11::make_index_sequence<N>());
}
template< class T>
struct is_optional
: std::false_type
{ };
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
template< class T>
struct is_optional< std::optional<T> >
: std::true_type
{ };
#endif // BOOST_NO_CXX17_HDR_OPTIONAL
template< class T >
struct to_described_member
{
using Ds = describe::describe_members<
T, describe::mod_public | describe::mod_inherited>;
template< class D >
using described_member_t = remove_cvref<decltype(
std::declval<T&>().* D::pointer )>;
result<T>& res;
object const& obj;
std::size_t count;
template< class I >
void
operator()(I)
{
if( !res )
return;
using D = mp11::mp_at<Ds, I>;
using M = described_member_t<D>;
auto const found = obj.find(D::name);
if( found == obj.end() )
{
BOOST_IF_CONSTEXPR( !is_optional<M>::value )
{
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());
#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>
result<T>
value_to_impl(
try_value_to_tag<T>,
value const& jv,
described_class_conversion_tag)
{
result<T> res;
auto* obj = jv.if_object();
if( !obj )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::not_object);
res = {boost::system::in_place_error, ec};
return res;
}
to_described_member<T> member_converter{res, *obj, 0u};
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() )
{
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>
result<T>
value_to_impl(
try_value_to_tag<T>,
value const& jv,
described_enum_conversion_tag)
{
T val = {};
(void)jv;
#ifdef BOOST_DESCRIBE_CXX14
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};
}
//----------------------------------------------------------
// User-provided conversion
template<class T>
typename std::enable_if<
mp11::mp_valid<has_user_conversion_to_impl, T>::value,
T>::type
value_to_impl(
value_to_tag<T> tag,
value const& jv,
user_conversion_tag)
{
return tag_invoke(tag, jv);
}
template<class T>
typename std::enable_if<
!mp11::mp_valid<has_user_conversion_to_impl, T>::value,
T>::type
value_to_impl(
value_to_tag<T>,
value const& jv,
user_conversion_tag)
{
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>
typename std::enable_if<
mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
result<T>>::type
value_to_impl(
try_value_to_tag<T>,
value const& jv,
user_conversion_tag)
{
return tag_invoke(try_value_to_tag<T>(), jv);
}
template<class T>
typename std::enable_if<
!mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
result<T>>::type
value_to_impl(
try_value_to_tag<T>,
value const& jv,
user_conversion_tag)
{
try
{
return {
boost::system::in_place_value, tag_invoke(value_to_tag<T>(), jv)};
}
catch( std::bad_alloc const&)
{
throw;
}
catch( system_error const& e)
{
return {boost::system::in_place_error, e.code()};
}
catch( ... )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::exception);
return {boost::system::in_place_error, ec};
}
}
// no suitable conversion implementation
template<class T>
T
value_to_impl(
value_to_tag<T>,
value const&,
no_conversion_tag)
{
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 T, class Impl>
T
value_to_impl(
value_to_tag<T>,
value const& jv,
Impl impl)
{
return value_to_impl(try_value_to_tag<T>(), jv, impl).value();
}
template<class T>
using value_to_implementation
= conversion_implementation<T, value_to_conversion>;
} // detail
// std::optional
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
template<class T>
result<std::optional<T>>
tag_invoke(
try_value_to_tag<std::optional<T>>,
value const& jv)
{
if( jv.is_null() )
return std::optional<T>();
else
return try_value_to<T>(jv);
}
inline
result<std::nullopt_t>
tag_invoke(
try_value_to_tag<std::nullopt_t>,
value const& jv)
{
if( jv.is_null() )
return std::nullopt;
error_code ec;
BOOST_JSON_FAIL(ec, error::not_null);
return ec;
}
#endif
// std::variant
#ifndef BOOST_NO_CXX17_HDR_VARIANT
template<class... Ts>
result< std::variant<Ts...> >
tag_invoke(
try_value_to_tag< std::variant<Ts...> >,
value const& jv)
{
error_code ec;
BOOST_JSON_FAIL(ec, error::exhausted_variants);
using Variant = std::variant<Ts...>;
result<Variant> res = {system::in_place_error, ec};
mp11::mp_for_each< mp11::mp_iota_c<sizeof...(Ts)> >([&](auto I) {
if( res )
return;
using T = std::variant_alternative_t<I.value, Variant>;
auto attempt = try_value_to<T>(jv);
if( attempt )
res.emplace(std::in_place_index_t<I>(), std::move(*attempt));
});
if( res.has_error() )
{
res = {system::in_place_error, ec};
}
return res;
}
#endif // BOOST_NO_CXX17_HDR_VARIANT
} // namespace json
} // namespace boost
#endif