1
0
mirror of https://github.com/nlohmann/json synced 2024-11-25 13:20:05 +00:00

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
This commit is contained in:
Florian Albrechtskirchinger 2022-08-07 13:54:55 +02:00 committed by GitHub
parent 8eee62d388
commit 0c7a18374c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 694 additions and 127 deletions

View File

@ -7,7 +7,7 @@ ValueType value(const typename object_t::key_type& key,
ValueType&& default_value) const; ValueType&& default_value) const;
// (2) // (2)
template<class KeyType, class ValueType> template<class ValueType, class KeyType>
ValueType value(KeyType&& key, ValueType value(KeyType&& key,
ValueType&& default_value) const; ValueType&& default_value) const;
@ -155,5 +155,5 @@ changes to any JSON value.
## Version history ## Version history
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0. 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. 3. Added in version 2.0.2.

View File

@ -684,5 +684,57 @@ inline constexpr bool value_in_range_of(T val)
return value_in_range_of_impl1<OfType, T>::test(val); return value_in_range_of_impl1<OfType, T>::test(val);
} }
template<bool Value>
using bool_constant = std::integral_constant<bool, Value>;
///////////////////////////////////////////////////////////////////////////////
// is_c_string
///////////////////////////////////////////////////////////////////////////////
namespace impl
{
template<typename T>
inline constexpr bool is_c_string()
{
using TUnExt = typename std::remove_extent<T>::type;
using TUnCVExt = typename std::remove_cv<TUnExt>::type;
using TUnPtr = typename std::remove_pointer<T>::type;
using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;
return
(std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)
|| (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);
}
} // namespace impl
// checks whether T is a [cv] char */[cv] char[] C string
template<typename T>
struct is_c_string : bool_constant<impl::is_c_string<T>()> {};
template<typename T>
using is_c_string_uncvref = is_c_string<uncvref_t<T>>;
///////////////////////////////////////////////////////////////////////////////
// is_transparent
///////////////////////////////////////////////////////////////////////////////
namespace impl
{
template<typename T>
inline constexpr bool is_transparent()
{
return is_detected<detect_is_transparent, T>::value;
}
} // namespace impl
// checks whether T has a member named is_transparent
template<typename T>
struct is_transparent : bool_constant<impl::is_transparent<T>()> {};
///////////////////////////////////////////////////////////////////////////////
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END

View File

