Compare commits

..

6 Commits

Author SHA1 Message Date
Niels Lohmann 0e03ecee10 Extend memcpy fast path to sized sentinels (e.g. std::counted_iterator)
Extend the memcpy fast path in iterator_input_adapter to sized
sentinels of a different type, not just same-type iterator pairs.
std::counted_iterator paired with std::default_sentinel_t already
satisfies std::contiguous_iterator and std::sized_sentinel_for, so
std::ranges::distance (C++20) lets that combination reach the fast
path too, instead of silently falling back to the byte-by-byte path.

- iterator_is_contiguous now also allows std::sized_sentinel_for<SentinelType,
  IteratorType> under C++20, gated the same way as the existing
  std::contiguous_iterator detection.
- get_elements_impl's fast path uses std::ranges::distance under C++20
  (works for both same-type and sized-sentinel pairs) and falls back to
  std::distance pre-C++20, where SentinelType is always IteratorType.
- Add a C++20-only test exercising json::parse/accept with
  std::counted_iterator + std::default_sentinel_t.
- Document std::default_sentinel_t + std::counted_iterator as a
  SentinelType example across parse.md, accept.md, sax_parse.md, and
  the five from_*.md pages, replacing an earlier ambiguously worded
  bullet.

Addresses review feedback: https://github.com/nlohmann/json/pull/5265#discussion_r3564237584

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 16:26:09 +02:00
Niels Lohmann d23806a327 Fix clang-tidy misc-const-correctness in heterogeneous sentinel test
json_str is only read via .data()/.size() and never reassigned, so
clang-tidy correctly flags it as const-able. Verified against the exact
CI job (silkeh/clang:dev, ci_clang_tidy target) by running clang-tidy
directly on this file plus the five binary-format sentinel tests
touched by prior commits; all are now clean.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 10:06:11 +02:00
Niels Lohmann fa9741f0ce Fix clang-tidy hicpp-named-parameter and misc-const-correctness
- Drop the unused reversed-order operator!= overload from
  utils::istreambuf_sentinel (only iterator != sentinel is ever
  evaluated) and name the remaining friend's sentinel parameter, fixing
  hicpp-named-parameter/readability-named-parameter.
- Mark the istreambuf_iterator first/last helper variable const in the
  five binary-format sentinel tests, fixing misc-const-correctness.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 00:10:23 +02:00
Niels Lohmann da95d1184e Fix -Wunneeded-internal-declaration for CustomSentinel in test
CustomSentinel lives in an anonymous namespace (internal linkage), and
the library's parse loop only ever evaluates the iterator-first
direction (it != last), so the reversed-order friend operator!= was
never referenced. Clang's -Weverything flags such unused internal
declarations as an error. Drop the unused overload; the used direction
is enough to satisfy can_compare_ne's either-order detection.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-10 23:25:21 +02:00
Niels Lohmann 146e5e0bc7 Merge iterator+sentinel overloads and fix ambiguity/CI issues
Address PR review feedback and CI failures:

- Merge the separate same-type and sentinel-type iterator overloads of
  parse(), accept(), sax_parse(), and the five from_* binary deserializers
  into a single overload with SentinelType defaulted to IteratorType,
  as suggested in review. Applied the same simplification to the
  detail::input_adapter() free functions.
- Fix a latent ambiguity: some compilers (e.g. GCC 4.8) unreliably SFINAE
  the operator!= detection for std::nullptr_t against container/string
  types, making calls like parse(s, nullptr, ...) ambiguous with the
  compatible-input overload. can_compare_ne now explicitly excludes
  std::nullptr_t as a SentinelType.
- Use a named enable_if_t template parameter instead of an unnamed
  function parameter for the SFINAE guard, fixing a clang-tidy
  hicpp-named-parameter/readability-named-parameter failure.
- Update parse.md, accept.md, sax_parse.md, and the five from_*.md pages
  to document the merged overload instead of separate (2)/(3) overloads,
  also fixing an over-160-char line that broke the documentation
  style_check CI job.
- Rework the BSON iterator+sentinel test to parse a BSON file already
  present in the test suite instead of writing/deleting a temp file.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-10 23:15:17 +02:00
Niels Lohmann 2269656bc6 Add iterator+sentinel tests and docs for binary deserializers
This commit extends the C++20 ranges support (iterator+sentinel pairs) to the
binary format deserializers from_cbor, from_msgpack, from_ubjson, from_bjdata,
and from_bson, matching what was already done for parse(), accept(), and
sax_parse().

