mirror of
https://github.com/nlohmann/json.git
synced 2026-09-29 19:20:30 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a22140705f | ||
|
|
1e50d65882 | ||
|
|
63c212bf8e | ||
|
|
055158bbfa | ||
|
|
1a5d52404e |
@@ -79,7 +79,6 @@ 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.)
|
* 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.
|
* 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.
|
* 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
|
## Simplify your life with macros
|
||||||
|
|||||||
@@ -307,51 +307,6 @@ 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.
|
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
|
### Missing STL function
|
||||||
|
|
||||||
!!! question "Questions"
|
!!! question "Questions"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -241,52 +241,6 @@ 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")
|
TEST_CASE("regression tests 2")
|
||||||
{
|
{
|
||||||
SECTION("issue #1001 - Fix memory leak during parser callback")
|
SECTION("issue #1001 - Fix memory leak during parser callback")
|
||||||
@@ -812,14 +766,6 @@ TEST_CASE("regression tests 2")
|
|||||||
CHECK(j == k);
|
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")
|
TEST_CASE("regression test - parser callback must not lose a duplicate key's prior value")
|
||||||
|
|||||||
@@ -36,5 +36,31 @@
|
|||||||
</Expand>
|
</Expand>
|
||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
|
<!-- Fallback for when the basic_json entry above does not match: json_default_base is the (empty) default
|
||||||
|
base class of basic_json, and base class visualizers are inherited by derived types and evaluated
|
||||||
|
against the derived object, so m_data is accessible here. The class lives in {{ ns }} after
|
||||||
|
3.12.0 (#5238) and in {{ ns }}::detail up to 3.12.0, so both names are listed. -->
|
||||||
|
{% for default_base in ['json_default_base', 'detail::json_default_base'] %}
|
||||||
|
<Type Name="{{ ns }}::{{ default_base }}">
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::null">null</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::object">{*(m_data.m_value.object)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::array">{*(m_data.m_value.array)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::string">{*(m_data.m_value.string)}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::boolean">{m_data.m_value.boolean}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::number_integer">{m_data.m_value.number_integer}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::number_unsigned">{m_data.m_value.number_unsigned}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::number_float">{m_data.m_value.number_float}</DisplayString>
|
||||||
|
<DisplayString Condition="m_data.m_type == {{ ns }}::detail::value_t::discarded">discarded</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == {{ ns }}::detail::value_t::object">
|
||||||
|
*(m_data.m_value.object),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
<ExpandedItem Condition="m_data.m_type == {{ ns }}::detail::value_t::array">
|
||||||
|
*(m_data.m_value.array),view(simple)
|
||||||
|
</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</AutoVisualizer>
|
</AutoVisualizer>
|
||||||
|
|||||||
Reference in New Issue
Block a user