add a switch to enable implicit conversions (defaults to true)

wrap implicit conversions tests around the JSON_USE_IMPLICIT_CONVERSIONS
macro
This commit is contained in:
Théo DELRIEU 2020-07-16 11:11:35 +02:00
parent 2cd10a7405
commit 74b446f5fd
No known key found for this signature in database
GPG key ID: 7D6E00D1DF01DEAF
15 changed files with 151 additions and 64 deletions

View file

@ -24,6 +24,7 @@ endif ()
option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON) option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON)
option(JSON_Install "Install CMake targets during install step." ON) option(JSON_Install "Install CMake targets during install step." ON)
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF) option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
option(JSON_ImplicitConversions "Enable implicit conversions" ON)
## ##
## CONFIGURATION ## CONFIGURATION
@ -60,6 +61,12 @@ else()
target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11) target_compile_features(${NLOHMANN_JSON_TARGET_NAME} INTERFACE cxx_std_11)
endif() endif()
target_compile_definitions(
${NLOHMANN_JSON_TARGET_NAME}
INTERFACE
JSON_USE_IMPLICIT_CONVERSIONS=$<BOOL:${JSON_ImplicitConversions}>
)
target_include_directories( target_include_directories(
${NLOHMANN_JSON_TARGET_NAME} ${NLOHMANN_JSON_TARGET_NAME}
INTERFACE INTERFACE

View file

@ -128,7 +128,7 @@ void from_json(const BasicJsonType& j, EnumType& e)
// forward_list doesn't have an insert method // forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator, template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@ -145,7 +145,7 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
// valarray doesn't have an insert method // valarray doesn't have an insert method
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::valarray<T>& l) void from_json(const BasicJsonType& j, std::valarray<T>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@ -153,7 +153,11 @@ void from_json(const BasicJsonType& j, std::valarray<T>& l)
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
} }
l.resize(j.size()); l.resize(j.size());
std::copy(j.begin(), j.end(), std::begin(l)); std::transform(j.begin(), j.end(), std::begin(l),
[](const BasicJsonType & elem)
{
return elem.template get<T>();
});
} }
template<typename BasicJsonType, typename T, std::size_t N> template<typename BasicJsonType, typename T, std::size_t N>

View file

@ -284,3 +284,13 @@
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
#define JSON_USE_IMPLICIT_CONVERSIONS 1
#endif
#if JSON_USE_IMPLICIT_CONVERSIONS
#define JSON_EXPLICIT
#else
#define JSON_EXPLICIT explicit
#endif

View file

@ -18,5 +18,6 @@
#undef JSON_HAS_CPP_17 #undef JSON_HAS_CPP_17
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
#undef NLOHMANN_BASIC_JSON_TPL #undef NLOHMANN_BASIC_JSON_TPL
#undef JSON_EXPLICIT
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp> #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>

View file

@ -94,6 +94,16 @@ using get_template_function = decltype(std::declval<T>().template get<U>());
template<typename BasicJsonType, typename T, typename = void> template<typename BasicJsonType, typename T, typename = void>
struct has_from_json : std::false_type {}; struct has_from_json : std::false_type {};
// trait checking if j.get<T> is valid
// use this trait instead of std::is_constructible or std::is_convertible,
// both rely on, or make use of implicit conversions, and thus fail when T
// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
template <typename BasicJsonType, typename T>
struct is_getable
{
static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
};
template<typename BasicJsonType, typename T> template<typename BasicJsonType, typename T>
struct has_from_json < BasicJsonType, T, struct has_from_json < BasicJsonType, T,
enable_if_t < !is_basic_json<T>::value >> enable_if_t < !is_basic_json<T>::value >>

View file

