From 0c7a18374ce6549b020eb366061fbdcf21096a78 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Sun, 7 Aug 2022 13:54:55 +0200 Subject: [PATCH] Reimplement value() access functions (#3663) * Reimplement value() access functions * Merges the 'const char *' with the 'ValueType &&' overloads. * Fixes ambiguities when default value is 0. * Fixes 'no matching function' error when specifying ValueType template parameter. * Fixes incorrect template parameter order in previous overloads. * Add additional value() tests * Make JSON_MultipleHeaders visible to unit tests Define the macro JSON_TEST_USING_MULTIPLE_HEADERS to 0/1 depending on JSON_MultipleHeaders. * Add type_traits unit test * Update documentation --- docs/mkdocs/docs/api/basic_json/value.md | 4 +- include/nlohmann/detail/meta/type_traits.hpp | 52 ++++ include/nlohmann/json.hpp | 175 +++++++---- single_include/nlohmann/json.hpp | 227 ++++++++++---- tests/CMakeLists.txt | 3 +- tests/src/unit-element_access2.cpp | 304 +++++++++++++++++++ tests/src/unit-type_traits.cpp | 56 ++++ 7 files changed, 694 insertions(+), 127 deletions(-) create mode 100644 tests/src/unit-type_traits.cpp diff --git a/docs/mkdocs/docs/api/basic_json/value.md b/docs/mkdocs/docs/api/basic_json/value.md index b5fd14d1c..edb5406ba 100644 --- a/docs/mkdocs/docs/api/basic_json/value.md +++ b/docs/mkdocs/docs/api/basic_json/value.md @@ -7,7 +7,7 @@ ValueType value(const typename object_t::key_type& key, ValueType&& default_value) const; // (2) -template +template ValueType value(KeyType&& key, ValueType&& default_value) const; @@ -155,5 +155,5 @@ changes to any JSON value. ## Version history 1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0. -2. Added in version 3.11.0. +2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2. 3. Added in version 2.0.2. diff --git a/include/nlohmann/detail/meta/type_traits.hpp b/include/nlohmann/detail/meta/type_traits.hpp index 2ec9d39a5..3df94b851 100644 --- a/include/nlohmann/detail/meta/type_traits.hpp +++ b/include/nlohmann/detail/meta/type_traits.hpp @@ -684,5 +684,57 @@ inline constexpr bool value_in_range_of(T val) return value_in_range_of_impl1::test(val); } +template +using bool_constant = std::integral_constant; + +/////////////////////////////////////////////////////////////////////////////// +// is_c_string +/////////////////////////////////////////////////////////////////////////////// + +namespace impl +{ + +template +inline constexpr bool is_c_string() +{ + using TUnExt = typename std::remove_extent::type; + using TUnCVExt = typename std::remove_cv::type; + using TUnPtr = typename std::remove_pointer::type; + using TUnCVPtr = typename std::remove_cv::type; + return + (std::is_array::value && std::is_same::value) + || (std::is_pointer::value && std::is_same::value); +} + +} // namespace impl + +// checks whether T is a [cv] char */[cv] char[] C string +template +struct is_c_string : bool_constant()> {}; + +template +using is_c_string_uncvref = is_c_string>; + +/////////////////////////////////////////////////////////////////////////////// +// is_transparent +/////////////////////////////////////////////////////////////////////////////// + +namespace impl +{ + +template +inline constexpr bool is_transparent() +{ + return is_detected::value; +} + +} // namespace impl + +// checks whether T has a member named is_transparent +template +struct is_transparent : bool_constant()> {}; + +/////////////////////////////////////////////////////////////////////////////// + } // namespace detail NLOHMANN_JSON_NAMESPACE_END diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 49188b314..5fda88f26 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -2194,14 +2194,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } + private: + template + using is_comparable_with_object_key = detail::is_comparable < + object_comparator_t, const typename object_t::key_type&, KeyType >; + + template + using value_return_type = std::conditional < + detail::is_c_string_uncvref::value, + string_t, typename std::decay::type >; + + public: /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ - // this is the value(const typename object_t::key_type&) overload - template < class KeyType, class ValueType, detail::enable_if_t < - std::is_same::value + template < class ValueType, detail::enable_if_t < + !detail::is_transparent::value && detail::is_getable::value - && !std::is_same::value, int > = 0 > - typename std::decay::type value(const KeyType& key, ValueType && default_value) const + && !std::is_same>::value, int > = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -2210,7 +2220,32 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const auto it = find(key); if (it != end()) { - return it->template get::type>(); + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + !detail::is_transparent::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); } return std::forward(default_value); @@ -2221,36 +2256,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ - /// overload for a default value of type const char* - string_t value(const typename object_t::key_type& key, const char* default_value) const - { - return value(key, string_t(default_value)); - } - - // these two functions, in conjunction with value(const KeyType &, ValueType &&), - // resolve an ambiguity that would otherwise occur between the json_pointer and - // typename object_t::key_type & overloads - template < class ValueType, detail::enable_if_t < - detail::is_getable::value - && !std::is_same::value, int > = 0 > - typename std::decay::type value(const char* key, ValueType && default_value) const - { - return value(typename object_t::key_type(key), std::forward(default_value)); - } - - string_t value(const char* key, const char* default_value) const - { - return value(typename object_t::key_type(key), string_t(default_value)); - } - - /// @brief access specified object element with default value - /// @sa https://json.nlohmann.me/api/basic_json/value/ - /// using std::is_convertible in a std::enable_if will fail when using explicit conversions - template < class KeyType, class ValueType, detail::enable_if_t < - detail::is_getable::value - && !std::is_same::value - && detail::is_usable_as_basic_json_key_type::value, int > = 0 > - typename std::decay::type value(KeyType && key, ValueType && default_value) const + template < class ValueType, class KeyType, detail::enable_if_t < + detail::is_transparent::value + && !detail::is_json_pointer::value + && is_comparable_with_object_key::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ValueType value(KeyType && key, const ValueType& default_value) const { // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -2259,7 +2271,34 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const auto it = find(std::forward(key)); if (it != end()) { - return it->template get::type>(); + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class KeyType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_transparent::value + && !detail::is_json_pointer::value + && is_comparable_with_object_key::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(KeyType && key, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(std::forward(key)); + if (it != end()) + { + return it->template get(); } return std::forward(default_value); @@ -2268,20 +2307,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); } - /// @brief access specified object element with default value - /// @sa https://json.nlohmann.me/api/basic_json/value/ - /// overload for a default value of type const char* - template < class KeyType, detail::enable_if_t < - !detail::is_json_pointer::value, int > = 0 > - string_t value(KeyType && key, const char* default_value) const - { - return value(std::forward(key), string_t(default_value)); - } - /// @brief access specified object element via JSON Pointer with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ template < class ValueType, detail::enable_if_t < - detail::is_getable::value, int> = 0 > + detail::is_getable::value + && !std::is_same>::value, int > = 0 > ValueType value(const json_pointer& ptr, const ValueType& default_value) const { // value only works for objects @@ -2301,29 +2331,50 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); } + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(const json_pointer& ptr, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return std::forward(default_value); + } + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + template < class ValueType, class BasicJsonType, detail::enable_if_t < - detail::is_getable::value, int> = 0 > + detail::is_basic_json::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) ValueType value(const ::nlohmann::json_pointer& ptr, const ValueType& default_value) const { return value(ptr.convert(), default_value); } - /// @brief access specified object element via JSON Pointer with default value - /// @sa https://json.nlohmann.me/api/basic_json/value/ - /// overload for a default value of type const char* - JSON_HEDLEY_NON_NULL(3) - string_t value(const json_pointer& ptr, const char* default_value) const - { - return value(ptr, string_t(default_value)); - } - - template + template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_basic_json::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) - JSON_HEDLEY_NON_NULL(3) - string_t value(const typename ::nlohmann::json_pointer& ptr, const char* default_value) const + ReturnType value(const ::nlohmann::json_pointer& ptr, ValueType && default_value) const { - return value(ptr.convert(), default_value); + return value(ptr.convert(), std::forward(default_value)); } /// @brief access the first element diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 6668a173b..7d8d91e2e 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4059,6 +4059,58 @@ inline constexpr bool value_in_range_of(T val) return value_in_range_of_impl1::test(val); } +template +using bool_constant = std::integral_constant; + +/////////////////////////////////////////////////////////////////////////////// +// is_c_string +/////////////////////////////////////////////////////////////////////////////// + +namespace impl +{ + +template +inline constexpr bool is_c_string() +{ + using TUnExt = typename std::remove_extent::type; + using TUnCVExt = typename std::remove_cv::type; + using TUnPtr = typename std::remove_pointer::type; + using TUnCVPtr = typename std::remove_cv::type; + return + (std::is_array::value && std::is_same::value) + || (std::is_pointer::value && std::is_same::value); +} + +} // namespace impl + +// checks whether T is a [cv] char */[cv] char[] C string +template +struct is_c_string : bool_constant()> {}; + +template +using is_c_string_uncvref = is_c_string>; + +/////////////////////////////////////////////////////////////////////////////// +// is_transparent +/////////////////////////////////////////////////////////////////////////////// + +namespace impl +{ + +template +inline constexpr bool is_transparent() +{ + return is_detected::value; +} + +} // namespace impl + +// checks whether T has a member named is_transparent +template +struct is_transparent : bool_constant()> {}; + +/////////////////////////////////////////////////////////////////////////////// + } // namespace detail NLOHMANN_JSON_NAMESPACE_END @@ -21274,14 +21326,24 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this)); } + private: + template + using is_comparable_with_object_key = detail::is_comparable < + object_comparator_t, const typename object_t::key_type&, KeyType >; + + template + using value_return_type = std::conditional < + detail::is_c_string_uncvref::value, + string_t, typename std::decay::type >; + + public: /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ - // this is the value(const typename object_t::key_type&) overload - template < class KeyType, class ValueType, detail::enable_if_t < - std::is_same::value + template < class ValueType, detail::enable_if_t < + !detail::is_transparent::value && detail::is_getable::value - && !std::is_same::value, int > = 0 > - typename std::decay::type value(const KeyType& key, ValueType && default_value) const + && !std::is_same>::value, int > = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const { // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -21290,7 +21352,32 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const auto it = find(key); if (it != end()) { - return it->template get::type>(); + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + !detail::is_transparent::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); } return std::forward(default_value); @@ -21301,36 +21388,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec /// @brief access specified object element with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ - /// overload for a default value of type const char* - string_t value(const typename object_t::key_type& key, const char* default_value) const - { - return value(key, string_t(default_value)); - } - - // these two functions, in conjunction with value(const KeyType &, ValueType &&), - // resolve an ambiguity that would otherwise occur between the json_pointer and - // typename object_t::key_type & overloads - template < class ValueType, detail::enable_if_t < - detail::is_getable::value - && !std::is_same::value, int > = 0 > - typename std::decay::type value(const char* key, ValueType && default_value) const - { - return value(typename object_t::key_type(key), std::forward(default_value)); - } - - string_t value(const char* key, const char* default_value) const - { - return value(typename object_t::key_type(key), string_t(default_value)); - } - - /// @brief access specified object element with default value - /// @sa https://json.nlohmann.me/api/basic_json/value/ - /// using std::is_convertible in a std::enable_if will fail when using explicit conversions - template < class KeyType, class ValueType, detail::enable_if_t < - detail::is_getable::value - && !std::is_same::value - && detail::is_usable_as_basic_json_key_type::value, int > = 0 > - typename std::decay::type value(KeyType && key, ValueType && default_value) const + template < class ValueType, class KeyType, detail::enable_if_t < + detail::is_transparent::value + && !detail::is_json_pointer::value + && is_comparable_with_object_key::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ValueType value(KeyType && key, const ValueType& default_value) const { // value only works for objects if (JSON_HEDLEY_LIKELY(is_object())) @@ -21339,7 +21403,34 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const auto it = find(std::forward(key)); if (it != end()) { - return it->template get::type>(); + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class KeyType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_transparent::value + && !detail::is_json_pointer::value + && is_comparable_with_object_key::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(KeyType && key, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(std::forward(key)); + if (it != end()) + { + return it->template get(); } return std::forward(default_value); @@ -21348,20 +21439,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); } - /// @brief access specified object element with default value - /// @sa https://json.nlohmann.me/api/basic_json/value/ - /// overload for a default value of type const char* - template < class KeyType, detail::enable_if_t < - !detail::is_json_pointer::value, int > = 0 > - string_t value(KeyType && key, const char* default_value) const - { - return value(std::forward(key), string_t(default_value)); - } - /// @brief access specified object element via JSON Pointer with default value /// @sa https://json.nlohmann.me/api/basic_json/value/ template < class ValueType, detail::enable_if_t < - detail::is_getable::value, int> = 0 > + detail::is_getable::value + && !std::is_same>::value, int > = 0 > ValueType value(const json_pointer& ptr, const ValueType& default_value) const { // value only works for objects @@ -21381,29 +21463,50 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); } + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template < class ValueType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_getable::value + && !std::is_same>::value, int > = 0 > + ReturnType value(const json_pointer& ptr, ValueType && default_value) const + { + // value only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return std::forward(default_value); + } + } + + JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this)); + } + template < class ValueType, class BasicJsonType, detail::enable_if_t < - detail::is_getable::value, int> = 0 > + detail::is_basic_json::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) ValueType value(const ::nlohmann::json_pointer& ptr, const ValueType& default_value) const { return value(ptr.convert(), default_value); } - /// @brief access specified object element via JSON Pointer with default value - /// @sa https://json.nlohmann.me/api/basic_json/value/ - /// overload for a default value of type const char* - JSON_HEDLEY_NON_NULL(3) - string_t value(const json_pointer& ptr, const char* default_value) const - { - return value(ptr, string_t(default_value)); - } - - template + template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type::type, + detail::enable_if_t < + detail::is_basic_json::value + && detail::is_getable::value + && !std::is_same>::value, int > = 0 > JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer) // NOLINT(readability/alt_tokens) - JSON_HEDLEY_NON_NULL(3) - string_t value(const typename ::nlohmann::json_pointer& ptr, const char* default_value) const + ReturnType value(const ::nlohmann::json_pointer& ptr, ValueType && default_value) const { - return value(ptr.convert(), default_value); + return value(ptr.convert(), std::forward(default_value)); } /// @brief access the first element diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 65b610f0e..1afb000ae 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,7 +37,8 @@ endif() add_library(test_main OBJECT src/unit.cpp) target_compile_definitions(test_main PUBLIC DOCTEST_CONFIG_SUPER_FAST_ASSERTS - JSON_TEST_KEEP_MACROS) + JSON_TEST_KEEP_MACROS + JSON_TEST_USING_MULTIPLE_HEADERS=$) target_compile_features(test_main PRIVATE cxx_std_11) target_compile_options(test_main PUBLIC $<$:/EHsc;$<$:/Od>> diff --git a/tests/src/unit-element_access2.cpp b/tests/src/unit-element_access2.cpp index 5f28e5a2c..cc713f323 100644 --- a/tests/src/unit-element_access2.cpp +++ b/tests/src/unit-element_access2.cpp @@ -14,6 +14,9 @@ using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) #endif +// build test with C++14 +// JSON_HAS_CPP_14 + TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_json) { SECTION("object") @@ -1488,3 +1491,304 @@ TEST_CASE_TEMPLATE("element access 2 (throwing tests)", Json, nlohmann::json, nl } } #endif + +// TODO(falbrechtskirchinger) merge with the other test case; clean up +TEST_CASE_TEMPLATE("element access 2 (additional value() tests)", Json, nlohmann::json, nlohmann::ordered_json) +{ + using string_t = typename Json::string_t; + using number_integer_t = typename Json::number_integer_t; + + // test assumes string_t and object_t::key_type are the same + REQUIRE(std::is_same::value); + + Json j + { + {"foo", "bar"}, + {"baz", 42} + }; + + const char* cpstr = "default"; + const char castr[] = "default"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + string_t str = "default"; + + number_integer_t integer = 69; + std::size_t size = 69; + + SECTION("deduced ValueType") + { + SECTION("literal key") + { + CHECK(j.value("foo", "default") == "bar"); + CHECK(j.value("foo", cpstr) == "bar"); + CHECK(j.value("foo", castr) == "bar"); + CHECK(j.value("foo", str) == "bar"); + // this test is in fact different than the one below, + // because of 0 considering const char * overloads + // where as any other number does not + CHECK(j.value("baz", 0) == 42); + CHECK(j.value("baz", 47) == 42); + CHECK(j.value("baz", integer) == 42); + CHECK(j.value("baz", size) == 42); + + CHECK(j.value("bar", "default") == "default"); + CHECK(j.value("bar", 0) == 0); + CHECK(j.value("bar", 47) == 47); + CHECK(j.value("bar", integer) == integer); + CHECK(j.value("bar", size) == size); + + CHECK_THROWS_WITH_AS(Json().value("foo", "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().value("foo", str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } + + SECTION("const char * key") + { + const char* key = "foo"; + const char* key2 = "baz"; + const char* key_notfound = "bar"; + + CHECK(j.value(key, "default") == "bar"); + CHECK(j.value(key, cpstr) == "bar"); + CHECK(j.value(key, castr) == "bar"); + CHECK(j.value(key, str) == "bar"); + CHECK(j.value(key2, 0) == 42); + CHECK(j.value(key2, 47) == 42); + CHECK(j.value(key2, integer) == 42); + CHECK(j.value(key2, size) == 42); + + CHECK(j.value(key_notfound, "default") == "default"); + CHECK(j.value(key_notfound, 0) == 0); + CHECK(j.value(key_notfound, 47) == 47); + CHECK(j.value(key_notfound, integer) == integer); + CHECK(j.value(key_notfound, size) == size); + + CHECK_THROWS_WITH_AS(Json().value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } + + SECTION("const char(&)[] key") + { + const char key[] = "foo"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key2[] = "baz"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key_notfound[] = "bar"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + + CHECK(j.value(key, "default") == "bar"); + CHECK(j.value(key, cpstr) == "bar"); + CHECK(j.value(key, castr) == "bar"); + CHECK(j.value(key, str) == "bar"); + CHECK(j.value(key2, 0) == 42); + CHECK(j.value(key2, 47) == 42); + CHECK(j.value(key2, integer) == 42); + CHECK(j.value(key2, size) == 42); + + CHECK(j.value(key_notfound, "default") == "default"); + CHECK(j.value(key_notfound, 0) == 0); + CHECK(j.value(key_notfound, 47) == 47); + CHECK(j.value(key_notfound, integer) == integer); + CHECK(j.value(key_notfound, size) == size); + + CHECK_THROWS_WITH_AS(Json().value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } + + SECTION("string_t/object_t::key_type key") + { + string_t key = "foo"; + string_t key2 = "baz"; + string_t key_notfound = "bar"; + + CHECK(j.value(key, "default") == "bar"); + CHECK(j.value(key, cpstr) == "bar"); + CHECK(j.value(key, castr) == "bar"); + CHECK(j.value(key, str) == "bar"); + CHECK(j.value(key2, 0) == 42); + CHECK(j.value(key2, 47) == 42); + CHECK(j.value(key2, integer) == 42); + CHECK(j.value(key2, size) == 42); + + CHECK(j.value(key_notfound, "default") == "default"); + CHECK(j.value(key_notfound, 0) == 0); + CHECK(j.value(key_notfound, 47) == 47); + CHECK(j.value(key_notfound, integer) == integer); + CHECK(j.value(key_notfound, size) == size); + + CHECK_THROWS_WITH_AS(Json().value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } + +#ifdef JSON_HAS_CPP_17 + SECTION("std::string_view key") + { + std::string_view key = "foo"; + std::string_view key2 = "baz"; + std::string_view key_notfound = "bar"; + + CHECK(j.value(key, "default") == "bar"); + CHECK(j.value(key, cpstr) == "bar"); + CHECK(j.value(key, castr) == "bar"); + CHECK(j.value(key, str) == "bar"); + CHECK(j.value(key2, 0) == 42); + CHECK(j.value(key2, 47) == 42); + CHECK(j.value(key2, integer) == 42); + CHECK(j.value(key2, size) == 42); + + CHECK(j.value(key_notfound, "default") == "default"); + CHECK(j.value(key_notfound, 0) == 0); + CHECK(j.value(key_notfound, 47) == 47); + CHECK(j.value(key_notfound, integer) == integer); + CHECK(j.value(key_notfound, size) == size); + + CHECK_THROWS_WITH_AS(Json().value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } +#endif + } + + SECTION("explicit ValueType") + { + SECTION("literal key") + { + CHECK(j.template value("foo", "default") == "bar"); + CHECK(j.template value("foo", cpstr) == "bar"); + CHECK(j.template value("foo", castr) == "bar"); + CHECK(j.template value("foo", str) == "bar"); + CHECK(j.template value("baz", 0) == 42); + CHECK(j.template value("baz", 47) == 42); + CHECK(j.template value("baz", integer) == 42); + CHECK(j.template value("baz", 0) == 42); + CHECK(j.template value("baz", 47) == 42); + CHECK(j.template value("baz", size) == 42); + + CHECK(j.template value("bar", "default") == "default"); + CHECK(j.template value("bar", 0) == 0); + CHECK(j.template value("bar", 47) == 47); + CHECK(j.template value("bar", integer) == integer); + CHECK(j.template value("bar", 0) == 0); + CHECK(j.template value("bar", 47) == 47); + CHECK(j.template value("bar", size) == size); + + CHECK_THROWS_WITH_AS(Json().template value("foo", "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().template value("foo", str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } + + SECTION("const char * key") + { + const char* key = "foo"; + const char* key2 = "baz"; + const char* key_notfound = "bar"; + + CHECK(j.template value(key, "default") == "bar"); + CHECK(j.template value(key, cpstr) == "bar"); + CHECK(j.template value(key, castr) == "bar"); + CHECK(j.template value(key, str) == "bar"); + CHECK(j.template value(key2, 0) == 42); + CHECK(j.template value(key2, 47) == 42); + CHECK(j.template value(key2, integer) == 42); + CHECK(j.template value(key2, 0) == 42); + CHECK(j.template value(key2, 47) == 42); + CHECK(j.template value(key2, size) == 42); + + CHECK(j.template value(key_notfound, "default") == "default"); + CHECK(j.template value(key_notfound, 0) == 0); + CHECK(j.template value(key_notfound, 47) == 47); + CHECK(j.template value(key_notfound, integer) == integer); + CHECK(j.template value(key_notfound, 0) == 0); + CHECK(j.template value(key_notfound, 47) == 47); + CHECK(j.template value(key_notfound, size) == size); + + CHECK_THROWS_WITH_AS(Json().template value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().template value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } + + SECTION("const char(&)[] key") + { + const char key[] = "foo"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key2[] = "baz"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + const char key_notfound[] = "bar"; // NOLINT(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + + CHECK(j.template value(key, "default") == "bar"); + CHECK(j.template value(key, cpstr) == "bar"); + CHECK(j.template value(key, castr) == "bar"); + CHECK(j.template value(key, str) == "bar"); + CHECK(j.template value(key2, 0) == 42); + CHECK(j.template value(key2, 47) == 42); + CHECK(j.template value(key2, integer) == 42); + CHECK(j.template value(key2, 0) == 42); + CHECK(j.template value(key2, 47) == 42); + CHECK(j.template value(key2, size) == 42); + + CHECK(j.template value(key_notfound, "default") == "default"); + CHECK(j.template value(key_notfound, 0) == 0); + CHECK(j.template value(key_notfound, 47) == 47); + CHECK(j.template value(key_notfound, integer) == integer); + CHECK(j.template value(key_notfound, 0) == 0); + CHECK(j.template value(key_notfound, 47) == 47); + CHECK(j.template value(key_notfound, size) == size); + + CHECK_THROWS_WITH_AS(Json().template value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().template value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } + + SECTION("string_t/object_t::key_type key") + { + string_t key = "foo"; + string_t key2 = "baz"; + string_t key_notfound = "bar"; + + CHECK(j.template value(key, "default") == "bar"); + CHECK(j.template value(key, cpstr) == "bar"); + CHECK(j.template value(key, castr) == "bar"); + CHECK(j.template value(key, str) == "bar"); + CHECK(j.template value(key2, 0) == 42); + CHECK(j.template value(key2, 47) == 42); + CHECK(j.template value(key2, 0) == 42); + CHECK(j.template value(key2, 47) == 42); + + CHECK(j.template value(key_notfound, "default") == "default"); + CHECK(j.template value(key_notfound, 0) == 0); + CHECK(j.template value(key_notfound, 47) == 47); + CHECK(j.template value(key_notfound, 0) == 0); + CHECK(j.template value(key_notfound, 47) == 47); + + CHECK_THROWS_WITH_AS(Json().template value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().template value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } + +#ifdef JSON_HAS_CPP_17 + SECTION("std::string_view key") + { + std::string_view key = "foo"; + std::string_view key2 = "baz"; + std::string_view key_notfound = "bar"; + + CHECK(j.template value(key, "default") == "bar"); + CHECK(j.template value(key, cpstr) == "bar"); + CHECK(j.template value(key, castr) == "bar"); + CHECK(j.template value(key, str) == "bar"); + CHECK(j.template value(key2, 0) == 42); + CHECK(j.template value(key2, 47) == 42); + CHECK(j.template value(key2, integer) == 42); + CHECK(j.template value(key2, 0) == 42); + CHECK(j.template value(key2, 47) == 42); + CHECK(j.template value(key2, size) == 42); + + CHECK(j.template value(key_notfound, "default") == "default"); + CHECK(j.template value(key_notfound, 0) == 0); + CHECK(j.template value(key_notfound, 47) == 47); + CHECK(j.template value(key_notfound, integer) == integer); + CHECK(j.template value(key_notfound, 0) == 0); + CHECK(j.template value(key_notfound, 47) == 47); + CHECK(j.template value(key_notfound, size) == size); + + CHECK(j.template value(key, "default") == "bar"); + CHECK(j.template value(key, cpstr) == "bar"); + CHECK(j.template value(key, castr) == "bar"); + CHECK(j.template value(key, str) == "bar"); + + CHECK(j.template value(key_notfound, "default") == "default"); + + CHECK_THROWS_WITH_AS(Json().template value(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + CHECK_THROWS_WITH_AS(Json().template value(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&); + } +#endif + } +} diff --git a/tests/src/unit-type_traits.cpp b/tests/src/unit-type_traits.cpp new file mode 100644 index 000000000..3be8e98a9 --- /dev/null +++ b/tests/src/unit-type_traits.cpp @@ -0,0 +1,56 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.11.1 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" + +#if JSON_TEST_USING_MULTIPLE_HEADERS + #include +#else + #include +#endif + +TEST_CASE("type traits") +{ + SECTION("is_c_string") + { + using nlohmann::detail::is_c_string; + using nlohmann::detail::is_c_string_uncvref; + + SECTION("char *") + { + CHECK(is_c_string::value); + CHECK(is_c_string::value); + CHECK(is_c_string::value); + CHECK(is_c_string::value); + + CHECK_FALSE(is_c_string::value); + CHECK_FALSE(is_c_string::value); + CHECK_FALSE(is_c_string::value); + CHECK_FALSE(is_c_string::value); + + CHECK(is_c_string_uncvref::value); + CHECK(is_c_string_uncvref::value); + CHECK(is_c_string_uncvref::value); + CHECK(is_c_string_uncvref::value); + } + + SECTION("char[]") + { + // NOLINTBEGIN(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + CHECK(is_c_string::value); + CHECK(is_c_string::value); + + CHECK_FALSE(is_c_string::value); + CHECK_FALSE(is_c_string::value); + + CHECK(is_c_string_uncvref::value); + CHECK(is_c_string_uncvref::value); + // NOLINTEND(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays) + } + } +}