mirror of
https://github.com/nlohmann/json.git
synced 2026-07-12 05:25:09 +00:00
Add iterator+sentinel tests and docs for binary deserializers (#5265)
* 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> * 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> * 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> * 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> * 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> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -7142,7 +7142,9 @@ class input_stream_adapter
|
||||
|
||||
// General-purpose iterator-based adapter. It might not be as fast as
|
||||
// theoretically possible for some containers, but it is extremely versatile.
|
||||
template<typename IteratorType>
|
||||
// SentinelType defaults to IteratorType for backward compatibility, but may
|
||||
// be a different type (e.g., a C++20 sentinel or counted_iterator).
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
class iterator_input_adapter
|
||||
{
|
||||
public:
|
||||
@@ -7156,9 +7158,10 @@ class iterator_input_adapter
|
||||
// in wide_string_input_adapter, which does not expose this).
|
||||
static constexpr bool supports_seek =
|
||||
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
|
||||
&& std::is_same<IteratorType, SentinelType>::value
|
||||
&& sizeof(char_type) == 1;
|
||||
|
||||
iterator_input_adapter(IteratorType first, IteratorType last)
|
||||
iterator_input_adapter(IteratorType first, SentinelType last)
|
||||
: begin(first), current(std::move(first)), end(std::move(last))
|
||||
{}
|
||||
|
||||
@@ -7203,12 +7206,14 @@ class iterator_input_adapter
|
||||
private:
|
||||
// whether IteratorType refers to a contiguous range and therefore supports
|
||||
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
|
||||
// library iterators such as those of std::vector and std::string)
|
||||
// library iterators such as those of std::vector and std::string).
|
||||
// The fast path also requires SentinelType == IteratorType so std::distance works.
|
||||
static constexpr bool iterator_is_contiguous =
|
||||
std::is_same<IteratorType, SentinelType>::value && (
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
std::contiguous_iterator<IteratorType> ||
|
||||
std::contiguous_iterator<IteratorType> ||
|
||||
#endif
|
||||
std::is_pointer<IteratorType>::value;
|
||||
std::is_pointer<IteratorType>::value);
|
||||
|
||||
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
||||
template<class T>
|
||||
@@ -7254,7 +7259,7 @@ class iterator_input_adapter
|
||||
|
||||
IteratorType begin;
|
||||
IteratorType current;
|
||||
IteratorType end;
|
||||
SentinelType end;
|
||||
|
||||
template<typename BaseInputAdapter, size_t T>
|
||||
friend struct wide_string_input_helper;
|
||||
@@ -7440,19 +7445,54 @@ class wide_string_input_adapter
|
||||
std::size_t utf8_bytes_filled = 0;
|
||||
};
|
||||
|
||||
template<typename IteratorType, typename Enable = void>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType, typename Enable = void>
|
||||
struct iterator_input_adapter_factory
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using sentinel_type = SentinelType;
|
||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using adapter_type = iterator_input_adapter<iterator_type>;
|
||||
using adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
static adapter_type create(IteratorType first, SentinelType last)
|
||||
{
|
||||
return adapter_type(std::move(first), std::move(last));
|
||||
}
|
||||
};
|
||||
|
||||
// Detection: whether IteratorType and SentinelType can be compared with !=
|
||||
template<typename IteratorType, typename SentinelType, typename = void>
|
||||
struct can_compare_ne_impl : std::false_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_impl < IteratorType, SentinelType,
|
||||
void_t < decltype(std::declval<IteratorType>() != std::declval<SentinelType>()) >>
|
||||
: std::true_type {};
|
||||
|
||||
// Workaround for reversed operator order
|
||||
template<typename IteratorType, typename SentinelType, typename = void>
|
||||
struct can_compare_ne_reversed : std::false_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_reversed < IteratorType, SentinelType,
|
||||
void_t < decltype(std::declval<SentinelType>() != std::declval<IteratorType>()) >>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_either_order : std::integral_constant < bool,
|
||||
can_compare_ne_impl<IteratorType, SentinelType>::value ||
|
||||
can_compare_ne_reversed<IteratorType, SentinelType>::value > {};
|
||||
|
||||
// std::nullptr_t is excluded explicitly: a literal `nullptr` passed as a
|
||||
// trailing default argument (e.g. parse(s, nullptr, ...)) must never be
|
||||
// mistaken for a sentinel, and some compilers (e.g. GCC 4.8) unreliably
|
||||
// SFINAE the `operator!=` detection above for std::nullptr_t against
|
||||
// container/string types, which would otherwise make such calls ambiguous
|
||||
// with the compatible-input overload.
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne : std::integral_constant < bool,
|
||||
!std::is_same<SentinelType, std::nullptr_t>::value &&
|
||||
can_compare_ne_either_order<IteratorType, SentinelType>::value > {};
|
||||
|
||||
template<typename T>
|
||||
struct is_iterator_of_multibyte
|
||||
{
|
||||
@@ -7463,25 +7503,31 @@ struct is_iterator_of_multibyte
|
||||
};
|
||||
};
|
||||
|
||||
template<typename IteratorType>
|
||||
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using sentinel_type = SentinelType;
|
||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using base_adapter_type = iterator_input_adapter<iterator_type>;
|
||||
using base_adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
static adapter_type create(IteratorType first, SentinelType last)
|
||||
{
|
||||
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
||||
}
|
||||
};
|
||||
|
||||
// General purpose iterator-based input
|
||||
template<typename IteratorType>
|
||||
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
|
||||
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
||||
// defaults to IteratorType for the common same-type case, but may differ for
|
||||
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
||||
// be compared with !=.
|
||||
template < typename IteratorType, typename SentinelType = IteratorType,
|
||||
typename = typename std::enable_if <
|
||||
can_compare_ne<IteratorType, SentinelType>::value >::type >
|
||||
typename iterator_input_adapter_factory<IteratorType, SentinelType>::adapter_type input_adapter(IteratorType first, SentinelType last)
|
||||
{
|
||||
using factory_type = iterator_input_adapter_factory<IteratorType>;
|
||||
using factory_type = iterator_input_adapter_factory<IteratorType, SentinelType>;
|
||||
return factory_type::create(first, last);
|
||||
}
|
||||
|
||||
@@ -25084,12 +25130,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief deserialize from a pair of character iterators
|
||||
/// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json parse(IteratorType first,
|
||||
IteratorType last,
|
||||
SentinelType last,
|
||||
parser_callback_t cb = nullptr,
|
||||
const bool allow_exceptions = true,
|
||||
const bool ignore_comments = false,
|
||||
@@ -25123,10 +25170,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
|
||||
}
|
||||
|
||||
/// @brief check if the input is valid JSON
|
||||
/// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
||||
template<typename IteratorType>
|
||||
static bool accept(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
static bool accept(IteratorType first, SentinelType last,
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
{
|
||||
@@ -25158,11 +25206,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||
}
|
||||
|
||||
/// @brief generate SAX events
|
||||
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
|
||||
template<class IteratorType, class SAX>
|
||||
template<class IteratorType, class SAX, class SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_NON_NULL(3)
|
||||
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
|
||||
static bool sax_parse(IteratorType first, SentinelType last, SAX* sax,
|
||||
input_format_t format = input_format_t::json,
|
||||
const bool strict = true,
|
||||
const bool ignore_comments = false,
|
||||
@@ -25462,11 +25511,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in CBOR format
|
||||
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_cbor(IteratorType first, IteratorType last,
|
||||
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
@@ -25519,11 +25569,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in MessagePack format
|
||||
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_msgpack(IteratorType first, IteratorType last,
|
||||
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -25573,11 +25624,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in UBJSON format
|
||||
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_ubjson(IteratorType first, IteratorType last,
|
||||
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -25627,11 +25679,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BJData format
|
||||
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -25657,11 +25710,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BSON format
|
||||
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_bson(IteratorType first, IteratorType last,
|
||||
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user