196 lines
5.0 KiB
C++
196 lines
5.0 KiB
C++
//
|
|
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
|
|
// Copyright (c) 2020 Krystian Stasiowski (sdkrystian@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_FROM_HPP
|
|
#define BOOST_JSON_DETAIL_VALUE_FROM_HPP
|
|
|
|
#include <boost/json/storage_ptr.hpp>
|
|
#include <boost/json/value.hpp>
|
|
#include <boost/json/detail/value_traits.hpp>
|
|
|
|
BOOST_JSON_NS_BEGIN
|
|
|
|
struct value_from_tag { };
|
|
|
|
template<class T, class = void>
|
|
struct has_value_from;
|
|
|
|
namespace detail {
|
|
|
|
// The integral_constant parameter here is an
|
|
// rvalue reference to make the standard conversion
|
|
// sequence to that parameter better, see
|
|
// http://eel.is/c++draft/over.ics.rank#3.2.6
|
|
template<std::size_t N, class T>
|
|
void
|
|
tuple_to_array(
|
|
T&&,
|
|
array&,
|
|
std::integral_constant<std::size_t, N>&&)
|
|
{
|
|
}
|
|
|
|
template<std::size_t N, std::size_t I, class T>
|
|
void
|
|
tuple_to_array(
|
|
T&& t,
|
|
array& arr,
|
|
const std::integral_constant<std::size_t, I>&)
|
|
{
|
|
using std::get;
|
|
arr.emplace_back(value_from(
|
|
get<I>(std::forward<T>(t)), arr.storage()));
|
|
return detail::tuple_to_array<N>(std::forward<T>(t),
|
|
arr, std::integral_constant<std::size_t, I + 1>());
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// User-provided conversion
|
|
|
|
template<class T, void_t<decltype(tag_invoke(value_from_tag(),
|
|
std::declval<value&>(), std::declval<T&&>()))>* = nullptr>
|
|
void
|
|
value_from_helper(
|
|
value& jv,
|
|
T&& from,
|
|
priority_tag<5>)
|
|
{
|
|
tag_invoke(value_from_tag(), jv, std::forward<T>(from));
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------
|
|
// Native conversion
|
|
|
|
template<class T, typename std::enable_if<
|
|
detail::value_constructible<T>::value>::type* = nullptr>
|
|
void
|
|
value_from_helper(
|
|
value& jv,
|
|
T&& from,
|
|
priority_tag<4>)
|
|
{
|
|
jv = std::forward<T>(from);
|
|
}
|
|
|
|
template<class T, typename std::enable_if<
|
|
std::is_same<detail::remove_cvref<T>,
|
|
std::nullptr_t>::value>::type* = nullptr>
|
|
void
|
|
value_from_helper(
|
|
value& jv,
|
|
T&&,
|
|
priority_tag<4>)
|
|
{
|
|
// do nothing
|
|
BOOST_ASSERT(jv.is_null());
|
|
(void)jv;
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// Generic conversions
|
|
|
|
// string-like types
|
|
// NOTE: original check for size used is_convertible but
|
|
// MSVC-140 selects wrong specialisation if used
|
|
template<class T, typename std::enable_if<
|
|
std::is_constructible<remove_cvref<T>, const char*, std::size_t>::value &&
|
|
std::is_convertible<decltype(std::declval<T&>().data()), const char*>::value &&
|
|
std::is_integral<decltype(std::declval<T&>().size())>::value
|
|
>::type* = nullptr>
|
|
void
|
|
value_from_helper(
|
|
value& jv,
|
|
T&& from,
|
|
priority_tag<3>)
|
|
{
|
|
jv.emplace_string().assign(
|
|
from.data(), from.size());
|
|
}
|
|
|
|
// map-like types; should go before ranges, so that we can differentiate
|
|
// map-like and other ranges
|
|
template<class T, typename std::enable_if<
|
|
map_traits<T>::has_unique_keys &&
|
|
has_value_from<typename map_traits<T>::pair_value_type>::value &&
|
|
std::is_convertible<typename map_traits<T>::pair_key_type,
|
|
string_view>::value>::type* = nullptr>
|
|
void
|
|
value_from_helper(
|
|
value& jv,
|
|
T&& from,
|
|
priority_tag<2>)
|
|
{
|
|
using std::get;
|
|
object& obj = jv.emplace_object();
|
|
obj.reserve(container_traits<T>::try_size(from));
|
|
for (auto&& elem : from)
|
|
obj.emplace(get<0>(elem), value_from(
|
|
get<1>(elem), obj.storage()));
|
|
}
|
|
|
|
// ranges; should go before tuple-like in order for std::array being handled
|
|
// by this overload
|
|
template<class T, typename std::enable_if<
|
|
has_value_from<typename container_traits<T>::
|
|
value_type>::value>::type* = nullptr>
|
|
void
|
|
value_from_helper(
|
|
value& jv,
|
|
T&& from,
|
|
priority_tag<1>)
|
|
{
|
|
array& result = jv.emplace_array();
|
|
result.reserve(container_traits<T>::try_size(from));
|
|
for (auto&& elem : from)
|
|
result.emplace_back(
|
|
value_from(elem, result.storage()));
|
|
}
|
|
|
|
// tuple-like types
|
|
template<class T, typename std::enable_if<
|
|
(std::tuple_size<remove_cvref<T>>::value > 0)>::type* = nullptr>
|
|
void
|
|
value_from_helper(
|
|
value& jv,
|
|
T&& from,
|
|
priority_tag<0>)
|
|
{
|
|
constexpr std::size_t n =
|
|
std::tuple_size<remove_cvref<T>>::value;
|
|
array& arr = jv.emplace_array();
|
|
arr.reserve(n);
|
|
detail::tuple_to_array<n>(std::forward<T>(from),
|
|
arr, std::integral_constant<std::size_t, 0>());
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
|
|
// Calls to value_from are forwarded to this function
|
|
// so we can use ADL and hide the built-in tag_invoke
|
|
// overloads in the detail namespace
|
|
template<class T, class = void_t<
|
|
decltype(detail::value_from_helper(std::declval<value&>(),
|
|
std::declval<T&&>(), priority_tag<5>()))>>
|
|
value
|
|
value_from_impl(
|
|
T&& from,
|
|
storage_ptr sp)
|
|
{
|
|
value jv(std::move(sp));
|
|
detail::value_from_helper(jv, std::forward<T>(from), priority_tag<5>());
|
|
return jv;
|
|
}
|
|
|
|
} // detail
|
|
BOOST_JSON_NS_END
|
|
|
|
#endif
|