From 04b4d79ec22b3b08534bc3191da45232f15a5993 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Thu, 5 May 2022 19:54:53 +0200 Subject: [PATCH] Add casting placeholder --- include/nlohmann/detail/meta/cpp_future.hpp | 15 ++ include/nlohmann/detail/meta/invoke.hpp | 51 ++++-- include/nlohmann/detail/meta/type_traits.hpp | 18 ++- include/nlohmann/detail/placeholders.hpp | 33 ++-- include/nlohmann/json.hpp | 43 +++-- single_include/nlohmann/json.hpp | 161 ++++++++++++++----- 6 files changed, 245 insertions(+), 76 deletions(-) diff --git a/include/nlohmann/detail/meta/cpp_future.hpp b/include/nlohmann/detail/meta/cpp_future.hpp index 66243a822..64017c5bf 100644 --- a/include/nlohmann/detail/meta/cpp_future.hpp +++ b/include/nlohmann/detail/meta/cpp_future.hpp @@ -176,6 +176,21 @@ void as_const(const T&&) = delete; #endif +#ifdef JSON_HAS_CPP_20 + +// the following utilities are natively available in C++20 +using std::type_identity; + +#else + +template +struct type_identity +{ + using type = T; +}; + +#endif + // dispatch utility (taken from ranges-v3) template struct priority_tag : priority_tag < N - 1 > {}; template<> struct priority_tag<0> {}; diff --git a/include/nlohmann/detail/meta/invoke.hpp b/include/nlohmann/detail/meta/invoke.hpp index cf42a671f..2b2a755eb 100644 --- a/include/nlohmann/detail/meta/invoke.hpp +++ b/include/nlohmann/detail/meta/invoke.hpp @@ -1,9 +1,10 @@ #pragma once +#include "nlohmann/detail/meta/cpp_future.hpp" #ifdef JSON_HAS_CPP_17 #include // invoke #endif -#include // invoke_result, is_base_of, is_function, is_invocable, is_object, is_same, remove_reference +#include // is_base_of, is_function, is_object, is_same, remove_reference #include // declval, forward #include @@ -15,15 +16,6 @@ namespace nlohmann namespace detail { -#ifdef JSON_HAS_CPP_17 - -// the following utilities are natively available in C++17 -using std::invoke; -using std::invoke_result; -using std::is_invocable; - -#else - // invoke_impl derived from the C++ standard draft [func.require] for qslib (proprietary) // modified to use standard library type traits and utilities where possible @@ -61,6 +53,14 @@ auto invoke_impl(T U::*f, V && v, Args && ... /*unused*/) -> decltype((*v).*f) return (*v).*f; } +// allow setting values by "invoking" data member pointers with one argument +template < typename T, typename U, typename V, typename Arg, enable_if_t< + std::is_object::value, int> = 0> +auto invoke_impl(T U::*f, V && v, Arg && arg) -> decltype((*v).*f = std::forward(arg)) +{ + return (*v).*f = std::forward(arg); +} + template using detect_invocable = decltype(invoke_impl(std::declval(), std::declval()...)); @@ -88,8 +88,6 @@ using invoke_result = internal::invoke_result; template using is_invocable = typename is_detected::type; -#endif - // used as a dummy argument struct null_arg_t { @@ -101,11 +99,38 @@ static constexpr null_arg_t null_arg{}; template using is_null_arg = typename std::is_same, null_arg_t>::type; +template +using detect_type = typename T::type; + +template::value> +struct apply_value_or_value_as_if_castable +{ + using type = Value; +}; + +template +struct apply_value_or_value_as_if_castable +{ + using as_type = typename Arg::type; + using type = typename std::conditional < + detail::is_static_castable::value, + as_type, Value >::type; +}; + +template +using apply_value_or_value_as_t = typename std::conditional < + is_basic_json_value_as_placeholder::value, + typename apply_value_or_value_as_if_castable>::type, + Value >::type; + template struct apply_value_or_arg { using element_type = typename std::tuple_element::type; - using type = typename std::conditional::value, Value, element_type>::type; + using type = typename std::conditional < + is_any_basic_json_value_placeholder::value, + apply_value_or_value_as_t, + element_type >::type; }; template diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index edc3c8e2a..5e401f263 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -557,8 +557,22 @@ struct is_ordered_map // checks if a type is a basic_json_value placeholder template -using is_basic_json_value_placeholder = typename std::is_same < - uncvref_t, uncvref_t>::type; +using is_basic_json_value_placeholder = + typename std::is_same, ::nlohmann::placeholders::basic_json_value_placeholder_t>::type; + +template +using is_basic_json_value_as_placeholder = + typename is_specialization_of<::nlohmann::placeholders::basic_json_value_as_placeholder_t, uncvref_t>::type; + +template +using is_any_basic_json_value_placeholder = + typename disjunction, is_basic_json_value_as_placeholder>::type; + +template +using detect_is_static_castable = decltype(static_cast(std::declval())); + +template +using is_static_castable = is_detected; // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > diff --git a/include/nlohmann/detail/placeholders.hpp b/include/nlohmann/detail/placeholders.hpp index b502520cc..b2a178d80 100644 --- a/include/nlohmann/detail/placeholders.hpp +++ b/include/nlohmann/detail/placeholders.hpp @@ -2,23 +2,28 @@ namespace nlohmann { -namespace detail -{ - -template -struct placeholder_t -{ - static constexpr int value = I; - - explicit placeholder_t() = default; -}; - -} // namespace detail - namespace placeholders { -static constexpr detail::placeholder_t < -1 > basic_json_value{}; +struct basic_json_value_placeholder_t +{ + explicit basic_json_value_placeholder_t() = default; +}; + +static constexpr basic_json_value_placeholder_t basic_json_value{}; + +template +struct basic_json_value_as_placeholder_t +{ + using type = T; + explicit basic_json_value_as_placeholder_t() = default; +}; + +template +inline constexpr basic_json_value_as_placeholder_t basic_json_value_as() noexcept +{ + return basic_json_value_as_placeholder_t {}; +} } // namespace placeholders } // namespace nlohmann diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 765b5dba5..5b45afa70 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -214,6 +214,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static constexpr decltype(::nlohmann::placeholders::basic_json_value) value_placeholder = ::nlohmann::placeholders::basic_json_value; + template + static constexpr auto value_as_placeholder() + noexcept -> decltype(::nlohmann::placeholders::basic_json_value_as()) + { + return ::nlohmann::placeholders::basic_json_value_as(); + } + //////////////// // exceptions // //////////////// @@ -5131,14 +5138,32 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec private: template::value, int> = 0> - static auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) -> decltype(std::forward(val)) + auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(std::forward(val)) { return std::forward(val); } + template < typename Arg, typename Value, typename AsType = typename detail::uncvref_t::type, + detail::enable_if_t < detail::is_basic_json_value_as_placeholder::value + && detail::is_static_castable::value, int > = 0 > + auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(static_cast(std::forward(val))) + { + return static_cast(std::forward(val)); + } + + template < typename Arg, typename Value, typename AsType = typename detail::uncvref_t::type, + detail::enable_if_t < detail::is_basic_json_value_as_placeholder::value + && !detail::is_static_castable::value, int > = 0 > + //static auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) -> decltype(std::forward(val)) + auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(std::forward(val)) + { + JSON_THROW(type_error::create(399, detail::concat("cannot cast to requested type"), this)); + return std::forward(val); + } + template < typename Arg, typename Value, - detail::enable_if_t < !detail::is_basic_json_value_placeholder::value, int > = 0 > - static auto apply_resolve_placeholder(Arg && arg, Value&& /*val*/) -> decltype(std::forward(arg)) + detail::enable_if_t < !detail::is_any_basic_json_value_placeholder::value, int > = 0 > + auto apply_resolve_placeholder(Arg && arg, Value&& /*val*/) const -> decltype(std::forward(arg)) { return std::forward(arg); } @@ -5257,31 +5282,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // convert arguments to tuple; insert basic_json_value placeholder if missing template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename FnArg, typename... Args, detail::enable_if_t < std::is_member_pointer::value - && !detail::is_basic_json_value_placeholder::value - && !detail::disjunction...>::value, int > = 0 > + && !detail::is_any_basic_json_value_placeholder::value + && !detail::disjunction...>::value, int > = 0 > void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, FnArg && f_arg, Args && ... args) const { apply_invoke( std::forward(val), std::forward(cb), std::forward(cb_arg), - std::forward(f), std::forward_as_tuple(f_arg, ::nlohmann::placeholders::basic_json_value, apply_maybe_wrap_arg(args)...), + std::forward(f), std::forward_as_tuple(f_arg, value_placeholder, apply_maybe_wrap_arg(args)...), detail::make_index_sequence < 2 + sizeof...(args) > ()); } template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args, detail::enable_if_t < !std::is_member_pointer::value - && !detail::disjunction...>::value, int > = 0 > + && !detail::disjunction...>::value, int > = 0 > void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const { apply_invoke( std::forward(val), std::forward(cb), std::forward(cb_arg), - std::forward(f), std::forward_as_tuple(::nlohmann::placeholders::basic_json_value, apply_maybe_wrap_arg(args)...), + std::forward(f), std::forward_as_tuple(value_placeholder, apply_maybe_wrap_arg(args)...), detail::make_index_sequence < 1 + sizeof...(args) > ()); } template...>::value, int> = 0> + detail::enable_if_t...>::value, int> = 0> void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const { apply_invoke( diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a9ff34111..b89fca8a6 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3064,6 +3064,21 @@ void as_const(const T&&) = delete; #endif +#ifdef JSON_HAS_CPP_20 + +// the following utilities are natively available in C++20 +using std::type_identity; + +#else + +template +struct type_identity +{ + using type = T; +}; + +#endif + // dispatch utility (taken from ranges-v3) template struct priority_tag : priority_tag < N - 1 > {}; template<> struct priority_tag<0> {}; @@ -3182,23 +3197,28 @@ NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); namespace nlohmann { -namespace detail -{ - -template -struct placeholder_t -{ - static constexpr int value = I; - - explicit placeholder_t() = default; -}; - -} // namespace detail - namespace placeholders { -static constexpr detail::placeholder_t < -1 > basic_json_value{}; +struct basic_json_value_placeholder_t +{ + explicit basic_json_value_placeholder_t() = default; +}; + +static constexpr basic_json_value_placeholder_t basic_json_value{}; + +template +struct basic_json_value_as_placeholder_t +{ + using type = T; + explicit basic_json_value_as_placeholder_t() = default; +}; + +template +inline constexpr basic_json_value_as_placeholder_t basic_json_value_as() noexcept +{ + return basic_json_value_as_placeholder_t {}; +} } // namespace placeholders } // namespace nlohmann @@ -3812,8 +3832,22 @@ struct is_ordered_map // checks if a type is a basic_json_value placeholder template -using is_basic_json_value_placeholder = typename std::is_same < - uncvref_t, uncvref_t>::type; +using is_basic_json_value_placeholder = + typename std::is_same, ::nlohmann::placeholders::basic_json_value_placeholder_t>::type; + +template +using is_basic_json_value_as_placeholder = + typename is_specialization_of<::nlohmann::placeholders::basic_json_value_as_placeholder_t, uncvref_t>::type; + +template +using is_any_basic_json_value_placeholder = + typename disjunction, is_basic_json_value_as_placeholder>::type; + +template +using detect_is_static_castable = decltype(static_cast(std::declval())); + +template +using is_static_castable = is_detected; // to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > @@ -14030,10 +14064,12 @@ class json_ref // #include +// #include "nlohmann/detail/meta/cpp_future.hpp" + #ifdef JSON_HAS_CPP_17 #include // invoke #endif -#include // invoke_result, is_base_of, is_function, is_invocable, is_object, is_same, remove_reference +#include // is_base_of, is_function, is_object, is_same, remove_reference #include // declval, forward // #include @@ -14048,15 +14084,6 @@ namespace nlohmann namespace detail { -#ifdef JSON_HAS_CPP_17 - -// the following utilities are natively available in C++17 -using std::invoke; -using std::invoke_result; -using std::is_invocable; - -#else - // invoke_impl derived from the C++ standard draft [func.require] for qslib (proprietary) // modified to use standard library type traits and utilities where possible @@ -14094,6 +14121,14 @@ auto invoke_impl(T U::*f, V && v, Args && ... /*unused*/) -> decltype((*v).*f) return (*v).*f; } +// allow setting values by "invoking" data member pointers with one argument +template < typename T, typename U, typename V, typename Arg, enable_if_t< + std::is_object::value, int> = 0> +auto invoke_impl(T U::*f, V && v, Arg && arg) -> decltype((*v).*f = std::forward(arg)) +{ + return (*v).*f = std::forward(arg); +} + template using detect_invocable = decltype(invoke_impl(std::declval(), std::declval()...)); @@ -14121,8 +14156,6 @@ using invoke_result = internal::invoke_result; template using is_invocable = typename is_detected::type; -#endif - // used as a dummy argument struct null_arg_t { @@ -14134,11 +14167,38 @@ static constexpr null_arg_t null_arg{}; template using is_null_arg = typename std::is_same, null_arg_t>::type; +template +using detect_type = typename T::type; + +template::value> +struct apply_value_or_value_as_if_castable +{ + using type = Value; +}; + +template +struct apply_value_or_value_as_if_castable +{ + using as_type = typename Arg::type; + using type = typename std::conditional < + detail::is_static_castable::value, + as_type, Value >::type; +}; + +template +using apply_value_or_value_as_t = typename std::conditional < + is_basic_json_value_as_placeholder::value, + typename apply_value_or_value_as_if_castable>::type, + Value >::type; + template struct apply_value_or_arg { using element_type = typename std::tuple_element::type; - using type = typename std::conditional::value, Value, element_type>::type; + using type = typename std::conditional < + is_any_basic_json_value_placeholder::value, + apply_value_or_value_as_t, + element_type >::type; }; template @@ -18594,6 +18654,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec static constexpr decltype(::nlohmann::placeholders::basic_json_value) value_placeholder = ::nlohmann::placeholders::basic_json_value; + template + static constexpr auto value_as_placeholder() + noexcept -> decltype(::nlohmann::placeholders::basic_json_value_as()) + { + return ::nlohmann::placeholders::basic_json_value_as(); + } + //////////////// // exceptions // //////////////// @@ -23511,14 +23578,32 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec private: template::value, int> = 0> - static auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) -> decltype(std::forward(val)) + auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(std::forward(val)) { return std::forward(val); } + template < typename Arg, typename Value, typename AsType = typename detail::uncvref_t::type, + detail::enable_if_t < detail::is_basic_json_value_as_placeholder::value + && detail::is_static_castable::value, int > = 0 > + auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(static_cast(std::forward(val))) + { + return static_cast(std::forward(val)); + } + + template < typename Arg, typename Value, typename AsType = typename detail::uncvref_t::type, + detail::enable_if_t < detail::is_basic_json_value_as_placeholder::value + && !detail::is_static_castable::value, int > = 0 > + //static auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) -> decltype(std::forward(val)) + auto apply_resolve_placeholder(Arg && /*arg*/, Value && val) const -> decltype(std::forward(val)) + { + JSON_THROW(type_error::create(399, detail::concat("cannot cast to requested type"), this)); + return std::forward(val); + } + template < typename Arg, typename Value, - detail::enable_if_t < !detail::is_basic_json_value_placeholder::value, int > = 0 > - static auto apply_resolve_placeholder(Arg && arg, Value&& /*val*/) -> decltype(std::forward(arg)) + detail::enable_if_t < !detail::is_any_basic_json_value_placeholder::value, int > = 0 > + auto apply_resolve_placeholder(Arg && arg, Value&& /*val*/) const -> decltype(std::forward(arg)) { return std::forward(arg); } @@ -23637,31 +23722,31 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec // convert arguments to tuple; insert basic_json_value placeholder if missing template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename FnArg, typename... Args, detail::enable_if_t < std::is_member_pointer::value - && !detail::is_basic_json_value_placeholder::value - && !detail::disjunction...>::value, int > = 0 > + && !detail::is_any_basic_json_value_placeholder::value + && !detail::disjunction...>::value, int > = 0 > void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, FnArg && f_arg, Args && ... args) const { apply_invoke( std::forward(val), std::forward(cb), std::forward(cb_arg), - std::forward(f), std::forward_as_tuple(f_arg, ::nlohmann::placeholders::basic_json_value, apply_maybe_wrap_arg(args)...), + std::forward(f), std::forward_as_tuple(f_arg, value_placeholder, apply_maybe_wrap_arg(args)...), detail::make_index_sequence < 2 + sizeof...(args) > ()); } template < bool ConstThis, typename Value, typename ResultCallback, typename CallbackArg, typename Fn, typename... Args, detail::enable_if_t < !std::is_member_pointer::value - && !detail::disjunction...>::value, int > = 0 > + && !detail::disjunction...>::value, int > = 0 > void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const { apply_invoke( std::forward(val), std::forward(cb), std::forward(cb_arg), - std::forward(f), std::forward_as_tuple(::nlohmann::placeholders::basic_json_value, apply_maybe_wrap_arg(args)...), + std::forward(f), std::forward_as_tuple(value_placeholder, apply_maybe_wrap_arg(args)...), detail::make_index_sequence < 1 + sizeof...(args) > ()); } template...>::value, int> = 0> + detail::enable_if_t...>::value, int> = 0> void apply_make_tuple(Value && val, ResultCallback && cb, CallbackArg && cb_arg, Fn && f, Args && ... args) const { apply_invoke(