mirror of
https://github.com/nlohmann/json.git
synced 2026-09-29 11:10:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b40114f9b | ||
|
|
5d7d4a9ae6 | ||
|
|
86ea63185e |
@@ -79,6 +79,7 @@ Some important things:
|
||||
* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.)
|
||||
* In function `from_json`, use function [`at()`](../api/basic_json/at.md) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior.
|
||||
* You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these.
|
||||
* If you control the type, consider defining `to_json`/`from_json` as `friend` functions inside the class ("hidden friends"). Argument-dependent lookup then only finds them for your type, which also avoids a [GCC < 11 compilation error](../home/faq.md#incomplete-detector-type-with-gcc-11).
|
||||
|
||||
|
||||
## Simplify your life with macros
|
||||
|
||||
@@ -307,6 +307,51 @@ APP_CPPFLAGS += -frtti -fexceptions
|
||||
The code compiles successfully with [Android NDK](https://developer.android.com/ndk/index.html?hl=ml), Revision 9 - 11 (and possibly later) and [CrystaX's Android NDK](https://www.crystax.net/en/android/ndk) version 10.
|
||||
|
||||
|
||||
### Incomplete `detector` type with GCC < 11
|
||||
|
||||
!!! question
|
||||
|
||||
Why does GCC 10 or older fail with `invalid use of incomplete type 'struct nlohmann::detail::detector<..., to_json_function, ...>'` for a type that holds an `optional` member?
|
||||
|
||||
This happens with GCC 10 and older in C++11/C++14 mode when all of these hold:
|
||||
|
||||
- a class `Holder` has an `optional<Dummy>` member (e.g., `boost::optional`),
|
||||
- `Dummy` has a constructor taking a `json` value, and
|
||||
- `to_json` for `Holder` is a free function in the namespace of `Dummy`.
|
||||
|
||||
```cpp
|
||||
class Dummy {
|
||||
public:
|
||||
explicit Dummy(const nlohmann::json& j);
|
||||
};
|
||||
|
||||
class Holder {
|
||||
boost::optional<Dummy> d;
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json& j, const Holder& h); // triggers the error
|
||||
```
|
||||
|
||||
To decide whether `Dummy` is copyable, the compiler checks whether a `Dummy` can be converted to `json`. That check
|
||||
looks up `to_json` via argument-dependent lookup, finds the unrelated `to_json` for `Holder`, and eventually asks again
|
||||
whether `Dummy` is copyable. GCC before version 11 turns this cycle into a hard error; GCC 11 and later, Clang, and
|
||||
C++17 mode compile the code. The same error shows up without this library whenever a constrained converting constructor
|
||||
is involved, so the library can't avoid it.
|
||||
|
||||
To work around this, define `to_json` (and `from_json`) as a *hidden friend* inside the class. That way,
|
||||
argument-dependent lookup only finds it for `Holder`:
|
||||
|
||||
```cpp
|
||||
class Holder {
|
||||
boost::optional<Dummy> d;
|
||||
|
||||
friend void to_json(nlohmann::json& j, const Holder& h) { /* ... */ }
|
||||
};
|
||||
```
|
||||
|
||||
The [`NLOHMANN_DEFINE_TYPE_INTRUSIVE`](../api/macros/nlohmann_define_type_intrusive.md) macros define hidden friends as
|
||||
well. See [#3669](https://github.com/nlohmann/json/issues/3669) for details.
|
||||
|
||||
### Missing STL function
|
||||
|
||||
!!! question "Questions"
|
||||
|
||||
@@ -42,10 +42,6 @@ namespace detail
|
||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||
* allocated value (e.g., a string). See bug issue
|
||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||
*
|
||||
* A value that has to be allocated is created before the old one is destroyed:
|
||||
* were it the other way around, an exception while creating the new value would
|
||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||
*/
|
||||
|
||||
template<value_t> struct external_constructor;
|
||||
@@ -69,20 +65,18 @@ struct external_constructor<value_t::string>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(s);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = s;
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(s));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = std::move(s);
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
@@ -91,10 +85,9 @@ struct external_constructor<value_t::string>
|
||||
int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||
{
|
||||
auto* created = j.template create<typename BasicJsonType::string_t>(str);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value.string = created;
|
||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
@@ -105,20 +98,18 @@ struct external_constructor<value_t::binary>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(b);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(b));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
@@ -168,10 +159,9 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(arr);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = arr;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -179,10 +169,9 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(arr));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = std::move(arr);
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -198,10 +187,9 @@ struct external_constructor<value_t::array>
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value.array = created;
|
||||
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -209,17 +197,15 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||
{
|
||||
typename BasicJsonType::array_t elements;
|
||||
elements.reserve(arr.size());
|
||||
for (const bool x : arr)
|
||||
{
|
||||
elements.push_back(x);
|
||||
}
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->reserve(arr.size());
|
||||
for (const bool x : arr)
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
}
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
@@ -227,12 +213,11 @@ struct external_constructor<value_t::array>
|
||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||
{
|
||||
typename BasicJsonType::array_t elements(arr.size());
|
||||
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->resize(arr.size());
|
||||
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -244,16 +229,14 @@ struct external_constructor<value_t::array>
|
||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||
{
|
||||
typename BasicJsonType::array_t elements;
|
||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||
{
|
||||
elements.push_back(x);
|
||||
}
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.m_data.m_value = value_t::array;
|
||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
}
|
||||
j.assert_invariant();
|
||||
}
|
||||
#endif
|
||||
@@ -265,10 +248,9 @@ struct external_constructor<value_t::object>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(obj);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = obj;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -276,10 +258,9 @@ struct external_constructor<value_t::object>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(obj));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = std::move(obj);
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -291,10 +272,9 @@ struct external_constructor<value_t::object>
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value.object = created;
|
||||
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
+15
-15
@@ -1702,8 +1702,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
if (is_an_object)
|
||||
{
|
||||
// the initializer list is a list of pairs -> create an object
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
|
||||
for (auto& element_ref : init)
|
||||
{
|
||||
@@ -1725,8 +1725,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
#endif
|
||||
// the initializer list describes an array -> create an array
|
||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||
}
|
||||
|
||||
set_parents();
|
||||
@@ -1739,8 +1739,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(const typename binary_t::container_type& init)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_value = init;
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = init;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1750,8 +1750,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_value = binary_t(init, subtype);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = binary_t(init, subtype);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1761,8 +1761,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(typename binary_t::container_type&& init)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_value = std::move(init);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = std::move(init);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1772,8 +1772,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -2819,8 +2819,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value.array = create<array_t>();
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value.array = create<array_t>();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -2879,8 +2879,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -2932,8 +2932,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -3868,8 +3868,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -3901,8 +3901,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -3933,8 +3933,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -3989,8 +3989,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -4014,8 +4014,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -4196,8 +4196,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
|
||||
@@ -6571,10 +6571,6 @@ namespace detail
|
||||
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
||||
* allocated value (e.g., a string). See bug issue
|
||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
||||
*
|
||||
* A value that has to be allocated is created before the old one is destroyed:
|
||||
* were it the other way around, an exception while creating the new value would
|
||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||
*/
|
||||
|
||||
template<value_t> struct external_constructor;
|
||||
@@ -6598,20 +6594,18 @@ struct external_constructor<value_t::string>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(s);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = s;
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(s));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = std::move(s);
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
@@ -6620,10 +6614,9 @@ struct external_constructor<value_t::string>
|
||||
int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
||||
{
|
||||
auto* created = j.template create<typename BasicJsonType::string_t>(str);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::string;
|
||||
j.m_data.m_value.string = created;
|
||||
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
@@ -6634,20 +6627,18 @@ struct external_constructor<value_t::binary>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(b);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(b));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::binary;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
||||
j.assert_invariant();
|
||||
}
|
||||
};
|
||||
@@ -6697,10 +6688,9 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(arr);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = arr;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6708,10 +6698,9 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(arr));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = std::move(arr);
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6727,10 +6716,9 @@ struct external_constructor<value_t::array>
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
auto* created = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value.array = created;
|
||||
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6738,17 +6726,15 @@ struct external_constructor<value_t::array>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
||||
{
|
||||
typename BasicJsonType::array_t elements;
|
||||
elements.reserve(arr.size());
|
||||
for (const bool x : arr)
|
||||
{
|
||||
elements.push_back(x);
|
||||
}
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->reserve(arr.size());
|
||||
for (const bool x : arr)
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
}
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
@@ -6756,12 +6742,11 @@ struct external_constructor<value_t::array>
|
||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
||||
{
|
||||
typename BasicJsonType::array_t elements(arr.size());
|
||||
std::copy(std::begin(arr), std::end(arr), elements.begin());
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = value_t::array;
|
||||
j.m_data.m_value.array->resize(arr.size());
|
||||
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6773,16 +6758,14 @@ struct external_constructor<value_t::array>
|
||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||
{
|
||||
typename BasicJsonType::array_t elements;
|
||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||
{
|
||||
elements.push_back(x);
|
||||
}
|
||||
const typename BasicJsonType::json_value value(std::move(elements));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value;
|
||||
j.set_parents();
|
||||
j.m_data.m_value = value_t::array;
|
||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
}
|
||||
j.assert_invariant();
|
||||
}
|
||||
#endif
|
||||
@@ -6794,10 +6777,9 @@ struct external_constructor<value_t::object>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(obj);
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = obj;
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6805,10 +6787,9 @@ struct external_constructor<value_t::object>
|
||||
template<typename BasicJsonType>
|
||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
||||
{
|
||||
const typename BasicJsonType::json_value value(std::move(obj));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value = value;
|
||||
j.m_data.m_value = std::move(obj);
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -6820,10 +6801,9 @@ struct external_constructor<value_t::object>
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
auto* created = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::object;
|
||||
j.m_data.m_value.object = created;
|
||||
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
@@ -27605,8 +27585,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
if (is_an_object)
|
||||
{
|
||||
// the initializer list is a list of pairs -> create an object
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
|
||||
for (auto& element_ref : init)
|
||||
{
|
||||
@@ -27628,8 +27608,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
#endif
|
||||
// the initializer list describes an array -> create an array
|
||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value.array = create<array_t>(init.begin(), init.end());
|
||||
}
|
||||
|
||||
set_parents();
|
||||
@@ -27642,8 +27622,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(const typename binary_t::container_type& init)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_value = init;
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = init;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -27653,8 +27633,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_value = binary_t(init, subtype);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = binary_t(init, subtype);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -27664,8 +27644,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(typename binary_t::container_type&& init)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_value = std::move(init);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = std::move(init);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -27675,8 +27655,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
|
||||
{
|
||||
auto res = basic_json();
|
||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||
res.m_data.m_type = value_t::binary;
|
||||
res.m_data.m_value = binary_t(std::move(init), subtype);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -28722,8 +28702,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value.array = create<array_t>();
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value.array = create<array_t>();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -28782,8 +28762,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -28835,8 +28815,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -29771,8 +29751,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -29804,8 +29784,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -29836,8 +29816,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -29892,8 +29872,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an array
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::array;
|
||||
m_data.m_type = value_t::array;
|
||||
m_data.m_value = value_t::array;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -29917,8 +29897,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// transform a null object into an object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value = value_t::object;
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value = value_t::object;
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
@@ -30099,8 +30079,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
// implicitly convert a null value to an empty object
|
||||
if (is_null())
|
||||
{
|
||||
m_data.m_value.object = create<object_t>();
|
||||
m_data.m_type = value_t::object;
|
||||
m_data.m_value.object = create<object_t>();
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <valarray>
|
||||
#if JSON_HAS_RANGES
|
||||
#include <ranges>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
// special test case to check if memory is leaked if constructor throws
|
||||
@@ -399,173 +394,3 @@ TEST_CASE("bad my_allocator::construct")
|
||||
j["test"].push_back("should not leak");
|
||||
}
|
||||
}
|
||||
|
||||
// the no-exceptions CI job skips every CHECK_THROWS_AS, which would leave
|
||||
// next_construct_fails set for the next allocation outside a check
|
||||
#if !defined(JSON_NOEXCEPTION)
|
||||
TEST_CASE("a failed allocation leaves the value unchanged")
|
||||
{
|
||||
// create JSON type using the throwing allocator
|
||||
using my_json = nlohmann::basic_json<std::map,
|
||||
std::vector,
|
||||
std::string,
|
||||
bool,
|
||||
std::int64_t,
|
||||
std::uint64_t,
|
||||
double,
|
||||
my_allocator>;
|
||||
|
||||
// Each of these creates a string, array, object, or binary value. The
|
||||
// value must be created before the type is changed: otherwise, a failed
|
||||
// creation left a value of the new type without anything behind it (an
|
||||
// assertion in its destructor, a null pointer everywhere else) or, when
|
||||
// an old value was destroyed first, with a pointer to that destroyed one.
|
||||
|
||||
SECTION("creating a binary value")
|
||||
{
|
||||
const std::vector<std::uint8_t> bytes = {1, 2, 3};
|
||||
my_json _;
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(_ = my_json::binary(bytes), std::bad_alloc&);
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(_ = my_json::binary(bytes, 42), std::bad_alloc&);
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes)), std::bad_alloc&);
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(_ = my_json::binary(std::vector<std::uint8_t>(bytes), 42), std::bad_alloc&);
|
||||
next_construct_fails = false;
|
||||
}
|
||||
|
||||
SECTION("turning a null value into an array or object")
|
||||
{
|
||||
my_json j;
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j[0], std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j["key"], std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j[std::string_view("key")], std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
#endif
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j.push_back(my_json(1)), std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
|
||||
const my_json one = 1;
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j.push_back(one), std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j.push_back(my_json::object_t::value_type("key", 1)), std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j.emplace_back(1), std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j.emplace("key", 1), std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
|
||||
const my_json object = {{"key", 1}};
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(j.update(object), std::bad_alloc&);
|
||||
CHECK(j.is_null());
|
||||
|
||||
next_construct_fails = false;
|
||||
}
|
||||
|
||||
SECTION("converting into an existing value")
|
||||
{
|
||||
// to_json replaces the value it is given; the old one must survive a
|
||||
// failed creation of the new one
|
||||
my_json j = "old";
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::string("new")), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<int> {1, 2}), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
// with iterator debugging, the default constructor of VS 2015's
|
||||
// std::vector is noexcept but constructs a proxy with the allocator,
|
||||
// which terminates when this allocator throws
|
||||
#if !(defined(_MSC_VER) && _MSC_VER < 1910 && defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0)
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::vector<bool> {true, false}), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
#endif
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::map<std::string, int> {{"a", 1}}), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, my_json::binary_t({1, 2})), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
// the overloads for lvalues of the value types, for the value types
|
||||
// themselves, and for the remaining compatible types
|
||||
const std::string string = "new";
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, string), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, "new"), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
// to_json only moves a binary value that it converted from another
|
||||
// container type, which my_json's std::vector<std::uint8_t> is not
|
||||
using binary_constructor = nlohmann::detail::external_constructor<nlohmann::detail::value_t::binary>;
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(binary_constructor::construct(j, my_json::binary_t({1, 2})), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
my_json::array_t array = {1, 2};
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, array), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(array)), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
my_json::object_t object = {{"a", 1}};
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, object), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::move(object)), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, std::valarray<int> {1, 2}), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
const std::vector<int> numbers = {1, 2};
|
||||
next_construct_fails = true;
|
||||
CHECK_THROWS_AS(nlohmann::to_json(j, numbers | std::views::filter([](int /*unused*/)
|
||||
{
|
||||
return true;
|
||||
})), std::bad_alloc&);
|
||||
CHECK(j == "old");
|
||||
#endif
|
||||
|
||||
next_construct_fails = false;
|
||||
nlohmann::to_json(j, std::vector<int> {1, 2});
|
||||
CHECK(j == my_json({1, 2}));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -241,6 +241,52 @@ class my_allocator : public std::allocator<T>
|
||||
};
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// for #3669
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
// mimics boost::optional's converting constructor, whose SFINAE check asks
|
||||
// whether T is constructible from const U&
|
||||
template<class T, class Arg>
|
||||
struct issue3669_is_constructible
|
||||
{
|
||||
template<class T2, class A2, class = decltype(T2(std::declval<A2>()))>
|
||||
static char test(int);
|
||||
template<class, class>
|
||||
static long test(...);
|
||||
static constexpr bool value = sizeof(test<T, Arg>(0)) == 1;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class issue3669_optional
|
||||
{
|
||||
public:
|
||||
issue3669_optional() = default;
|
||||
template<class U>
|
||||
issue3669_optional(const issue3669_optional<U>& /*unused*/, // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
|
||||
typename std::enable_if<issue3669_is_constructible<T, const U&>::value, bool>::type /*unused*/ = true) {}
|
||||
};
|
||||
|
||||
class Issue3669Dummy
|
||||
{
|
||||
public:
|
||||
explicit Issue3669Dummy(const json& /*unused*/) {}
|
||||
};
|
||||
|
||||
class Issue3669Holder
|
||||
{
|
||||
issue3669_optional<Issue3669Dummy> d{};
|
||||
|
||||
// GCC < 11 (C++11/14) rejects a free to_json(json&, const Issue3669Holder&)
|
||||
// here, because ADL for Issue3669Dummy finds it and closes an instantiation
|
||||
// cycle; a hidden friend is only visible to ADL for Issue3669Holder
|
||||
friend void to_json(json& j, const Issue3669Holder& h)
|
||||
{
|
||||
static_cast<void>(h.d); // silence -Wunused-private-field
|
||||
j = "holder";
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("regression tests 2")
|
||||
{
|
||||
SECTION("issue #1001 - Fix memory leak during parser callback")
|
||||
@@ -766,6 +812,14 @@ TEST_CASE("regression tests 2")
|
||||
CHECK(j == k);
|
||||
}
|
||||
|
||||
SECTION("issue #3669 - invalid use of incomplete type with optional member and to_json")
|
||||
{
|
||||
const Issue3669Holder h{};
|
||||
const Issue3669Holder h2(h); // NOLINT(performance-unnecessary-copy-initialization)
|
||||
const json j = h2;
|
||||
CHECK(j == "holder");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("regression test - parser callback must not lose a duplicate key's prior value")
|
||||
|
||||
Reference in New Issue
Block a user