@ -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)); JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
} }
private:
template<typename KeyType>
using is_comparable_with_object_key = detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >;
template<typename ValueType>
using value_return_type = std::conditional <
detail::is_c_string_uncvref<ValueType>::value,
string_t, typename std::decay<ValueType>::type >;
public:
/// @brief access specified object element with default value /// @brief access specified object element with default value
/// @sa https://json.nlohmann.me/api/basic_json/value/ /// @sa https://json.nlohmann.me/api/basic_json/value/
// this is the value(const typename object_t::key_type&) overload template < class ValueType, detail::enable_if_t <
template < class KeyType, class ValueType, detail::enable_if_t < !detail::is_transparent<object_comparator_t>::value
std::is_same<KeyType, typename object_t::key_type>::value
&& detail::is_getable<basic_json_t, ValueType>::value && detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value, int > = 0 > && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
typename std::decay<ValueType>::type value(const KeyType& key, ValueType && default_value) const ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
{ {
// value only works for objects // value only works for objects
if (JSON_HEDLEY_LIKELY(is_object())) 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); const auto it = find(key);
if (it != end()) if (it != end())
{ {
return it->template get<typename std::decay<ValueType>::type>(); return it->template get<ValueType>();
}
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<ValueType>::type,
detail::enable_if_t <
!detail::is_transparent<object_comparator_t>::value
&& detail::is_getable<basic_json_t, ReturnType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::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<ReturnType>();
} }
return std::forward<ValueType>(default_value); return std::forward<ValueType>(default_value);
@ -2221,36 +2256,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief access specified object element with default value /// @brief access specified object element with default value
/// @sa https://json.nlohmann.me/api/basic_json/value/ /// @sa https://json.nlohmann.me/api/basic_json/value/
/// overload for a default value of type const char* template < class ValueType, class KeyType, detail::enable_if_t <
string_t value(const typename object_t::key_type& key, const char* default_value) const detail::is_transparent<object_comparator_t>::value
{ && !detail::is_json_pointer<KeyType>::value
return value(key, string_t(default_value)); && is_comparable_with_object_key<KeyType>::value
} && detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
// these two functions, in conjunction with value(const KeyType &, ValueType &&), ValueType value(KeyType && key, const ValueType& default_value) const
// 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<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value, int > = 0 >
typename std::decay<ValueType>::type value(const char* key, ValueType && default_value) const
{
return value(typename object_t::key_type(key), std::forward<ValueType>(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<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value
&& detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
typename std::decay<ValueType>::type value(KeyType && key, ValueType && default_value) const
{ {
// value only works for objects // value only works for objects
if (JSON_HEDLEY_LIKELY(is_object())) 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<KeyType>(key)); const auto it = find(std::forward<KeyType>(key));
if (it != end()) if (it != end())
{ {
return it->template get<typename std::decay<ValueType>::type>(); return it->template get<ValueType>();
}
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<ValueType>::type,
detail::enable_if_t <
detail::is_transparent<object_comparator_t>::value
&& !detail::is_json_pointer<KeyType>::value
&& is_comparable_with_object_key<KeyType>::value
&& detail::is_getable<basic_json_t, ReturnType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::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<KeyType>(key));
if (it != end())
{
return it->template get<ReturnType>();
} }
return std::forward<ValueType>(default_value); return std::forward<ValueType>(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)); 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<KeyType>::value, int > = 0 >
string_t value(KeyType && key, const char* default_value) const
{
return value(std::forward<KeyType>(key), string_t(default_value));
}
/// @brief access specified object element via JSON Pointer with default value /// @brief access specified object element via JSON Pointer with default value
/// @sa https://json.nlohmann.me/api/basic_json/value/ /// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, detail::enable_if_t < template < class ValueType, detail::enable_if_t <
detail::is_getable<basic_json_t, ValueType>::value, int> = 0 > detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
ValueType value(const json_pointer& ptr, const ValueType& default_value) const ValueType value(const json_pointer& ptr, const ValueType& default_value) const
{ {
// value only works for objects // 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)); 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<ValueType>::type,
detail::enable_if_t <
detail::is_getable<basic_json_t, ReturnType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::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<ReturnType>();
}
JSON_INTERNAL_CATCH (out_of_range&)
{
return std::forward<ValueType>(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 < template < class ValueType, class BasicJsonType, detail::enable_if_t <
detail::is_getable<basic_json_t, ValueType>::value, int> = 0 > detail::is_basic_json<BasicJsonType>::value
&& detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
{ {
return value(ptr.convert(), default_value); return value(ptr.convert(), default_value);
} }
/// @brief access specified object element via JSON Pointer with default value template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
/// @sa https://json.nlohmann.me/api/basic_json/value/ detail::enable_if_t <
/// overload for a default value of type const char* detail::is_basic_json<BasicJsonType>::value
JSON_HEDLEY_NON_NULL(3) && detail::is_getable<basic_json_t, ReturnType>::value
string_t value(const json_pointer& ptr, const char* default_value) const && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
{
return value(ptr, string_t(default_value));
}
template<typename BasicJsonType>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
JSON_HEDLEY_NON_NULL(3) ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const
string_t value(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr, const char* default_value) const
{ {
return value(ptr.convert(), default_value); return value(ptr.convert(), std::forward<ValueType>(default_value));
} }
/// @brief access the first element /// @brief access the first element

View File

@ -4059,6 +4059,58 @@ inline constexpr bool value_in_range_of(T val)
return value_in_range_of_impl1<OfType, T>::test(val); return value_in_range_of_impl1<OfType, T>::test(val);
} }
template<bool Value>
using bool_constant = std::integral_constant<bool, Value>;
///////////////////////////////////////////////////////////////////////////////
// is_c_string
///////////////////////////////////////////////////////////////////////////////
namespace impl
{
template<typename T>
inline constexpr bool is_c_string()
{
using TUnExt = typename std::remove_extent<T>::type;
using TUnCVExt = typename std::remove_cv<TUnExt>::type;
using TUnPtr = typename std::remove_pointer<T>::type;
using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;
return
(std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)
|| (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);
}
} // namespace impl
// checks whether T is a [cv] char */[cv] char[] C string
template<typename T>
struct is_c_string : bool_constant<impl::is_c_string<T>()> {};
template<typename T>
using is_c_string_uncvref = is_c_string<uncvref_t<T>>;
///////////////////////////////////////////////////////////////////////////////
// is_transparent
///////////////////////////////////////////////////////////////////////////////
namespace impl
{
template<typename T>
inline constexpr bool is_transparent()
{
return is_detected<detect_is_transparent, T>::value;
}
} // namespace impl
// checks whether T has a member named is_transparent
template<typename T>
struct is_transparent : bool_constant<impl::is_transparent<T>()> {};
///////////////////////////////////////////////////////////////////////////////
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END 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)); JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
} }
private:
template<typename KeyType>
using is_comparable_with_object_key = detail::is_comparable <
object_comparator_t, const typename object_t::key_type&, KeyType >;
template<typename ValueType>
using value_return_type = std::conditional <
detail::is_c_string_uncvref<ValueType>::value,
string_t, typename std::decay<ValueType>::type >;
public:
/// @brief access specified object element with default value /// @brief access specified object element with default value
/// @sa https://json.nlohmann.me/api/basic_json/value/ /// @sa https://json.nlohmann.me/api/basic_json/value/
// this is the value(const typename object_t::key_type&) overload template < class ValueType, detail::enable_if_t <
template < class KeyType, class ValueType, detail::enable_if_t < !detail::is_transparent<object_comparator_t>::value
std::is_same<KeyType, typename object_t::key_type>::value
&& detail::is_getable<basic_json_t, ValueType>::value && detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value, int > = 0 > && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
typename std::decay<ValueType>::type value(const KeyType& key, ValueType && default_value) const ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
{ {
// value only works for objects // value only works for objects
if (JSON_HEDLEY_LIKELY(is_object())) 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); const auto it = find(key);
if (it != end()) if (it != end())
{ {
return it->template get<typename std::decay<ValueType>::type>(); return it->template get<ValueType>();
}
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<ValueType>::type,
detail::enable_if_t <
!detail::is_transparent<object_comparator_t>::value
&& detail::is_getable<basic_json_t, ReturnType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::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<ReturnType>();
} }
return std::forward<ValueType>(default_value); return std::forward<ValueType>(default_value);
@ -21301,36 +21388,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @brief access specified object element with default value /// @brief access specified object element with default value
/// @sa https://json.nlohmann.me/api/basic_json/value/ /// @sa https://json.nlohmann.me/api/basic_json/value/
/// overload for a default value of type const char* template < class ValueType, class KeyType, detail::enable_if_t <
string_t value(const typename object_t::key_type& key, const char* default_value) const detail::is_transparent<object_comparator_t>::value
{ && !detail::is_json_pointer<KeyType>::value
return value(key, string_t(default_value)); && is_comparable_with_object_key<KeyType>::value
} && detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
// these two functions, in conjunction with value(const KeyType &, ValueType &&), ValueType value(KeyType && key, const ValueType& default_value) const
// 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<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value, int > = 0 >
typename std::decay<ValueType>::type value(const char* key, ValueType && default_value) const
{
return value(typename object_t::key_type(key), std::forward<ValueType>(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<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value
&& detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
typename std::decay<ValueType>::type value(KeyType && key, ValueType && default_value) const
{ {
// value only works for objects // value only works for objects
if (JSON_HEDLEY_LIKELY(is_object())) 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<KeyType>(key)); const auto it = find(std::forward<KeyType>(key));
if (it != end()) if (it != end())
{ {
return it->template get<typename std::decay<ValueType>::type>(); return it->template get<ValueType>();
}
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<ValueType>::type,
detail::enable_if_t <
detail::is_transparent<object_comparator_t>::value
&& !detail::is_json_pointer<KeyType>::value
&& is_comparable_with_object_key<KeyType>::value
&& detail::is_getable<basic_json_t, ReturnType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::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<KeyType>(key));
if (it != end())
{
return it->template get<ReturnType>();
} }
return std::forward<ValueType>(default_value); return std::forward<ValueType>(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)); 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<KeyType>::value, int > = 0 >
string_t value(KeyType && key, const char* default_value) const
{
return value(std::forward<KeyType>(key), string_t(default_value));
}
/// @brief access specified object element via JSON Pointer with default value /// @brief access specified object element via JSON Pointer with default value
/// @sa https://json.nlohmann.me/api/basic_json/value/ /// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, detail::enable_if_t < template < class ValueType, detail::enable_if_t <
detail::is_getable<basic_json_t, ValueType>::value, int> = 0 > detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
ValueType value(const json_pointer& ptr, const ValueType& default_value) const ValueType value(const json_pointer& ptr, const ValueType& default_value) const
{ {
// value only works for objects // 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)); 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<ValueType>::type,
detail::enable_if_t <
detail::is_getable<basic_json_t, ReturnType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::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<ReturnType>();
}
JSON_INTERNAL_CATCH (out_of_range&)
{
return std::forward<ValueType>(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 < template < class ValueType, class BasicJsonType, detail::enable_if_t <
detail::is_getable<basic_json_t, ValueType>::value, int> = 0 > detail::is_basic_json<BasicJsonType>::value
&& detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
{ {
return value(ptr.convert(), default_value); return value(ptr.convert(), default_value);
} }
/// @brief access specified object element via JSON Pointer with default value template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
/// @sa https://json.nlohmann.me/api/basic_json/value/ detail::enable_if_t <
/// overload for a default value of type const char* detail::is_basic_json<BasicJsonType>::value
JSON_HEDLEY_NON_NULL(3) && detail::is_getable<basic_json_t, ReturnType>::value
string_t value(const json_pointer& ptr, const char* default_value) const && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
{
return value(ptr, string_t(default_value));
}
template<typename BasicJsonType>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens) JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
JSON_HEDLEY_NON_NULL(3) ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const
string_t value(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr, const char* default_value) const
{ {
return value(ptr.convert(), default_value); return value(ptr.convert(), std::forward<ValueType>(default_value));
} }
/// @brief access the first element /// @brief access the first element

View File

@ -37,7 +37,8 @@ endif()
add_library(test_main OBJECT src/unit.cpp) add_library(test_main OBJECT src/unit.cpp)
target_compile_definitions(test_main PUBLIC target_compile_definitions(test_main PUBLIC
DOCTEST_CONFIG_SUPER_FAST_ASSERTS DOCTEST_CONFIG_SUPER_FAST_ASSERTS
JSON_TEST_KEEP_MACROS) JSON_TEST_KEEP_MACROS
JSON_TEST_USING_MULTIPLE_HEADERS=$<BOOL:${JSON_MultipleHeaders}>)
target_compile_features(test_main PRIVATE cxx_std_11) target_compile_features(test_main PRIVATE cxx_std_11)
target_compile_options(test_main PUBLIC target_compile_options(test_main PUBLIC
$<$<CXX_COMPILER_ID:MSVC>:/EHsc;$<$<CONFIG:Release>:/Od>> $<$<CXX_COMPILER_ID:MSVC>:/EHsc;$<$<CONFIG:Release>:/Od>>

View File

@ -14,6 +14,9 @@
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace) using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
#endif #endif
// build test with C++14
// JSON_HAS_CPP_14
TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_json) TEST_CASE_TEMPLATE("element access 2", Json, nlohmann::json, nlohmann::ordered_json)
{ {
SECTION("object") SECTION("object")
@ -1488,3 +1491,304 @@ TEST_CASE_TEMPLATE("element access 2 (throwing tests)", Json, nlohmann::json, nl
} }
} }
#endif #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<string_t, typename Json::object_t::key_type>::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<string_t>("foo", "default") == "bar");
CHECK(j.template value<string_t>("foo", cpstr) == "bar");
CHECK(j.template value<string_t>("foo", castr) == "bar");
CHECK(j.template value<string_t>("foo", str) == "bar");
CHECK(j.template value<number_integer_t>("baz", 0) == 42);
CHECK(j.template value<number_integer_t>("baz", 47) == 42);
CHECK(j.template value<number_integer_t>("baz", integer) == 42);
CHECK(j.template value<std::size_t>("baz", 0) == 42);
CHECK(j.template value<std::size_t>("baz", 47) == 42);
CHECK(j.template value<std::size_t>("baz", size) == 42);
CHECK(j.template value<string_t>("bar", "default") == "default");
CHECK(j.template value<number_integer_t>("bar", 0) == 0);
CHECK(j.template value<number_integer_t>("bar", 47) == 47);
CHECK(j.template value<number_integer_t>("bar", integer) == integer);
CHECK(j.template value<std::size_t>("bar", 0) == 0);
CHECK(j.template value<std::size_t>("bar", 47) == 47);
CHECK(j.template value<std::size_t>("bar", size) == size);
CHECK_THROWS_WITH_AS(Json().template value<string_t>("foo", "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&);
CHECK_THROWS_WITH_AS(Json().template value<string_t>("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<string_t>(key, "default") == "bar");
CHECK(j.template value<string_t>(key, cpstr) == "bar");
CHECK(j.template value<string_t>(key, castr) == "bar");
CHECK(j.template value<string_t>(key, str) == "bar");
CHECK(j.template value<number_integer_t>(key2, 0) == 42);
CHECK(j.template value<number_integer_t>(key2, 47) == 42);
CHECK(j.template value<number_integer_t>(key2, integer) == 42);
CHECK(j.template value<std::size_t>(key2, 0) == 42);
CHECK(j.template value<std::size_t>(key2, 47) == 42);
CHECK(j.template value<std::size_t>(key2, size) == 42);
CHECK(j.template value<string_t>(key_notfound, "default") == "default");
CHECK(j.template value<number_integer_t>(key_notfound, 0) == 0);
CHECK(j.template value<number_integer_t>(key_notfound, 47) == 47);
CHECK(j.template value<number_integer_t>(key_notfound, integer) == integer);
CHECK(j.template value<std::size_t>(key_notfound, 0) == 0);
CHECK(j.template value<std::size_t>(key_notfound, 47) == 47);
CHECK(j.template value<std::size_t>(key_notfound, size) == size);
CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&);
CHECK_THROWS_WITH_AS(Json().template value<string_t>(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<string_t>(key, "default") == "bar");
CHECK(j.template value<string_t>(key, cpstr) == "bar");
CHECK(j.template value<string_t>(key, castr) == "bar");
CHECK(j.template value<string_t>(key, str) == "bar");
CHECK(j.template value<number_integer_t>(key2, 0) == 42);
CHECK(j.template value<number_integer_t>(key2, 47) == 42);
CHECK(j.template value<number_integer_t>(key2, integer) == 42);
CHECK(j.template value<std::size_t>(key2, 0) == 42);
CHECK(j.template value<std::size_t>(key2, 47) == 42);
CHECK(j.template value<std::size_t>(key2, size) == 42);
CHECK(j.template value<string_t>(key_notfound, "default") == "default");
CHECK(j.template value<number_integer_t>(key_notfound, 0) == 0);
CHECK(j.template value<number_integer_t>(key_notfound, 47) == 47);
CHECK(j.template value<number_integer_t>(key_notfound, integer) == integer);
CHECK(j.template value<std::size_t>(key_notfound, 0) == 0);
CHECK(j.template value<std::size_t>(key_notfound, 47) == 47);
CHECK(j.template value<std::size_t>(key_notfound, size) == size);
CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&);
CHECK_THROWS_WITH_AS(Json().template value<string_t>(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<string_t>(key, "default") == "bar");
CHECK(j.template value<string_t>(key, cpstr) == "bar");
CHECK(j.template value<string_t>(key, castr) == "bar");
CHECK(j.template value<string_t>(key, str) == "bar");
CHECK(j.template value<number_integer_t>(key2, 0) == 42);
CHECK(j.template value<number_integer_t>(key2, 47) == 42);
CHECK(j.template value<std::size_t>(key2, 0) == 42);
CHECK(j.template value<std::size_t>(key2, 47) == 42);
CHECK(j.template value<string_t>(key_notfound, "default") == "default");
CHECK(j.template value<number_integer_t>(key_notfound, 0) == 0);
CHECK(j.template value<number_integer_t>(key_notfound, 47) == 47);
CHECK(j.template value<std::size_t>(key_notfound, 0) == 0);
CHECK(j.template value<std::size_t>(key_notfound, 47) == 47);
CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&);
CHECK_THROWS_WITH_AS(Json().template value<string_t>(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<string_t>(key, "default") == "bar");
CHECK(j.template value<string_t>(key, cpstr) == "bar");
CHECK(j.template value<string_t>(key, castr) == "bar");
CHECK(j.template value<string_t>(key, str) == "bar");
CHECK(j.template value<number_integer_t>(key2, 0) == 42);
CHECK(j.template value<number_integer_t>(key2, 47) == 42);
CHECK(j.template value<number_integer_t>(key2, integer) == 42);
CHECK(j.template value<std::size_t>(key2, 0) == 42);
CHECK(j.template value<std::size_t>(key2, 47) == 42);
CHECK(j.template value<std::size_t>(key2, size) == 42);
CHECK(j.template value<string_t>(key_notfound, "default") == "default");
CHECK(j.template value<number_integer_t>(key_notfound, 0) == 0);
CHECK(j.template value<number_integer_t>(key_notfound, 47) == 47);
CHECK(j.template value<number_integer_t>(key_notfound, integer) == integer);
CHECK(j.template value<std::size_t>(key_notfound, 0) == 0);
CHECK(j.template value<std::size_t>(key_notfound, 47) == 47);
CHECK(j.template value<std::size_t>(key_notfound, size) == size);
CHECK(j.template value<std::string_view>(key, "default") == "bar");
CHECK(j.template value<std::string_view>(key, cpstr) == "bar");
CHECK(j.template value<std::string_view>(key, castr) == "bar");
CHECK(j.template value<std::string_view>(key, str) == "bar");
CHECK(j.template value<std::string_view>(key_notfound, "default") == "default");
CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, "default"), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&);
CHECK_THROWS_WITH_AS(Json().template value<string_t>(key, str), "[json.exception.type_error.306] cannot use value() with null", typename Json::type_error&);
}
#endif
}
}

View File

@ -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 <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include "doctest_compatibility.h"
#if JSON_TEST_USING_MULTIPLE_HEADERS
#include <nlohmann/detail/meta/type_traits.hpp>
#else
#include <nlohmann/json.hpp>
#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<char*>::value);
CHECK(is_c_string<const char*>::value);
CHECK(is_c_string<char* const>::value);
CHECK(is_c_string<const char* const>::value);
CHECK_FALSE(is_c_string<char*&>::value);
CHECK_FALSE(is_c_string<const char*&>::value);
CHECK_FALSE(is_c_string<char* const&>::value);
CHECK_FALSE(is_c_string<const char* const&>::value);
CHECK(is_c_string_uncvref<char*&>::value);
CHECK(is_c_string_uncvref<const char*&>::value);
CHECK(is_c_string_uncvref<char* const&>::value);
CHECK(is_c_string_uncvref<const char* const&>::value);
}
SECTION("char[]")
{
// NOLINTBEGIN(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
CHECK(is_c_string<char[]>::value);
CHECK(is_c_string<const char[]>::value);
CHECK_FALSE(is_c_string<char(&)[]>::value);
CHECK_FALSE(is_c_string<const char(&)[]>::value);
CHECK(is_c_string_uncvref<char(&)[]>::value);
CHECK(is_c_string_uncvref<const char(&)[]>::value);
// NOLINTEND(hicpp-avoid-c-arrays,modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
}
}
}