Changes:
- Add istreambuf_sentinel helper to test_utils.hpp for EOF detection in tests
- Add 5 new test cases that read binary files directly via
  std::istreambuf_iterator<char> + sentinel, without pre-buffering
- Update documentation for all 5 from_* functions to document overload (3)
  with SentinelType parameter
- All tests pass; verified against existing test suite data
- Fix potential buffer over-read warning in heterogeneous iterator test

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-10 19:50:04 +02:00
11 changed files with 12 additions and 496 deletions
@@ -51,38 +51,6 @@ represent a byte array in modern C++.
The default values for `BinaryType` is `#!cpp std::vector<std::uint8_t>`. The default values for `BinaryType` is `#!cpp std::vector<std::uint8_t>`.
#### Custom BinaryType behavior
When a custom `BinaryType` is configured (other than the default `#!cpp std::vector<std::uint8_t>`), you can assign
values of that type directly to a `basic_json` instance, and they will automatically be recognized as binary values
rather than arrays:
```cpp
using custom_json = nlohmann::basic_json<
nlohmann::ordered_map, // ObjectType
std::vector, // ArrayType
std::string, // StringType
bool, // BooleanType
std::int64_t, // NumberIntegerType
std::uint64_t, // NumberUnsignedType
double, // NumberFloatType
std::allocator, // AllocatorType
nlohmann::adl_serializer,
std::vector<std::byte> // Custom BinaryType
>;
std::vector<std::byte> data{std::byte{1}, std::byte{2}, std::byte{3}};
custom_json j = data; // Creates a binary value, not an array
assert(j.is_binary());
// Round-tripping works seamlessly
auto extracted = j.get<std::vector<std::byte>>();
assert(extracted == data);
```
This automatic type detection is a convenience feature that only applies to custom (non-default) `BinaryType` configurations.
The default `nlohmann::json` continues to treat `#!cpp std::vector<std::uint8_t>` as arrays for backward compatibility.
#### Storage #### Storage
Binary Arrays are stored as pointers in a `basic_json` type. That is, for any access to array values, a pointer of the Binary Arrays are stored as pointers in a `basic_json` type. That is, for any access to array values, a pointer of the
@@ -38,8 +38,7 @@ When the macro is not defined, the library will define it to its default value.
Diagnostic messages can also be controlled with the CMake option Diagnostic messages can also be controlled with the CMake option
[`JSON_Diagnostics`](../../integration/cmake.md#json_diagnostics) (`OFF` by default) [`JSON_Diagnostics`](../../integration/cmake.md#json_diagnostics) (`OFF` by default)
which defines `JSON_DIAGNOSTICS` accordingly. Note this only applies when building the which defines `JSON_DIAGNOSTICS` accordingly.
library from source — see the pre-installed-package caveat on that page.
## Examples ## Examples
-36
View File
@@ -47,28 +47,6 @@ json j = {{"one", 1}, {"two", 2}};
auto m = j.get<std::map<std::string, int>>(); // {{"one", 1}, {"two", 2}} auto m = j.get<std::map<std::string, int>>(); // {{"one", 1}, {"two", 2}}
``` ```
`#!cpp std::pair` and `#!cpp std::tuple` are also supported, converting positionally to and from a JSON array:
```cpp
json j = {1.0, "hello", 42};
auto t = j.get<std::tuple<double, std::string, int>>(); // {1.0, "hello", 42}
```
!!! info "Extracting references into a tuple"
A tuple type may also hold references (e.g. `#!cpp std::tuple<double&, std::string&>`) to avoid copying: `get`
then returns a tuple of references pointing directly at the elements stored inside the `basic_json` array,
rather than a tuple of copies:
```cpp
json j = {1.0, "hello"};
auto refs = j.get<std::tuple<double&, std::string&>>();
std::get<1>(refs) = "world"; // modifies j[1] in place
```
A referenced type must be one the library actually stores (or an arithmetic type it can convert to/from);
otherwise this is a compile error.
## Implicit conversions ## Implicit conversions
By default, a JSON value implicitly converts to a compatible C++ type, so the explicit `get` call can often be omitted: By default, a JSON value implicitly converts to a compatible C++ type, so the explicit `get` call can often be omitted:
@@ -158,20 +136,6 @@ std::vector<int> numbers = {1, 2, 3};
json j = numbers; // [1,2,3] json j = numbers; // [1,2,3]
``` ```
!!! info "Constructing from a C++20 range view"
A `json` array can also be constructed directly from a C++20 range view (`std::ranges::view`), such as the result
of `std::views::filter` or `std::views::transform` -- no intermediate container is needed:
```cpp
std::vector<int> nums{1, 2, 37, 42, 21};
auto filtered = nums | std::views::filter([](int i) { return i > 10; });
json j(filtered); // [37,42,21]
```
This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due
to incomplete C++20 ranges support there.
## Your own types ## Your own types
The conversions above are built in for standard types. To make the same syntax work for **your own** types, provide The conversions above are built in for standard types. To make the same syntax work for **your own** types, provide
-25
View File
@@ -135,31 +135,6 @@ Enable CI build targets. The exact targets are used during the several CI steps
Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default. Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default.
!!! warning "Does not apply to a pre-installed package"
This option only takes effect when building nlohmann/json from source as part of your own
CMake project (e.g. via [`FetchContent`](#fetchcontent) or [`add_subdirectory`](#external)).
It has **no effect** on a package that was already built and installed elsewhere (Homebrew,
vcpkg, a system package, etc.) — the resulting compile definition is baked into the exported
`nlohmann_jsonTargets.cmake` at install time, and `set(JSON_Diagnostics ON)` before
`find_package()` does not change it (verified against the Homebrew-installed package: the
exported target still carries a fixed `$<$<BOOL:OFF>:JSON_DIAGNOSTICS=1>`, regardless of any
variable set in the consuming project).
To enable extended diagnostics for a pre-installed package, override the imported target's
property directly after `find_package()`:
```cmake
find_package(nlohmann_json REQUIRED)
set_target_properties(nlohmann_json::nlohmann_json PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "JSON_DIAGNOSTICS=1")
```
This only works cleanly when your project is the sole consumer of that imported target. If
nlohmann_json is pulled in from more than one place in your dependency graph with different
`JSON_DIAGNOSTICS` values, you may see a `"JSON_DIAGNOSTICS" redefined` compiler error, since
conflicting `-D` flags can end up on the same compile command line.
### `JSON_Diagnostic_Positions` ### `JSON_Diagnostic_Positions`
Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md). This option is `OFF` by default. Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md). This option is `OFF` by default.
@@ -19,7 +19,6 @@
#include <unordered_map> // unordered_map #include <unordered_map> // unordered_map
#include <utility> // pair, declval #include <utility> // pair, declval
#include <valarray> // valarray #include <valarray> // valarray
#include <vector> // vector
#include <nlohmann/detail/exceptions.hpp> #include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp> #include <nlohmann/detail/macro_scope.hpp>
@@ -333,7 +332,6 @@ template < typename BasicJsonType, typename ConstructibleArrayType,
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&& !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&& !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&& !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
!is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_basic_json<ConstructibleArrayType>::value, !is_basic_json<ConstructibleArrayType>::value,
int > = 0 > int > = 0 >
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
@@ -379,25 +377,6 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>(); bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
} }
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
int > = 0 >
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
{
if (j.is_binary())
{
bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
}
else if (j.is_array())
{
from_json_array_impl(j, bin, priority_tag<3> {});
}
else
{
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
}
}
template<typename BasicJsonType, typename ConstructibleObjectType, template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0> enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
@@ -177,11 +177,8 @@ struct external_constructor<value_t::array>
} }
template < typename BasicJsonType, typename CompatibleArrayType, template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
#if JSON_HAS_RANGES && !defined(__MINGW32__) int > = 0 >
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
, int > = 0 >
static void construct(BasicJsonType& j, const CompatibleArrayType& arr) static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
{ {
using std::begin; using std::begin;
@@ -221,25 +218,6 @@ struct external_constructor<value_t::array>
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
// std::ranges does not work properly on MinGW due to incomplete C++20 support
// see https://github.com/nlohmann/json/issues/4916
#if JSON_HAS_RANGES && !defined(__MINGW32__)
template<typename BasicJsonType, typename CompatibleArrayType,
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
static void construct(BasicJsonType& j, CompatibleArrayType && 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_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
}; };
template<> template<>
@@ -377,44 +355,19 @@ template < typename BasicJsonType, typename CompatibleArrayType,
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&& !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&& !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&& !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
!is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value&& !is_basic_json<CompatibleArrayType>::value,
!is_basic_json<CompatibleArrayType>::value
#if JSON_HAS_RANGES && !defined(__MINGW32__)
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
,
int > = 0 > int > = 0 >
inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr) inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
{ {
external_constructor<value_t::array>::construct(j, arr); external_constructor<value_t::array>::construct(j, arr);
} }
#if JSON_HAS_RANGES && !defined(__MINGW32__)
template < typename BasicJsonType, typename T,
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
&& !is_compatible_string_type<BasicJsonType, std::remove_cvref_t<T>>::value
&& !is_compatible_object_type<BasicJsonType, std::remove_cvref_t<T>>::value
&& !is_basic_json<std::remove_cvref_t<T>>::value, int > = 0 >
inline void to_json(BasicJsonType& j, T && arr)
{
external_constructor<value_t::array>::construct(j, std::forward<T>(arr));
}
#endif
template<typename BasicJsonType> template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
{ {
external_constructor<value_t::binary>::construct(j, bin); external_constructor<value_t::binary>::construct(j, bin);
} }
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
int > = 0 >
inline void to_json(BasicJsonType& j, const CompatibleArrayType& bin)
{
external_constructor<value_t::binary>::construct(j, typename BasicJsonType::binary_t(bin));
}
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr) inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
+1 -83
View File
@@ -13,15 +13,11 @@
#include <tuple> // tuple #include <tuple> // tuple
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type #include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
#include <utility> // declval #include <utility> // declval
#include <vector> // vector
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L #if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
#include <cstddef> // byte #include <cstddef> // byte
#endif #endif
#include <nlohmann/detail/iterators/iterator_traits.hpp> #include <nlohmann/detail/iterators/iterator_traits.hpp>
#include <nlohmann/detail/macro_scope.hpp> #include <nlohmann/detail/macro_scope.hpp>
#ifdef JSON_HAS_CPP_17
#include <optional> // optional
#endif
#include <nlohmann/detail/meta/call_std/begin.hpp> #include <nlohmann/detail/meta/call_std/begin.hpp>
#include <nlohmann/detail/meta/call_std/end.hpp> #include <nlohmann/detail/meta/call_std/end.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp> #include <nlohmann/detail/meta/cpp_future.hpp>
@@ -454,51 +450,6 @@ struct is_constructible_string_type
value_type_t, laundered_type >>::value; value_type_t, laundered_type >>::value;
}; };
// Forward declarations: iteration_proxy.hpp includes this file, so we cannot
// include it here.
template<typename IteratorType> class iteration_proxy;
template<typename IteratorType> class iteration_proxy_value;
// Identifies nlohmann's internal iteration-proxy types. These must be excluded
// before evaluating any std::ranges concept to avoid circular constraints.
template<typename T> struct is_iteration_proxy_type : std::false_type {};
template<typename T> struct is_iteration_proxy_type<iteration_proxy<T>> : std::true_type {};
template<typename T> struct is_iteration_proxy_type<iteration_proxy_value<T>> : std::true_type {};
// In C++26, std::optional satisfies std::ranges::view; exclude it so the
// range-view overload does not hijack the optional serializer.
#ifdef JSON_HAS_CPP_17
template<typename T> struct is_range_view_optional_type : std::false_type {};
template<typename T> struct is_range_view_optional_type<std::optional<T>> : std::true_type {};
#else
template<typename T> struct is_range_view_optional_type : std::false_type {};
#endif
// std::ranges does not work properly on MinGW due to incomplete C++20 support
// see https://github.com/nlohmann/json/issues/4916
#if JSON_HAS_RANGES && !defined(__MINGW32__)
// SafeToCheck guards against types that trigger circular constraints when
// std::ranges::view<T> is evaluated on GCC 12 / libstdc++ 12:
// - iteration_proxy / iteration_proxy_value directly
// - views wrapping the above (e.g. owning_view<iteration_proxy<...>>)
// - views wrapping basic_json (e.g. ref_view<json>) — same circularity
// via json's constructors → is_compatible_array_type → here
// nlohmann's plain range_value_t (iterator_traits-based) is safe to call
// before any std::ranges concept is touched, so we use it for the checks.
template < typename T, bool SafeToCheck =
!is_iteration_proxy_type<T>::value &&
!is_iteration_proxy_type<detected_t<range_value_t, T>>::value &&
!is_basic_json<detected_t<range_value_t, T>>::value &&
!is_range_view_optional_type<T>::value >
struct is_compatible_range_view : std::false_type {};
template<typename T>
struct is_compatible_range_view<T, true>
: std::bool_constant<std::ranges::view<T>> {};
#endif
template<typename BasicJsonType, typename CompatibleArrayType, typename = void> template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
struct is_compatible_array_type_impl : std::false_type {}; struct is_compatible_array_type_impl : std::false_type {};
@@ -510,38 +461,13 @@ struct is_compatible_array_type_impl <
is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&& is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
// special case for types like std::filesystem::path whose iterator's value_type are themselves // special case for types like std::filesystem::path whose iterator's value_type are themselves
// c.f. https://github.com/nlohmann/json/pull/3073 // c.f. https://github.com/nlohmann/json/pull/3073
!std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
// When range-view support is enabled, std::ranges::view types (e.g. std::string_view,
// filter_view) can match BOTH this iterator-based specialization AND the view-based one
// below, causing ambiguity. Exclude views here so the two specializations are mutually
// exclusive: this one handles plain iterable containers, the other handles views.
#if JSON_HAS_RANGES && !defined(__MINGW32__)
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
>>
{ {
static constexpr bool value = static constexpr bool value =
is_constructible<BasicJsonType, is_constructible<BasicJsonType,
range_value_t<CompatibleArrayType>>::value; range_value_t<CompatibleArrayType>>::value;
}; };
#if JSON_HAS_RANGES && !defined(__MINGW32__)
template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type_impl <
BasicJsonType, CompatibleArrayType,
enable_if_t < is_compatible_range_view<CompatibleArrayType>::value
&& !std::is_same<detected_t<range_value_t, CompatibleArrayType>, char>::value
&& !std::is_same<detected_t<range_value_t, CompatibleArrayType>, wchar_t>::value >>
{
// CompatibleArrayType is a std::ranges::view here, so std::ranges::range_value_t
// is safe and correctly handles C++20 iterators that may lack classic iterator_traits.
static constexpr bool value =
is_constructible<BasicJsonType,
std::ranges::range_value_t<CompatibleArrayType>>::value;
};
#endif
template<typename BasicJsonType, typename CompatibleArrayType> template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type struct is_compatible_array_type
: is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {}; : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
@@ -633,14 +559,6 @@ template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type struct is_compatible_type
: is_compatible_type_impl<BasicJsonType, CompatibleType> {}; : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_binary_type
{
static constexpr bool value =
std::is_same<typename BasicJsonType::binary_t::container_type, CompatibleArrayType>::value &&
!std::is_same<typename BasicJsonType::binary_t::container_type, std::vector<std::uint8_t>>::value;
};
template<typename BasicJsonType, typename CompatibleReferenceType> template<typename BasicJsonType, typename CompatibleReferenceType>
struct is_compatible_reference_type_impl struct is_compatible_reference_type_impl
{ {
+4 -154
View File
@@ -189,7 +189,6 @@
#include <unordered_map> // unordered_map #include <unordered_map> // unordered_map
#include <utility> // pair, declval #include <utility> // pair, declval
#include <valarray> // valarray #include <valarray> // valarray
#include <vector> // vector
// #include <nlohmann/detail/exceptions.hpp> // #include <nlohmann/detail/exceptions.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
@@ -3593,7 +3592,6 @@ NLOHMANN_JSON_NAMESPACE_END
#include <tuple> // tuple #include <tuple> // tuple
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type #include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
#include <utility> // declval #include <utility> // declval
#include <vector> // vector
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L #if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
#include <cstddef> // byte #include <cstddef> // byte
#endif #endif
@@ -3665,9 +3663,6 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/macro_scope.hpp> // #include <nlohmann/detail/macro_scope.hpp>
#ifdef JSON_HAS_CPP_17
#include <optional> // optional
#endif
// #include <nlohmann/detail/meta/call_std/begin.hpp> // #include <nlohmann/detail/meta/call_std/begin.hpp>
// __ _____ _____ _____ // __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ // __| | __| | | | JSON for Modern C++
@@ -4217,51 +4212,6 @@ struct is_constructible_string_type
value_type_t, laundered_type >>::value; value_type_t, laundered_type >>::value;
}; };
// Forward declarations: iteration_proxy.hpp includes this file, so we cannot
// include it here.
template<typename IteratorType> class iteration_proxy;
template<typename IteratorType> class iteration_proxy_value;
// Identifies nlohmann's internal iteration-proxy types. These must be excluded
// before evaluating any std::ranges concept to avoid circular constraints.
template<typename T> struct is_iteration_proxy_type : std::false_type {};
template<typename T> struct is_iteration_proxy_type<iteration_proxy<T>> : std::true_type {};
template<typename T> struct is_iteration_proxy_type<iteration_proxy_value<T>> : std::true_type {};
// In C++26, std::optional satisfies std::ranges::view; exclude it so the
// range-view overload does not hijack the optional serializer.
#ifdef JSON_HAS_CPP_17
template<typename T> struct is_range_view_optional_type : std::false_type {};
template<typename T> struct is_range_view_optional_type<std::optional<T>> : std::true_type {};
#else
template<typename T> struct is_range_view_optional_type : std::false_type {};
#endif
// std::ranges does not work properly on MinGW due to incomplete C++20 support
// see https://github.com/nlohmann/json/issues/4916
#if JSON_HAS_RANGES && !defined(__MINGW32__)
// SafeToCheck guards against types that trigger circular constraints when
// std::ranges::view<T> is evaluated on GCC 12 / libstdc++ 12:
// - iteration_proxy / iteration_proxy_value directly
// - views wrapping the above (e.g. owning_view<iteration_proxy<...>>)
// - views wrapping basic_json (e.g. ref_view<json>) — same circularity
// via json's constructors → is_compatible_array_type → here
// nlohmann's plain range_value_t (iterator_traits-based) is safe to call
// before any std::ranges concept is touched, so we use it for the checks.
template < typename T, bool SafeToCheck =
!is_iteration_proxy_type<T>::value &&
!is_iteration_proxy_type<detected_t<range_value_t, T>>::value &&
!is_basic_json<detected_t<range_value_t, T>>::value &&
!is_range_view_optional_type<T>::value >
struct is_compatible_range_view : std::false_type {};
template<typename T>
struct is_compatible_range_view<T, true>
: std::bool_constant<std::ranges::view<T>> {};
#endif
template<typename BasicJsonType, typename CompatibleArrayType, typename = void> template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
struct is_compatible_array_type_impl : std::false_type {}; struct is_compatible_array_type_impl : std::false_type {};
@@ -4273,38 +4223,13 @@ struct is_compatible_array_type_impl <
is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&& is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
// special case for types like std::filesystem::path whose iterator's value_type are themselves // special case for types like std::filesystem::path whose iterator's value_type are themselves
// c.f. https://github.com/nlohmann/json/pull/3073 // c.f. https://github.com/nlohmann/json/pull/3073
!std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
// When range-view support is enabled, std::ranges::view types (e.g. std::string_view,
// filter_view) can match BOTH this iterator-based specialization AND the view-based one
// below, causing ambiguity. Exclude views here so the two specializations are mutually
// exclusive: this one handles plain iterable containers, the other handles views.
#if JSON_HAS_RANGES && !defined(__MINGW32__)
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
>>
{ {
static constexpr bool value = static constexpr bool value =
is_constructible<BasicJsonType, is_constructible<BasicJsonType,
range_value_t<CompatibleArrayType>>::value; range_value_t<CompatibleArrayType>>::value;
}; };
#if JSON_HAS_RANGES && !defined(__MINGW32__)
template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type_impl <
BasicJsonType, CompatibleArrayType,
enable_if_t < is_compatible_range_view<CompatibleArrayType>::value
&& !std::is_same<detected_t<range_value_t, CompatibleArrayType>, char>::value
&& !std::is_same<detected_t<range_value_t, CompatibleArrayType>, wchar_t>::value >>
{
// CompatibleArrayType is a std::ranges::view here, so std::ranges::range_value_t
// is safe and correctly handles C++20 iterators that may lack classic iterator_traits.
static constexpr bool value =
is_constructible<BasicJsonType,
std::ranges::range_value_t<CompatibleArrayType>>::value;
};
#endif
template<typename BasicJsonType, typename CompatibleArrayType> template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_array_type struct is_compatible_array_type
: is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {}; : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
@@ -4396,14 +4321,6 @@ template<typename BasicJsonType, typename CompatibleType>
struct is_compatible_type struct is_compatible_type
: is_compatible_type_impl<BasicJsonType, CompatibleType> {}; : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
template<typename BasicJsonType, typename CompatibleArrayType>
struct is_compatible_binary_type
{
static constexpr bool value =
std::is_same<typename BasicJsonType::binary_t::container_type, CompatibleArrayType>::value &&
!std::is_same<typename BasicJsonType::binary_t::container_type, std::vector<std::uint8_t>>::value;
};
template<typename BasicJsonType, typename CompatibleReferenceType> template<typename BasicJsonType, typename CompatibleReferenceType>
struct is_compatible_reference_type_impl struct is_compatible_reference_type_impl
{ {
@@ -5547,7 +5464,6 @@ template < typename BasicJsonType, typename ConstructibleArrayType,
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&& !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&& !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&& !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
!is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_basic_json<ConstructibleArrayType>::value, !is_basic_json<ConstructibleArrayType>::value,
int > = 0 > int > = 0 >
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
@@ -5593,25 +5509,6 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>(); bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
} }
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
int > = 0 >
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
{
if (j.is_binary())
{
bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
}
else if (j.is_array())
{
from_json_array_impl(j, bin, priority_tag<3> {});
}
else
{
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
}
}
template<typename BasicJsonType, typename ConstructibleObjectType, template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0> enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
@@ -6308,11 +6205,8 @@ struct external_constructor<value_t::array>
} }
template < typename BasicJsonType, typename CompatibleArrayType, template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
#if JSON_HAS_RANGES && !defined(__MINGW32__) int > = 0 >
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
, int > = 0 >
static void construct(BasicJsonType& j, const CompatibleArrayType& arr) static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
{ {
using std::begin; using std::begin;
@@ -6352,25 +6246,6 @@ struct external_constructor<value_t::array>
j.set_parents(); j.set_parents();
j.assert_invariant(); j.assert_invariant();
} }
// std::ranges does not work properly on MinGW due to incomplete C++20 support
// see https://github.com/nlohmann/json/issues/4916
#if JSON_HAS_RANGES && !defined(__MINGW32__)
template<typename BasicJsonType, typename CompatibleArrayType,
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
static void construct(BasicJsonType& j, CompatibleArrayType && 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_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
}; };
template<> template<>
@@ -6508,44 +6383,19 @@ template < typename BasicJsonType, typename CompatibleArrayType,
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&& !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&& !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&& !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
!is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value&& !is_basic_json<CompatibleArrayType>::value,
!is_basic_json<CompatibleArrayType>::value
#if JSON_HAS_RANGES && !defined(__MINGW32__)
&& !is_compatible_range_view<CompatibleArrayType>::value
#endif
,
int > = 0 > int > = 0 >
inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr) inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
{ {
external_constructor<value_t::array>::construct(j, arr); external_constructor<value_t::array>::construct(j, arr);
} }
#if JSON_HAS_RANGES && !defined(__MINGW32__)
template < typename BasicJsonType, typename T,
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
&& !is_compatible_string_type<BasicJsonType, std::remove_cvref_t<T>>::value
&& !is_compatible_object_type<BasicJsonType, std::remove_cvref_t<T>>::value
&& !is_basic_json<std::remove_cvref_t<T>>::value, int > = 0 >
inline void to_json(BasicJsonType& j, T && arr)
{
external_constructor<value_t::array>::construct(j, std::forward<T>(arr));
}
#endif
template<typename BasicJsonType> template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
{ {
external_constructor<value_t::binary>::construct(j, bin); external_constructor<value_t::binary>::construct(j, bin);
} }
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
int > = 0 >
inline void to_json(BasicJsonType& j, const CompatibleArrayType& bin)
{
external_constructor<value_t::binary>::construct(j, typename BasicJsonType::binary_t(bin));
}
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0> enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr) inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
-15
View File
@@ -1782,21 +1782,6 @@ TEST_CASE("std::optional")
"[json.exception.type_error.302] type must be string, but is null", json::type_error&); "[json.exception.type_error.302] type must be string, but is null", json::type_error&);
CHECK_THROWS_WITH_AS(std::optional<int>(j_null), CHECK_THROWS_WITH_AS(std::optional<int>(j_null),
"[json.exception.type_error.302] type must be number, but is null", json::type_error&); "[json.exception.type_error.302] type must be number, but is null", json::type_error&);
// Assignment goes through the same overload resolution as direct
// construction, so it throws for the same reason. This relies on
// basic_json's implicit conversion operator, so it only applies
// when JSON_USE_IMPLICIT_CONVERSIONS is enabled (the default).
#if JSON_USE_IMPLICIT_CONVERSIONS
std::optional<std::string> opt_assign;
CHECK_THROWS_WITH_AS(opt_assign = j_null,
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
#endif
// get_to() is the correct way to obtain std::nullopt from a JSON null.
std::optional<std::string> opt_get_to = "placeholder";
j_null.get_to(opt_get_to);
CHECK(opt_get_to == std::nullopt);
} }
SECTION("string") SECTION("string")
-74
View File
@@ -1136,40 +1136,6 @@ TEST_CASE("regression tests 2")
CHECK((decoded == json_4804::array())); CHECK((decoded == json_4804::array()));
} }
SECTION("discussion #4209 - custom BinaryType direct assignment and round-tripping")
{
// Test that assigning a custom BinaryType directly creates a binary value, not an array
const std::vector<std::byte> original{std::byte{1}, std::byte{2}, std::byte{3}};
const json_4804 j = original;
CHECK(j.is_binary());
CHECK(!j.is_array());
// Test round-tripping: extracting the binary value back as the custom container type
const auto extracted = j.get<std::vector<std::byte>>();
CHECK(extracted == original);
// Test that the default json alias behavior is unchanged: std::vector<uint8_t> -> array
const json default_json = std::vector<std::uint8_t> {1, 2, 3};
CHECK(default_json.is_array());
CHECK(!default_json.is_binary());
}
SECTION("discussion #4209 - custom BinaryType extraction from parsed array")
{
// Test that extracting a custom BinaryType from a parsed JSON array still works
// (not just from a binary-typed node)
const auto j = json_4804::parse("[1,2,3]");
CHECK(j.is_array());
CHECK(!j.is_binary());
// Extracting as custom BinaryType should work from arrays
const auto extracted = j.get<std::vector<std::byte>>();
CHECK(extracted.size() == 3);
CHECK(extracted[0] == std::byte{1});
CHECK(extracted[1] == std::byte{2});
CHECK(extracted[2] == std::byte{3});
}
SECTION("issue #5046 - implicit conversion of return json to std::optional no longer implicit") SECTION("issue #5046 - implicit conversion of return json to std::optional no longer implicit")
{ {
const json jval{}; const json jval{};
@@ -1199,46 +1165,6 @@ TEST_CASE("regression tests 2")
} }
#endif #endif
#if JSON_HAS_RANGES && !defined(__MINGW32__)
SECTION("issue #4916 - constructing array from C++20 ranges view does not work")
{
std::vector<int> nums{1, 2, 37, 42, 21};
auto filteredNums = nums | std::views::filter([](int i)
{
return i > 10;
});
json const j(filteredNums);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({37, 42, 21}));
}
#endif
// owning_view is not available in libstdc++ < 12
#if JSON_HAS_RANGES && !defined(__MINGW32__) && !(defined(__GLIBCXX__) && _GLIBCXX_RELEASE < 12)
SECTION("issue #4916 - constructing array from prvalue C++20 ranges view (owning_view)")
{
json const j(std::vector<int> {1, 2, 37, 42, 21} | std::views::filter([](int i)
{
return i > 10;
}));
CHECK(j.type() == json::value_t::array);
CHECK(j == json({37, 42, 21}));
}
#endif
#if JSON_HAS_RANGES && !defined(__MINGW32__)
SECTION("issue #4916 - constructing array from C++20 transform view (prvalue elements)")
{
std::vector<int> nums{1, 2, 3};
auto t = nums | std::views::transform([](int i) noexcept
{
return i * 2;
});
json const j(t);
CHECK(j.type() == json::value_t::array);
CHECK(j == json({2, 4, 6}));
}
#endif
} }
TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
+3 -4
View File
@@ -216,16 +216,15 @@ TEST_CASE("Parse with heterogeneous iterator and sentinel types")
// JSON_HAS_CPP_20 (do not remove; see note at top of file) // JSON_HAS_CPP_20 (do not remove; see note at top of file)
TEST_CASE("Parse with std::counted_iterator and std::default_sentinel_t") TEST_CASE("Parse with std::counted_iterator and std::default_sentinel_t")
{ {
using iterator_type = std::string::const_iterator;
const std::string json_str = R"({"key":"value","array":[1,2,3]})"; const std::string json_str = R"({"key":"value","array":[1,2,3]})";
const auto len = static_cast<std::iter_difference_t<iterator_type>>(json_str.size()); const auto len = static_cast<std::iter_difference_t<std::string::const_iterator>>(json_str.size());
const std::counted_iterator<iterator_type> first(json_str.begin(), len); std::counted_iterator first(json_str.begin(), len);
const json j = json::parse(first, std::default_sentinel); const json j = json::parse(first, std::default_sentinel);
CHECK(j["key"] == "value"); CHECK(j["key"] == "value");
CHECK(j["array"].size() == 3); CHECK(j["array"].size() == 3);
const std::counted_iterator<iterator_type> first2(json_str.begin(), len); std::counted_iterator first2(json_str.begin(), len);
CHECK(json::accept(first2, std::default_sentinel)); CHECK(json::accept(first2, std::default_sentinel));
} }
#endif #endif