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 std::string type_name(Json const &j) +template std::string type_name(const Json &j) { switch (j.m_type) { @@ -189,6 +189,11 @@ template std::string type_name(Json const &j) } } +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag {}; + +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 struct external_constructor; @@ -491,14 +496,15 @@ template ::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(j).template get_ptr(); - else if (j.is_number_unsigned()) - val = *const_cast(j).template get_ptr(); + // unsigned must be checked first, since is_number_integer() == true for unsigned + if (j.is_number_unsigned()) + val = static_cast(*j.template get_ptr()); + else if (j.is_number_integer()) + val = static_cast(*j.template get_ptr()); else if (j.is_number_float()) - val = *const_cast(j).template get_ptr(); + val = static_cast(*j.template get_ptr()); else throw std::domain_error("type must be number, but is " + type_name(j)); } @@ -522,7 +528,7 @@ template ::value, int> = 0> void to_json(Json &j, FloatType val) noexcept { - external_constructor::construct(j, val); + external_constructor::construct(j, static_cast(val)); } @@ -533,7 +539,7 @@ template < int> = 0> void to_json(Json &j, CompatibleNumberUnsignedType val) noexcept { - external_constructor::construct(j, val); + external_constructor::construct(j, static_cast(val)); } template < @@ -543,7 +549,7 @@ template < int> = 0> void to_json(Json &j, CompatibleNumberIntegerType val) noexcept { - external_constructor::construct(j, val); + external_constructor::construct(j, static_cast(val)); } template ::value or std::is_same::value, int> = 0> -void to_json(Json &j, CompatibleArrayType const &arr) +void to_json(Json &j, const CompatibleArrayType &arr) { external_constructor::construct(j, arr); } @@ -568,48 +574,48 @@ template < typename Json, typename CompatibleObjectType, enable_if_t::value, int> = 0> -void to_json(Json &j, CompatibleObjectType const &arr) +void to_json(Json &j, const CompatibleObjectType &arr) { external_constructor::construct(j, arr); } template -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(j).template get_ptr(); + b = *j.template get_ptr(); } template -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(j).template get_ptr(); + s = *j.template get_ptr(); } template -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 -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 -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 ::value, int> = 0> -void from_json(Json const &j, UnscopedEnumType& e) +void from_json(const Json &j, UnscopedEnumType& e) { typename std::underlying_type::type val = e; get_arithmetic_value(j, val); @@ -617,16 +623,16 @@ void from_json(Json const &j, UnscopedEnumType& e) } template -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(j).template get_ptr(); + arr = *j.template get_ptr(); } // forward_list doesn't have an insert method, TODO find a way to avoid including forward_list template -void from_json(Json const&j, std::forward_list& l) +void from_json(const Json &j, std::forward_list& 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& l) l.push_front(it->template get()); } +template +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() returns *this, this won't call a from_json method when + // value_type is Json + return i.template get(); + }); +} + +template +auto from_json_array_impl(const Json &j, CompatibleArrayType &arr, priority_tag<1>) + -> decltype( + arr.reserve(std::declval()), + 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() returns *this, this won't call a from_json method when + // value_type is Json + return i.template get(); + }); +} + template < typename Json, typename CompatibleArrayType, enable_if_t::value and not std::is_same::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() returns *this, this won't call a from_json method when - // value_type is Json - return i.template get(); - }); + from_json_array_impl(j, arr, priority_tag<1>{}); } @@ -674,12 +705,12 @@ template < typename Json, typename CompatibleObjectType, enable_if_t::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(j).template get_ptr(); + auto inner_object = j.template get_ptr(); 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::value and not std::is_same::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(j).template get_ptr(); - else if (j.is_number_unsigned()) - val = *const_cast(j).template get_ptr(); + if (j.is_number_unsigned()) + val = static_cast(*j.template get_ptr()); + else if (j.is_number_integer()) + val = static_cast(*j.template get_ptr()); else if (j.is_number_float()) - val = *const_cast(j).template get_ptr(); + val = static_cast(*j.template get_ptr()); else if (j.is_boolean()) - val = *const_cast(j).template get_ptr(); + val = static_cast(*j.template get_ptr()); 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 - constexpr auto operator()(Json&& j, T&& val) const - noexcept(noexcept(to_json(std::forward(j), std::forward(val)))) - -> decltype(to_json(std::forward(j), std::forward(val)), + auto call(Json& j, T&& val, priority_tag<1>) const + noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) { - return to_json(std::forward(j), std::forward(val)); + return to_json(j, std::forward(val)); } + + template + 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 + void operator()(Json &j, T &&val) const + noexcept(noexcept(std::declval().call(j, std::forward(val), priority_tag<1>{}))) + { + return call(j, std::forward(val), priority_tag<1>{}); + } }; struct from_json_fn { - template - constexpr auto operator()(Json&& j, T& val) const - noexcept(noexcept(from_json(std::forward(j), val))) - -> decltype(from_json(std::forward(j), val), void()) - { - return from_json(std::forward(j), val); - } +private: + template + 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 + 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 + void operator()(const Json &j, T &val) const + noexcept(noexcept(std::declval().call(j, val, priority_tag<1>{}))) + { + return call(j, val, priority_tag<1>{}); + } }; /*! @@ -756,7 +816,6 @@ struct DecimalSeparator : std::numpunct return '.'; } }; - } // taken from ranges-v3 @@ -772,8 +831,8 @@ constexpr T static_const::value; inline namespace { -constexpr auto const& to_json = static_const::value; -constexpr auto const& from_json = static_const::value; +constexpr const auto & to_json = static_const::value; +constexpr const auto & from_json = static_const::value; } // default JSONSerializer template argument, doesn't care about template argument @@ -788,9 +847,10 @@ struct adl_serializer } template - static void to_json(Json& j, T&& val) + static void to_json(Json &j, T &&val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) { - ::nlohmann::to_json(j, std::forward(val)); + ::nlohmann::to_json(j, std::forward(val)); } }; @@ -888,7 +948,7 @@ class basic_json { private: template <::nlohmann::value_t> friend struct detail::external_constructor; - template friend std::string detail::type_name(Json const &); + template friend std::string detail::type_name(const Json &); /// workaround type for MSVC using basic_json_t = basic_json>::value, int> = 0> // do we really want the uncvref ? if a user call get, 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>::from_json( - std::declval(), std::declval &>()))) + std::declval(), std::declval &>()))) -> uncvref_t { using type = uncvref_t; - static_assert(std::is_default_constructible::value && + static_assert(std::is_default_constructible::value and std::is_copy_constructible::value, - "user-defined types must be DefaultConstructible and " + "Types must be DefaultConstructible and " "CopyConstructible when used with get"); type ret; JSONSerializer::from_json(*this, ret); @@ -2992,12 +3052,11 @@ class basic_json detail::has_non_default_from_json>::value, int> = 0> - constexpr uncvref_t get() const noexcept(noexcept(JSONSerializer::from_json(std::declval()))) + uncvref_t get() const noexcept(noexcept(JSONSerializer::from_json(std::declval()))) { return JSONSerializer::from_json(*this); } - // TODO what to do with those... /*! @brief get a pointer value (explicit)