From 794dae8911d2a4ea42a28066a536d3b5f42227d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20DELRIEU?= <theo@tanker.io> Date: Sun, 15 Jan 2017 21:01:29 +0100 Subject: [PATCH] apply changes to json.hpp.re2c --- src/json.hpp.re2c | 191 ++++++++++++++++++++++++++++++---------------- 1 file changed, 125 insertions(+), 66 deletions(-) diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 5dc2e027..4dbeda8a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -168,7 +168,7 @@ using is_unscoped_enum = namespace detail { -template <typename Json> std::string type_name(Json const &j) +template <typename Json> std::string type_name(const Json &j) { switch (j.m_type) { @@ -189,6 +189,11 @@ template <typename Json> std::string type_name(Json const &j) } } +// dispatch utility (taken from ranges-v3) +template <unsigned N> struct priority_tag : priority_tag<N - 1> {}; + +template <> struct priority_tag<0> {}; + // This is an experiment. I need this to move constructors out of basic_json. // I'm sure there is a better way, but this might need a big basic_json refactoring template <value_t> struct external_constructor; @@ -491,14 +496,15 @@ template <typename Json, typename ArithmeticType, not std::is_same<ArithmeticType, typename Json::boolean_t>::value, int> = 0> -void get_arithmetic_value(Json const &j, ArithmeticType &val) +void get_arithmetic_value(const Json &j, ArithmeticType &val) { - if (j.is_number_integer()) - val = *const_cast<Json&>(j).template get_ptr<typename Json::number_integer_t*>(); - else if (j.is_number_unsigned()) - val = *const_cast<Json&>(j).template get_ptr<typename Json::number_unsigned_t*>(); + // unsigned must be checked first, since is_number_integer() == true for unsigned + if (j.is_number_unsigned()) + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_unsigned_t*>()); + else if (j.is_number_integer()) + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_integer_t*>()); else if (j.is_number_float()) - val = *const_cast<Json&>(j).template get_ptr<typename Json::number_float_t*>(); + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_float_t*>()); else throw std::domain_error("type must be number, but is " + type_name(j)); } @@ -522,7 +528,7 @@ template <typename Json, typename FloatType, enable_if_t<std::is_floating_point<FloatType>::value, int> = 0> void to_json(Json &j, FloatType val) noexcept { - external_constructor<value_t::number_float>::construct(j, val); + external_constructor<value_t::number_float>::construct(j, static_cast<typename Json::number_float_t>(val)); } @@ -533,7 +539,7 @@ template < int> = 0> void to_json(Json &j, CompatibleNumberUnsignedType val) noexcept { - external_constructor<value_t::number_unsigned>::construct(j, val); + external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename Json::number_unsigned_t>(val)); } template < @@ -543,7 +549,7 @@ template < int> = 0> void to_json(Json &j, CompatibleNumberIntegerType val) noexcept { - external_constructor<value_t::number_integer>::construct(j, val); + external_constructor<value_t::number_integer>::construct(j, static_cast<typename Json::number_integer_t>(val)); } template <typename Json, typename UnscopedEnumType, @@ -559,7 +565,7 @@ template < is_compatible_array_type<Json, CompatibleArrayType>::value or std::is_same<typename Json::array_t, CompatibleArrayType>::value, int> = 0> -void to_json(Json &j, CompatibleArrayType const &arr) +void to_json(Json &j, const CompatibleArrayType &arr) { external_constructor<value_t::array>::construct(j, arr); } @@ -568,48 +574,48 @@ template < typename Json, typename CompatibleObjectType, enable_if_t<is_compatible_object_type<Json, CompatibleObjectType>::value, int> = 0> -void to_json(Json &j, CompatibleObjectType const &arr) +void to_json(Json &j, const CompatibleObjectType &arr) { external_constructor<value_t::object>::construct(j, arr); } template <typename Json> -void from_json(Json const& j, typename Json::boolean_t& b) +void from_json(const Json & j, typename Json::boolean_t& b) { if (!j.is_boolean()) throw std::domain_error("type must be boolean, but is " + type_name(j)); - b = *const_cast<Json&>(j).template get_ptr<typename Json::boolean_t*>(); + b = *j.template get_ptr<const typename Json::boolean_t*>(); } template <typename Json> -void from_json(Json const& j, typename Json::string_t& s) +void from_json(const Json & j, typename Json::string_t& s) { if (!j.is_string()) throw std::domain_error("type must be string, but is " + type_name(j)); - s = *const_cast<Json&>(j).template get_ptr<typename Json::string_t*>(); + s = *j.template get_ptr<const typename Json::string_t*>(); } template <typename Json> -void from_json(Json const& j, typename Json::number_float_t& val) +void from_json(const Json & j, typename Json::number_float_t& val) { get_arithmetic_value(j, val); } template <typename Json> -void from_json(Json const& j, typename Json::number_unsigned_t& val) +void from_json(const Json & j, typename Json::number_unsigned_t& val) { get_arithmetic_value(j, val); } template <typename Json> -void from_json(Json const& j, typename Json::number_integer_t& val) +void from_json(const Json & j, typename Json::number_integer_t& val) { get_arithmetic_value(j, val); } template <typename Json, typename UnscopedEnumType, enable_if_t<is_unscoped_enum<UnscopedEnumType>::value, int> = 0> -void from_json(Json const &j, UnscopedEnumType& e) +void from_json(const Json &j, UnscopedEnumType& e) { typename std::underlying_type<UnscopedEnumType>::type val = e; get_arithmetic_value(j, val); @@ -617,16 +623,16 @@ void from_json(Json const &j, UnscopedEnumType& e) } template <typename Json> -void from_json(Json const &j, typename Json::array_t &arr) +void from_json(const Json &j, typename Json::array_t &arr) { if (!j.is_array()) throw std::domain_error("type must be array, but is " + type_name(j)); - arr = *const_cast<Json&>(j).template get_ptr<typename Json::array_t*>(); + arr = *j.template get_ptr<const typename Json::array_t*>(); } // forward_list doesn't have an insert method, TODO find a way to avoid including forward_list template <typename Json, typename T, typename Allocator> -void from_json(Json const&j, std::forward_list<T, Allocator>& l) +void from_json(const Json &j, std::forward_list<T, Allocator>& l) { // do not perform the check when user wants to retrieve jsons // (except when it's null.. ?) @@ -641,13 +647,47 @@ void from_json(Json const&j, std::forward_list<T, Allocator>& l) l.push_front(it->template get<T>()); } +template <typename Json, typename CompatibleArrayType> +void from_json_array_impl(const Json &j, CompatibleArrayType &arr, priority_tag<0>) +{ + using std::begin; + using std::end; + + std::transform( + j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json &i) + { + // get<Json>() returns *this, this won't call a from_json method when + // value_type is Json + return i.template get<typename CompatibleArrayType::value_type>(); + }); +} + +template <typename Json, typename CompatibleArrayType> +auto from_json_array_impl(const Json &j, CompatibleArrayType &arr, priority_tag<1>) + -> decltype( + arr.reserve(std::declval<typename CompatibleArrayType::size_type>()), + void()) +{ + using std::begin; + using std::end; + + arr.reserve(j.size()); + std::transform( + j.begin(), j.end(), std::inserter(arr, end(arr)), [](const Json &i) + { + // get<Json>() returns *this, this won't call a from_json method when + // value_type is Json + return i.template get<typename CompatibleArrayType::value_type>(); + }); +} + template < typename Json, typename CompatibleArrayType, enable_if_t<is_compatible_array_type<Json, CompatibleArrayType>::value and not std::is_same<typename Json::array_t, CompatibleArrayType>::value, int> = 0> -void from_json(Json const &j, CompatibleArrayType &arr) +void from_json(const Json &j, CompatibleArrayType &arr) { if (j.is_null()) throw std::domain_error("type must be array, but is " + type_name(j)); @@ -657,16 +697,7 @@ void from_json(Json const &j, CompatibleArrayType &arr) if (!j.is_array()) throw std::domain_error("type must be array, but is " + type_name(j)); } - - using std::begin; - using std::end; - std::transform( - j.begin(), j.end(), std::inserter(arr, end(arr)), [](Json const &i) - { - // get<Json>() returns *this, this won't call a from_json method when - // value_type is Json - return i.template get<typename CompatibleArrayType::value_type>(); - }); + from_json_array_impl(j, arr, priority_tag<1>{}); } @@ -674,12 +705,12 @@ template < typename Json, typename CompatibleObjectType, enable_if_t<is_compatible_object_type<Json, CompatibleObjectType>::value, int> = 0> -void from_json(Json const &j, CompatibleObjectType &obj) +void from_json(const Json &j, CompatibleObjectType &obj) { if (!j.is_object()) throw std::domain_error("type must be object, but is " + type_name(j)); - auto inner_object = const_cast<Json&>(j).template get_ptr<typename Json::object_t*>(); + auto inner_object = j.template get_ptr<const typename Json::object_t*>(); using std::begin; using std::end; // we could avoid the assignment, but this might require a for loop, which @@ -700,16 +731,16 @@ template < not std::is_same<ArithmeticType, typename Json::number_float_t>::value and not std::is_same<ArithmeticType, typename Json::boolean_t>::value, int> = 0> -void from_json(Json const &j, ArithmeticType &val) +void from_json(const Json &j, ArithmeticType &val) { - if (j.is_number_integer()) - val = *const_cast<Json&>(j).template get_ptr<typename Json::number_integer_t*>(); - else if (j.is_number_unsigned()) - val = *const_cast<Json&>(j).template get_ptr<typename Json::number_unsigned_t*>(); + if (j.is_number_unsigned()) + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_unsigned_t*>()); + else if (j.is_number_integer()) + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_integer_t*>()); else if (j.is_number_float()) - val = *const_cast<Json&>(j).template get_ptr<typename Json::number_float_t*>(); + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::number_float_t*>()); else if (j.is_boolean()) - val = *const_cast<Json&>(j).template get_ptr<typename Json::boolean_t*>(); + val = static_cast<ArithmeticType>(*j.template get_ptr<const typename Json::boolean_t*>()); else throw std::domain_error("type must be number, but is " + type_name(j)); } @@ -717,24 +748,53 @@ void from_json(Json const &j, ArithmeticType &val) struct to_json_fn { template <typename Json, typename T> - constexpr auto operator()(Json&& j, T&& val) const - noexcept(noexcept(to_json(std::forward<Json>(j), std::forward<T>(val)))) - -> decltype(to_json(std::forward<Json>(j), std::forward<T>(val)), + auto call(Json& j, T&& val, priority_tag<1>) const + noexcept(noexcept(to_json(j, std::forward<T>(val)))) + -> decltype(to_json(j, std::forward<T>(val)), void()) { - return to_json(std::forward<Json>(j), std::forward<T>(val)); + return to_json(j, std::forward<T>(val)); } + + template <typename Json, typename T> + void call(Json&, T&&, priority_tag<0>) const noexcept + { + static_assert(sizeof(Json) == 0, "to_json method in T's namespace can not be called"); + } + +public: + template <typename Json, typename T> + void operator()(Json &j, T &&val) const + noexcept(noexcept(std::declval<to_json_fn>().call(j, std::forward<T>(val), priority_tag<1>{}))) + { + return call(j, std::forward<T>(val), priority_tag<1>{}); + } }; struct from_json_fn { - template <typename Json, typename T> - constexpr auto operator()(Json&& j, T& val) const - noexcept(noexcept(from_json(std::forward<Json>(j), val))) - -> decltype(from_json(std::forward<Json>(j), val), void()) - { - return from_json(std::forward<Json>(j), val); - } +private: + template <typename Json, typename T> + auto call(const Json &j, T &val, priority_tag<1>) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } + + template <typename Json, typename T> + void call(const Json &, T&, priority_tag<0>) const noexcept + { + static_assert(sizeof(Json) == 0, "from_json method in T's namespace can not be called"); + } + +public: + template <typename Json, typename T> + void operator()(const Json &j, T &val) const + noexcept(noexcept(std::declval<from_json_fn>().call(j, val, priority_tag<1>{}))) + { + return call(j, val, priority_tag<1>{}); + } }; /*! @@ -756,7 +816,6 @@ struct DecimalSeparator : std::numpunct<char> return '.'; } }; - } // taken from ranges-v3 @@ -772,8 +831,8 @@ constexpr T static_const<T>::value; inline namespace { -constexpr auto const& to_json = static_const<detail::to_json_fn>::value; -constexpr auto const& from_json = static_const<detail::from_json_fn>::value; +constexpr const auto & to_json = static_const<detail::to_json_fn>::value; +constexpr const auto & from_json = static_const<detail::from_json_fn>::value; } // default JSONSerializer template argument, doesn't care about template argument @@ -788,9 +847,10 @@ struct adl_serializer } template <typename Json, typename T> - static void to_json(Json& j, T&& val) + static void to_json(Json &j, T &&val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward<T>(val)))) { - ::nlohmann::to_json(j, std::forward<T>(val)); + ::nlohmann::to_json(j, std::forward<T>(val)); } }; @@ -888,7 +948,7 @@ class basic_json { private: template <::nlohmann::value_t> friend struct detail::external_constructor; - template <typename Json> friend std::string detail::type_name(Json const &); + template <typename Json> friend std::string detail::type_name(const Json &); /// workaround type for MSVC using basic_json_t = basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, @@ -2967,16 +3027,16 @@ class basic_json uncvref_t<T>>::value, int> = 0> // do we really want the uncvref ? if a user call get<int &>, shouldn't we - // i know there is a special behaviour for boolean_t* and such // static assert ? + // i know there is a special behaviour for boolean_t* and such auto get() const noexcept(noexcept(JSONSerializer<uncvref_t<T>>::from_json( - std::declval<basic_json_t const &>(), std::declval<uncvref_t<T> &>()))) + std::declval<const basic_json_t &>(), std::declval<uncvref_t<T> &>()))) -> uncvref_t<T> { using type = uncvref_t<T>; - static_assert(std::is_default_constructible<type>::value && + static_assert(std::is_default_constructible<type>::value and std::is_copy_constructible<type>::value, - "user-defined types must be DefaultConstructible and " + "Types must be DefaultConstructible and " "CopyConstructible when used with get"); type ret; JSONSerializer<type>::from_json(*this, ret); @@ -2992,12 +3052,11 @@ class basic_json detail::has_non_default_from_json<basic_json_t, uncvref_t<T>>::value, int> = 0> - constexpr uncvref_t<T> get() const noexcept(noexcept(JSONSerializer<T>::from_json(std::declval<basic_json_t const&>()))) + uncvref_t<T> get() const noexcept(noexcept(JSONSerializer<T>::from_json(std::declval<const basic_json_t &>()))) { return JSONSerializer<T>::from_json(*this); } - // TODO what to do with those... /*! @brief get a pointer value (explicit)