@ -3229,7 +3229,7 @@ class basic_json
#endif #endif
&& detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value && detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
, int >::type = 0 > , int >::type = 0 >
operator ValueType() const JSON_EXPLICIT operator ValueType() const
{ {
// delegate the call to get<>() const // delegate the call to get<>() const
return get<ValueType>(); return get<ValueType>();
@ -3784,8 +3784,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
// using std::is_convertible in a std::enable_if will fail when using explicit conversions
template < class ValueType, typename std::enable_if < template < class ValueType, typename std::enable_if <
std::is_convertible<basic_json_t, ValueType>::value detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value, int >::type = 0 > && !std::is_same<value_t, ValueType>::value, int >::type = 0 >
ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
{ {
@ -3796,7 +3797,7 @@ class basic_json
const auto it = find(key); const auto it = find(key);
if (it != end()) if (it != end())
{ {
return *it; return it->template get<ValueType>();
} }
return default_value; return default_value;
@ -3858,7 +3859,7 @@ class basic_json
@since version 2.0.2 @since version 2.0.2
*/ */
template<class ValueType, typename std::enable_if< template<class ValueType, typename std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>
ValueType value(const json_pointer& ptr, const ValueType& default_value) const ValueType value(const json_pointer& ptr, const ValueType& default_value) const
{ {
// at only works for objects // at only works for objects
@ -3867,7 +3868,7 @@ class basic_json
// if pointer resolves a value, return it or use default value // if pointer resolves a value, return it or use default value
JSON_TRY JSON_TRY
{ {
return ptr.get_checked(this); return ptr.get_checked(this).template get<ValueType>();
} }
JSON_INTERNAL_CATCH (out_of_range&) JSON_INTERNAL_CATCH (out_of_range&)
{ {
@ -8341,8 +8342,8 @@ class basic_json
} }
// collect mandatory members // collect mandatory members
const std::string op = get_value("op", "op", true); const auto op = get_value("op", "op", true).template get<std::string>();
const std::string path = get_value(op, "path", true); const auto path = get_value(op, "path", true).template get<std::string>();
json_pointer ptr(path); json_pointer ptr(path);
switch (get_op(op)) switch (get_op(op))
@ -8368,7 +8369,7 @@ class basic_json
case patch_operations::move: case patch_operations::move:
{ {
const std::string from_path = get_value("move", "from", true); const auto from_path = get_value("move", "from", true).template get<std::string>();
json_pointer from_ptr(from_path); json_pointer from_ptr(from_path);
// the "from" location must exist - use at() // the "from" location must exist - use at()
@ -8385,7 +8386,7 @@ class basic_json
case patch_operations::copy: case patch_operations::copy:
{ {
const std::string from_path = get_value("copy", "from", true); const auto from_path = get_value("copy", "from", true).template get<std::string>();
const json_pointer from_ptr(from_path); const json_pointer from_ptr(from_path);
// the "from" location must exist - use at() // the "from" location must exist - use at()

View file

@ -2302,6 +2302,16 @@ JSON_HEDLEY_DIAGNOSTIC_POP
void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ void to_json(nlohmann::json& j, const Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } void from_json(const nlohmann::json& j, Type& t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
#define JSON_USE_IMPLICIT_CONVERSIONS 1
#endif
#if JSON_USE_IMPLICIT_CONVERSIONS
#define JSON_EXPLICIT
#else
#define JSON_EXPLICIT explicit
#endif
namespace nlohmann namespace nlohmann
{ {
@ -3022,6 +3032,16 @@ using get_template_function = decltype(std::declval<T>().template get<U>());
template<typename BasicJsonType, typename T, typename = void> template<typename BasicJsonType, typename T, typename = void>
struct has_from_json : std::false_type {}; struct has_from_json : std::false_type {};
// trait checking if j.get<T> is valid
// use this trait instead of std::is_constructible or std::is_convertible,
// both rely on, or make use of implicit conversions, and thus fail when T
// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
template <typename BasicJsonType, typename T>
struct is_getable
{
static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
};
template<typename BasicJsonType, typename T> template<typename BasicJsonType, typename T>
struct has_from_json < BasicJsonType, T, struct has_from_json < BasicJsonType, T,
enable_if_t < !is_basic_json<T>::value >> enable_if_t < !is_basic_json<T>::value >>
@ -3507,7 +3527,7 @@ void from_json(const BasicJsonType& j, EnumType& e)
// forward_list doesn't have an insert method // forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator, template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@ -3524,7 +3544,7 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
// valarray doesn't have an insert method // valarray doesn't have an insert method
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0> enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
void from_json(const BasicJsonType& j, std::valarray<T>& l) void from_json(const BasicJsonType& j, std::valarray<T>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@ -3532,7 +3552,11 @@ void from_json(const BasicJsonType& j, std::valarray<T>& l)
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
} }
l.resize(j.size()); l.resize(j.size());
std::copy(j.begin(), j.end(), std::begin(l)); std::transform(j.begin(), j.end(), std::begin(l),
[](const BasicJsonType & elem)
{
return elem.template get<T>();
});
} }
template<typename BasicJsonType, typename T, std::size_t N> template<typename BasicJsonType, typename T, std::size_t N>
@ -19508,7 +19532,7 @@ class basic_json
#endif #endif
&& detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value && detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value
, int >::type = 0 > , int >::type = 0 >
operator ValueType() const JSON_EXPLICIT operator ValueType() const
{ {
// delegate the call to get<>() const // delegate the call to get<>() const
return get<ValueType>(); return get<ValueType>();
@ -20063,8 +20087,9 @@ class basic_json
@since version 1.0.0 @since version 1.0.0
*/ */
// using std::is_convertible in a std::enable_if will fail when using explicit conversions
template < class ValueType, typename std::enable_if < template < class ValueType, typename std::enable_if <
std::is_convertible<basic_json_t, ValueType>::value detail::is_getable<basic_json_t, ValueType>::value
&& !std::is_same<value_t, ValueType>::value, int >::type = 0 > && !std::is_same<value_t, ValueType>::value, int >::type = 0 >
ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
{ {
@ -20075,7 +20100,7 @@ class basic_json
const auto it = find(key); const auto it = find(key);
if (it != end()) if (it != end())
{ {
return *it; return it->template get<ValueType>();
} }
return default_value; return default_value;
@ -20137,7 +20162,7 @@ class basic_json
@since version 2.0.2 @since version 2.0.2
*/ */
template<class ValueType, typename std::enable_if< template<class ValueType, typename std::enable_if<
std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0> detail::is_getable<basic_json_t, ValueType>::value, int>::type = 0>
ValueType value(const json_pointer& ptr, const ValueType& default_value) const ValueType value(const json_pointer& ptr, const ValueType& default_value) const
{ {
// at only works for objects // at only works for objects
@ -20146,7 +20171,7 @@ class basic_json
// if pointer resolves a value, return it or use default value // if pointer resolves a value, return it or use default value
JSON_TRY JSON_TRY
{ {
return ptr.get_checked(this); return ptr.get_checked(this).template get<ValueType>();
} }
JSON_INTERNAL_CATCH (out_of_range&) JSON_INTERNAL_CATCH (out_of_range&)
{ {
@ -24620,8 +24645,8 @@ class basic_json
} }
// collect mandatory members // collect mandatory members
const std::string op = get_value("op", "op", true); const auto op = get_value("op", "op", true).template get<std::string>();
const std::string path = get_value(op, "path", true); const auto path = get_value(op, "path", true).template get<std::string>();
json_pointer ptr(path); json_pointer ptr(path);
switch (get_op(op)) switch (get_op(op))
@ -24647,7 +24672,7 @@ class basic_json
case patch_operations::move: case patch_operations::move:
{ {
const std::string from_path = get_value("move", "from", true); const auto from_path = get_value("move", "from", true).template get<std::string>();
json_pointer from_ptr(from_path); json_pointer from_ptr(from_path);
// the "from" location must exist - use at() // the "from" location must exist - use at()
@ -24664,7 +24689,7 @@ class basic_json
case patch_operations::copy: case patch_operations::copy:
{ {
const std::string from_path = get_value("copy", "from", true); const auto from_path = get_value("copy", "from", true).template get<std::string>();
const json_pointer from_ptr(from_path); const json_pointer from_ptr(from_path);
// the "from" location must exist - use at() // the "from" location must exist - use at()
@ -25086,6 +25111,7 @@ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std
#undef JSON_HAS_CPP_17 #undef JSON_HAS_CPP_17
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION #undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
#undef NLOHMANN_BASIC_JSON_TPL #undef NLOHMANN_BASIC_JSON_TPL
#undef JSON_EXPLICIT
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp> // #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
#undef JSON_HEDLEY_ALWAYS_INLINE #undef JSON_HEDLEY_ALWAYS_INLINE

View file

@ -1011,21 +1011,21 @@ TEST_CASE("CBOR")
SECTION("0 (0 00000 0000000000)") SECTION("0 (0 00000 0000000000)")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x00, 0x00})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x00, 0x00}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == 0.0); CHECK(d == 0.0);
} }
SECTION("-0 (1 00000 0000000000)") SECTION("-0 (1 00000 0000000000)")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x80, 0x00})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x80, 0x00}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == -0.0); CHECK(d == -0.0);
} }
SECTION("2**-24 (0 00000 0000000001)") SECTION("2**-24 (0 00000 0000000001)")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x00, 0x01})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x00, 0x01}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == std::pow(2.0, -24.0)); CHECK(d == std::pow(2.0, -24.0));
} }
} }
@ -1035,7 +1035,7 @@ TEST_CASE("CBOR")
SECTION("infinity (0 11111 0000000000)") SECTION("infinity (0 11111 0000000000)")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c, 0x00})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c, 0x00}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == std::numeric_limits<json::number_float_t>::infinity()); CHECK(d == std::numeric_limits<json::number_float_t>::infinity());
CHECK(j.dump() == "null"); CHECK(j.dump() == "null");
} }
@ -1043,7 +1043,7 @@ TEST_CASE("CBOR")
SECTION("-infinity (1 11111 0000000000)") SECTION("-infinity (1 11111 0000000000)")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0xfc, 0x00})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0xfc, 0x00}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == -std::numeric_limits<json::number_float_t>::infinity()); CHECK(d == -std::numeric_limits<json::number_float_t>::infinity());
CHECK(j.dump() == "null"); CHECK(j.dump() == "null");
} }
@ -1054,21 +1054,21 @@ TEST_CASE("CBOR")
SECTION("1 (0 01111 0000000000)") SECTION("1 (0 01111 0000000000)")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x3c, 0x00})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x3c, 0x00}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == 1); CHECK(d == 1);
} }
SECTION("-2 (1 10000 0000000000)") SECTION("-2 (1 10000 0000000000)")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0xc0, 0x00})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0xc0, 0x00}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == -2); CHECK(d == -2);
} }
SECTION("65504 (0 11110 1111111111)") SECTION("65504 (0 11110 1111111111)")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7b, 0xff})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7b, 0xff}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == 65504); CHECK(d == 65504);
} }
} }
@ -1076,7 +1076,7 @@ TEST_CASE("CBOR")
SECTION("infinity") SECTION("infinity")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c, 0x00})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c, 0x00}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(!std::isfinite(d)); CHECK(!std::isfinite(d));
CHECK(j.dump() == "null"); CHECK(j.dump() == "null");
} }
@ -1084,7 +1084,7 @@ TEST_CASE("CBOR")
SECTION("NaN") SECTION("NaN")
{ {
json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7e, 0x00})); json j = json::from_cbor(std::vector<uint8_t>({0xf9, 0x7e, 0x00}));
json::number_float_t d = j; json::number_float_t d{j};
CHECK(std::isnan(d)); CHECK(std::isnan(d));
CHECK(j.dump() == "null"); CHECK(j.dump() == "null");
} }

