From 5086c607e848f08acb2be4d423df590725e4665b Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Fri, 24 Jul 2026 15:05:51 +0100 Subject: [PATCH] fix(json): add missing iterator traits to Value iterators json::ValueConstIterator and ValueIterator declared difference_type, reference and pointer but not value_type or iterator_category. Under C++23, std::iterator_traits then classifies them as output iterators, so std::all_of over a Value's members (isValidJson2 in RPCCall.cpp) fails to instantiate on GCC 13/14 with: cannot convert 'output_iterator_tag' to 'std::input_iterator_tag' GCC 15 masks this via LWG-3798/P2609, but the perf CI image ships GCC 13, so the source needs the traits regardless. The iterators wrap a std::map iterator (++/-- only), so the category is bidirectional. Add value_type + iterator_category to both iterators, include , and add a regression test asserting the traits and that std::all_of / std::count_if compile and run over Value members. Co-Authored-By: Claude Opus 4.8 (1M context) --- include/xrpl/json/json_value.h | 11 +++++++++ src/tests/libxrpl/json/Value.cpp | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/include/xrpl/json/json_value.h b/include/xrpl/json/json_value.h index 47ad3ac1e0..77c5f65210 100644 --- a/include/xrpl/json/json_value.h +++ b/include/xrpl/json/json_value.h @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -623,6 +624,12 @@ class ValueConstIterator : public ValueIteratorBase public: using size_t = unsigned int; using difference_type = int; + // std::iterator_traits needs value_type and iterator_category to classify + // this as a Cpp17InputIterator; without them it defaults to output-only, + // which breaks standard algorithms (e.g. std::all_of). The iterator walks a + // map both ways via ++/--, so it is bidirectional. + using value_type = Value; + using iterator_category = std::bidirectional_iterator_tag; using reference = Value const&; using pointer = Value const*; using SelfType = ValueConstIterator; @@ -687,6 +694,10 @@ class ValueIterator : public ValueIteratorBase public: using size_t = unsigned int; using difference_type = int; + // See ValueConstIterator: value_type and iterator_category are required for + // std::iterator_traits to treat this as a bidirectional iterator. + using value_type = Value; + using iterator_category = std::bidirectional_iterator_tag; using reference = Value&; using pointer = Value*; using SelfType = ValueIterator; diff --git a/src/tests/libxrpl/json/Value.cpp b/src/tests/libxrpl/json/Value.cpp index a58a5df9fd..fdbc0b7406 100644 --- a/src/tests/libxrpl/json/Value.cpp +++ b/src/tests/libxrpl/json/Value.cpp @@ -11,12 +11,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include namespace xrpl { @@ -1331,6 +1333,44 @@ TEST(json_value, iterator) } } +TEST(json_value, iterator_traits) +{ + // The iterators must expose value_type and iterator_category so that + // std::iterator_traits classifies them as (at least) input iterators. + // Without these, iterator_traits deduces output_iterator_tag and standard + // algorithms such as std::all_of fail to instantiate. + using CIt = json::ValueConstIterator; + using It = json::ValueIterator; + + static_assert(std::is_same_v::value_type, json::Value>); + static_assert(std::is_same_v::value_type, json::Value>); + static_assert(std::is_same_v< + std::iterator_traits::iterator_category, + std::bidirectional_iterator_tag>); + static_assert(std::is_same_v< + std::iterator_traits::iterator_category, + std::bidirectional_iterator_tag>); + + // A standard algorithm over the iterators must compile and run. This is the + // exact usage (std::all_of over a Value's members) that regressed the build. + json::Value obj{json::ValueType::Object}; + obj["a"] = 2; + obj["b"] = 4; + obj["c"] = 6; + + json::Value const& cobj = obj; + EXPECT_TRUE(std::all_of(cobj.begin(), cobj.end(), [](json::Value const& v) { + return v.asInt() % 2 == 0; + })); + EXPECT_FALSE( + std::all_of(cobj.begin(), cobj.end(), [](json::Value const& v) { return v.asInt() > 3; })); + + // count_if exercises the input-iterator contract and returns an exact count. + EXPECT_EQ( + std::count_if(cobj.begin(), cobj.end(), [](json::Value const& v) { return v.asInt() > 3; }), + 2); +} + TEST(json_value, nest_limits) { json::Reader r;