View file

@ -341,7 +341,7 @@ TEST_CASE("constructors")
CHECK(j.type() == json::value_t::array); CHECK(j.type() == json::value_t::array);
CHECK(j == json({1, 2, 3, 4, 5})); CHECK(j == json({1, 2, 3, 4, 5}));
std::valarray<int> jva = j; auto jva = j.get<std::valarray<int>>();
CHECK(jva.size() == va.size()); CHECK(jva.size() == va.size());
for (size_t i = 0; i < jva.size(); ++i) for (size_t i = 0; i < jva.size(); ++i)
{ {
@ -356,7 +356,7 @@ TEST_CASE("constructors")
CHECK(j.type() == json::value_t::array); CHECK(j.type() == json::value_t::array);
CHECK(j == json({1.2, 2.3, 3.4, 4.5, 5.6})); CHECK(j == json({1.2, 2.3, 3.4, 4.5, 5.6}));
std::valarray<double> jva = j; auto jva = j.get<std::valarray<double>>();
CHECK(jva.size() == va.size()); CHECK(jva.size() == va.size());
for (size_t i = 0; i < jva.size(); ++i) for (size_t i = 0; i < jva.size(); ++i)
{ {
@ -846,7 +846,7 @@ TEST_CASE("constructors")
CHECK(j.type() == json::value_t::number_float); CHECK(j.type() == json::value_t::number_float);
// check round trip of NaN // check round trip of NaN
json::number_float_t d = j; json::number_float_t d{j};
CHECK((std::isnan(d) && std::isnan(n)) == true); CHECK((std::isnan(d) && std::isnan(n)) == true);
// check that NaN is serialized to null // check that NaN is serialized to null
@ -861,7 +861,7 @@ TEST_CASE("constructors")
CHECK(j.type() == json::value_t::number_float); CHECK(j.type() == json::value_t::number_float);
// check round trip of infinity // check round trip of infinity
json::number_float_t d = j; json::number_float_t d{j};
CHECK(d == n); CHECK(d == n);
// check that inf is serialized to null // check that inf is serialized to null

View file

@ -189,6 +189,7 @@ TEST_CASE("value conversion")
} }
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("get an object (implicit)") SECTION("get an object (implicit)")
{ {
json::object_t o_reference = {{"object", json::object()}, json::object_t o_reference = {{"object", json::object()},
@ -230,6 +231,7 @@ TEST_CASE("value conversion")
CHECK(json(o) == j); CHECK(json(o) == j);
} }
} }
#endif
SECTION("get an array (explicit)") SECTION("get an array (explicit)")
{ {
@ -276,7 +278,8 @@ TEST_CASE("value conversion")
SECTION("reserve is called on containers that supports it") SECTION("reserve is called on containers that supports it")
{ {
// make sure all values are properly copied // make sure all values are properly copied
std::vector<int> v2 = json({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); json j({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
auto v2 = j.get<std::vector<int>>();
CHECK(v2.size() == 10); CHECK(v2.size() == 10);
} }
#endif #endif
@ -405,6 +408,7 @@ TEST_CASE("value conversion")
} }
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("get an array (implicit)") SECTION("get an array (implicit)")
{ {
json::array_t a_reference{json(1), json(1u), json(2.2), json::array_t a_reference{json(1), json(1u), json(2.2),
@ -441,6 +445,7 @@ TEST_CASE("value conversion")
CHECK(json(a) == j); CHECK(json(a) == j);
} }
} }
#endif
SECTION("get a string (explicit)") SECTION("get a string (explicit)")
{ {
@ -598,6 +603,7 @@ TEST_CASE("value conversion")
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("get a string (implicit)") SECTION("get a string (implicit)")
{ {
json::string_t s_reference{"Hello world"}; json::string_t s_reference{"Hello world"};
@ -623,6 +629,7 @@ TEST_CASE("value conversion")
CHECK(json(s) == j); CHECK(json(s) == j);
} }
} }
#endif
SECTION("get a boolean (explicit)") SECTION("get a boolean (explicit)")
{ {
@ -695,6 +702,7 @@ TEST_CASE("value conversion")
} }
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("get a boolean (implicit)") SECTION("get a boolean (implicit)")
{ {
json::boolean_t b_reference{true}; json::boolean_t b_reference{true};
@ -712,6 +720,7 @@ TEST_CASE("value conversion")
CHECK(json(b) == j); CHECK(json(b) == j);
} }
} }
#endif
SECTION("get an integer number (explicit)") SECTION("get an integer number (explicit)")
{ {
@ -962,6 +971,7 @@ TEST_CASE("value conversion")
} }
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("get an integer number (implicit)") SECTION("get an integer number (implicit)")
{ {
json::number_integer_t n_reference{42}; json::number_integer_t n_reference{42};
@ -1173,6 +1183,7 @@ TEST_CASE("value conversion")
CHECK(json(n) == j_unsigned); CHECK(json(n) == j_unsigned);
} }
} }
#endif
SECTION("get a floating-point number (explicit)") SECTION("get a floating-point number (explicit)")
{ {
@ -1234,6 +1245,7 @@ TEST_CASE("value conversion")
} }
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("get a floating-point number (implicit)") SECTION("get a floating-point number (implicit)")
{ {
json::number_float_t n_reference{42.23}; json::number_float_t n_reference{42.23};
@ -1257,6 +1269,7 @@ TEST_CASE("value conversion")
CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float)); CHECK(json(n).m_value.number_float == Approx(j.m_value.number_float));
} }
} }
#endif
SECTION("get a binary value (explicit)") SECTION("get a binary value (explicit)")
{ {
@ -1364,6 +1377,7 @@ TEST_CASE("value conversion")
} }
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("get a binary value (implicit)") SECTION("get a binary value (implicit)")
{ {
json::binary_t n_reference{{1, 2, 3}}; json::binary_t n_reference{{1, 2, 3}};
@ -1375,6 +1389,7 @@ TEST_CASE("value conversion")
CHECK(*json(b).m_value.binary == *j.m_value.binary); CHECK(*json(b).m_value.binary == *j.m_value.binary);
} }
} }
#endif
SECTION("get an enum") SECTION("get an enum")
{ {
@ -1481,15 +1496,15 @@ TEST_CASE("value conversion")
SECTION("std::array is larger than JSON") SECTION("std::array is larger than JSON")
{ {
std::array<int, 6> arr6 = {{1, 2, 3, 4, 5, 6}}; std::array<int, 6> arr6 = {{1, 2, 3, 4, 5, 6}};
CHECK_THROWS_AS(arr6 = j1, json::out_of_range&); CHECK_THROWS_AS(j1.get_to(arr6), json::out_of_range&);
CHECK_THROWS_WITH(arr6 = j1, "[json.exception.out_of_range.401] " CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.out_of_range.401] "
"array index 4 is out of range"); "array index 4 is out of range");
} }
SECTION("std::array is smaller than JSON") SECTION("std::array is smaller than JSON")
{ {
std::array<int, 2> arr2 = {{8, 9}}; std::array<int, 2> arr2 = {{8, 9}};
arr2 = j1; j1.get_to(arr2);
CHECK(arr2[0] == 1); CHECK(arr2[0] == 1);
CHECK(arr2[1] == 2); CHECK(arr2[1] == 2);
} }

View file

@ -174,9 +174,9 @@ TEST_CASE("README" * doctest::skip())
} }
// getter/setter // getter/setter
const std::string tmp = j[0]; const auto tmp = j[0].get<std::string>();
j[1] = 42; j[1] = 42;
bool foo = j.at(2); bool foo{j.at(2)};
CHECK(foo == true); CHECK(foo == true);
// other stuff // other stuff
@ -258,18 +258,18 @@ TEST_CASE("README" * doctest::skip())
// strings // strings
std::string s1 = "Hello, world!"; std::string s1 = "Hello, world!";
json js = s1; json js = s1;
std::string s2 = js; auto s2 = js.get<std::string>();
// Booleans // Booleans
bool b1 = true; bool b1 = true;
json jb = b1; json jb = b1;
bool b2 = jb; bool b2{jb};
CHECK(b2 == true); CHECK(b2 == true);
// numbers // numbers
int i = 42; int i = 42;
json jn = i; json jn = i;
double f = jn; double f{jn};
CHECK(f == 42); CHECK(f == 42);
// etc. // etc.

View file

@ -240,12 +240,12 @@ TEST_CASE("regression tests")
{ {
json j1 = NAN; json j1 = NAN;
CHECK(j1.is_number_float()); CHECK(j1.is_number_float());
json::number_float_t f1 = j1; json::number_float_t f1{j1};
CHECK(std::isnan(f1)); CHECK(std::isnan(f1));
json j2 = json::number_float_t(NAN); json j2 = json::number_float_t(NAN);
CHECK(j2.is_number_float()); CHECK(j2.is_number_float());
json::number_float_t f2 = j2; json::number_float_t f2{j2};
CHECK(std::isnan(f2)); CHECK(std::isnan(f2));
} }
@ -253,12 +253,12 @@ TEST_CASE("regression tests")
{ {
json j1 = INFINITY; json j1 = INFINITY;
CHECK(j1.is_number_float()); CHECK(j1.is_number_float());
json::number_float_t f1 = j1; json::number_float_t f1{j1};
CHECK(!std::isfinite(f1)); CHECK(!std::isfinite(f1));
json j2 = json::number_float_t(INFINITY); json j2 = json::number_float_t(INFINITY);
CHECK(j2.is_number_float()); CHECK(j2.is_number_float());
json::number_float_t f2 = j2; json::number_float_t f2{j2};
CHECK(!std::isfinite(f2)); CHECK(!std::isfinite(f2));
} }
} }
@ -332,11 +332,11 @@ TEST_CASE("regression tests")
json j; json j;
ss >> j; ss >> j;
std::string test = j["Test"]; auto test = j["Test"].get<std::string>();
CHECK(test == "Test1"); CHECK(test == "Test1");
int number = j["Number"]; int number{j["Number"]};
CHECK(number == 100); CHECK(number == 100);
float foo = j["Foo"]; float foo{j["Foo"]};
CHECK(static_cast<double>(foo) == Approx(42.42)); CHECK(static_cast<double>(foo) == Approx(42.42));
} }
@ -453,6 +453,7 @@ TEST_CASE("regression tests")
CHECK(j["string"] == "\u0007\u0007"); CHECK(j["string"] == "\u0007\u0007");
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #144 - implicit assignment to std::string fails") SECTION("issue #144 - implicit assignment to std::string fails")
{ {
json o = {{"name", "value"}}; json o = {{"name", "value"}};
@ -470,6 +471,7 @@ TEST_CASE("regression tests")
CHECK_THROWS_AS(s2 = o["int"], json::type_error); CHECK_THROWS_AS(s2 = o["int"], json::type_error);
CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number"); CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number");
} }
#endif
SECTION("issue #146 - character following a surrogate pair is skipped") SECTION("issue #146 - character following a surrogate pair is skipped")
{ {
@ -689,7 +691,7 @@ TEST_CASE("regression tests")
{"object", {{"key1", 1}, {"key2", 2}}}, {"object", {{"key1", 1}, {"key2", 2}}},
}; };
int at_integer = j.at("/object/key2"_json_pointer); int at_integer{j.at("/object/key2"_json_pointer)};
int val_integer = j.value("/object/key2"_json_pointer, 0); int val_integer = j.value("/object/key2"_json_pointer, 0);
CHECK(at_integer == val_integer); CHECK(at_integer == val_integer);
@ -1233,6 +1235,7 @@ TEST_CASE("regression tests")
CHECK(j["double_value"].is_number_float()); CHECK(j["double_value"].is_number_float());
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #464 - VS2017 implicit to std::string conversion fix") SECTION("issue #464 - VS2017 implicit to std::string conversion fix")
{ {
json v = "test"; json v = "test";
@ -1240,6 +1243,7 @@ TEST_CASE("regression tests")
test = v; test = v;
CHECK(v == "test"); CHECK(v == "test");
} }
#endif
SECTION("issue #465 - roundtrip error while parsing 1000000000000000010E5") SECTION("issue #465 - roundtrip error while parsing 1000000000000000010E5")
{ {
@ -1250,6 +1254,7 @@ TEST_CASE("regression tests")
CHECK(s1 == s2); CHECK(s1 == s2);
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #473 - inconsistent behavior in conversion to array type") SECTION("issue #473 - inconsistent behavior in conversion to array type")
{ {
json j_array = {1, 2, 3, 4}; json j_array = {1, 2, 3, 4};
@ -1298,6 +1303,7 @@ TEST_CASE("regression tests")
CHECK_THROWS_WITH(create(j_null), "[json.exception.type_error.302] type must be array, but is null"); CHECK_THROWS_WITH(create(j_null), "[json.exception.type_error.302] type must be array, but is null");
} }
} }
#endif
SECTION("issue #486 - json::value_t can't be a map's key type in VC++ 2015") SECTION("issue #486 - json::value_t can't be a map's key type in VC++ 2015")
{ {
@ -1377,6 +1383,7 @@ TEST_CASE("regression tests")
CHECK_THROWS_AS(_ = json::parse(vec), json::parse_error&); CHECK_THROWS_AS(_ = json::parse(vec), json::parse_error&);
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #600 - how does one convert a map in Json back to std::map?") SECTION("issue #600 - how does one convert a map in Json back to std::map?")
{ {
SECTION("example 1") SECTION("example 1")
@ -1409,6 +1416,7 @@ TEST_CASE("regression tests")
CHECK(m1 == m2); CHECK(m1 == m2);
} }
} }
#endif
SECTION("issue #602 - BOM not skipped when using json:parse(iterator)") SECTION("issue #602 - BOM not skipped when using json:parse(iterator)")
{ {
@ -1417,6 +1425,7 @@ TEST_CASE("regression tests")
CHECK_NOTHROW(_ = json::parse(i.begin(), i.end())); CHECK_NOTHROW(_ = json::parse(i.begin(), i.end()));
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #702 - conversion from valarray<double> to json fails to build") SECTION("issue #702 - conversion from valarray<double> to json fails to build")
{ {
SECTION("original example") SECTION("original example")
@ -1445,6 +1454,7 @@ TEST_CASE("regression tests")
"[json.exception.type_error.302] type must be array, but is null"); "[json.exception.type_error.302] type must be array, but is null");
} }
} }
#endif
SECTION("issue #367 - Behavior of operator>> should more closely resemble that of built-in overloads.") SECTION("issue #367 - Behavior of operator>> should more closely resemble that of built-in overloads.")
{ {
@ -1456,9 +1466,9 @@ TEST_CASE("regression tests")
i1_2_3 >> j2; i1_2_3 >> j2;
i1_2_3 >> j3; i1_2_3 >> j3;
std::map<std::string, std::string> m1 = j1; auto m1 = j1.get<std::map<std::string, std::string>>();
std::map<std::string, std::string> m2 = j2; auto m2 = j2.get<std::map<std::string, std::string>>();
int i3 = j3; int i3{j3};
CHECK( m1 == ( std::map<std::string, std::string> {{ "first", "one" }} )); CHECK( m1 == ( std::map<std::string, std::string> {{ "first", "one" }} ));
CHECK( m2 == ( std::map<std::string, std::string> {{ "second", "two" }} )); CHECK( m2 == ( std::map<std::string, std::string> {{ "second", "two" }} ));
@ -1513,6 +1523,7 @@ TEST_CASE("regression tests")
CHECK_THROWS_WITH(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E"); CHECK_THROWS_WITH(j.dump(), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E");
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("issue #843 - converting to array not working") SECTION("issue #843 - converting to array not working")
{ {
json j; json j;
@ -1520,6 +1531,7 @@ TEST_CASE("regression tests")
j = ar; j = ar;
ar = j; ar = j;
} }
#endif
SECTION("issue #894 - invalid RFC6902 copy operation succeeds") SECTION("issue #894 - invalid RFC6902 copy operation succeeds")
{ {
@ -1632,7 +1644,7 @@ TEST_CASE("regression tests")
SECTION("issue #977 - Assigning between different json types") SECTION("issue #977 - Assigning between different json types")
{ {
foo_json lj = ns::foo{3}; foo_json lj = ns::foo{3};
ns::foo ff = lj; ns::foo ff(lj);
CHECK(lj.is_object()); CHECK(lj.is_object());
CHECK(lj.size() == 1); CHECK(lj.size() == 1);
CHECK(lj["x"] == 3); CHECK(lj["x"] == 3);
@ -1880,7 +1892,7 @@ TEST_CASE("regression tests")
{ {
{ {
json j; json j;
NonDefaultFromJsonStruct x = j; NonDefaultFromJsonStruct x(j);
NonDefaultFromJsonStruct y; NonDefaultFromJsonStruct y;
CHECK(x == y); CHECK(x == y);
} }

View file

@ -312,6 +312,7 @@ TEST_CASE("basic usage" * doctest::test_suite("udt"))
CHECK(name.m_val == "new name"); CHECK(name.m_val == "new name");
} }
#if JSON_USE_IMPLICIT_CONVERSIONS
SECTION("implicit conversions") SECTION("implicit conversions")
{ {
const udt::contact_book parsed_book = big_json; const udt::contact_book parsed_book = big_json;
@ -335,6 +336,7 @@ TEST_CASE("basic usage" * doctest::test_suite("udt"))
CHECK(book_name == udt::name{"C++"}); CHECK(book_name == udt::name{"C++"});
CHECK(book == parsed_book); CHECK(book == parsed_book);
} }
#endif
} }
} }

View file

@ -243,7 +243,7 @@ TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRU
CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}"); CHECK(json(p1).dump() == "{\"age\":1,\"metadata\":{\"haircuts\":2},\"name\":\"Erik\"}");
// deserialization // deserialization
T p2 = json(p1); auto p2 = json(p1).get<T>();
CHECK(p2 == p1); CHECK(p2 == p1);
// roundtrip // roundtrip
@ -253,8 +253,7 @@ TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRU
// check exception in case of missing field // check exception in case of missing field
json j = json(p1); json j = json(p1);
j.erase("age"); j.erase("age");
T p3; CHECK_THROWS_WITH_AS(j.get<T>(), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range);
CHECK_THROWS_WITH_AS(p3 = json(j), "[json.exception.out_of_range.403] key 'age' not found", json::out_of_range);
} }
} }

View file

@ -1168,7 +1168,7 @@ TEST_CASE("Unicode" * doctest::skip())
continue; continue;
} }
std::string ptr = s; auto ptr = s.get<std::string>();
// tilde must be followed by 0 or 1 // tilde must be followed by 0 or 1
if (ptr == "~") if (ptr == "~")