Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/workflows/abi-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]
shared_libs: [ON, OFF]
include:
- jsoncpp_std: 11
app_std: 17
- jsoncpp_std: 17
app_std: 11
- jsoncpp_std: 11
app_std: 23
- jsoncpp_std: 23
app_std: 11
- jsoncpp_std: 17
app_std: 23
- jsoncpp_std: 23
app_std: 17

steps:
- name: checkout project
Expand Down Expand Up @@ -47,11 +55,12 @@ jobs:

find_package(jsoncpp REQUIRED CONFIG)

add_executable(abi_test stringView.cpp)
add_executable(abi_test jsontest.cpp fuzz.cpp main.cpp)
target_link_libraries(abi_test PRIVATE JsonCpp::JsonCpp)
EOF

cp $GITHUB_WORKSPACE/example/stringView/stringView.cpp example-app/stringView.cpp
cp src/test_lib_json/*.cpp example-app/
cp src/test_lib_json/*.h example-app/

- name: build example app (C++${{ matrix.app_std }})
shell: bash
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
cxx_standard: [11, 17]

steps:
- name: checkout project
uses: actions/checkout@v4

- name: build project
uses: threeal/cmake-action@v2.0.0
with:
options: CMAKE_CXX_STANDARD=${{ matrix.cxx_standard }}

2 changes: 2 additions & 0 deletions include/json/forwards.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
class ValueMembersView;
class ValueConstMembersView;

} // namespace Json

Expand Down
130 changes: 130 additions & 0 deletions include/json/value.h
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,11 @@ class JSON_API Value {
iterator begin();
iterator end();

// \brief Returns a view of member pairs for range-based for loops.
ValueMembersView members();
// \brief Returns a view of member pairs for range-based for loops.
ValueConstMembersView members() const;

/// \brief Returns a reference to the first element in the `Value`.
/// Requires that this value holds an array or json object, with at least one
/// element.
Expand Down Expand Up @@ -1040,6 +1045,131 @@ class JSON_API ValueIterator : public ValueIteratorBase {
pointer operator->() const { return const_cast<pointer>(&deref()); }
};

/** \brief Proxy struct to enable range-based for loops over object members.
*/
struct MemberProxy {
const String name;
Value& value;
};

/** \brief Proxy struct to enable range-based for loops over const object
* members.
*/
struct ConstMemberProxy {
const String name;
const Value& value;
};

/** \brief Iterator adapter for range-based for loops.
*/
class ValueMembersIterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = MemberProxy;
using difference_type = int;
using pointer = MemberProxy*;
using reference = MemberProxy;

ValueMembersIterator() = default;
explicit ValueMembersIterator(ValueIterator const& iter) : it_(iter) {}

ValueMembersIterator& operator++() {
++it_;
return *this;
}
ValueMembersIterator operator++(int) {
ValueMembersIterator temp(*this);
++*this;
return temp;
}
bool operator==(ValueMembersIterator const& other) const {
return it_ == other.it_;
}
bool operator!=(ValueMembersIterator const& other) const {
return it_ != other.it_;
}
MemberProxy operator*() const { return MemberProxy{it_.name(), *it_}; }

private:
ValueIterator it_;
};

/** \brief Iterator adapter for range-based for loops.
*/
class ValueConstMembersIterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = ConstMemberProxy;
using difference_type = int;
using pointer = ConstMemberProxy*;
using reference = ConstMemberProxy;

ValueConstMembersIterator() = default;
explicit ValueConstMembersIterator(ValueConstIterator const& iter)
: it_(iter) {}

ValueConstMembersIterator& operator++() {
++it_;
return *this;
}
ValueConstMembersIterator operator++(int) {
ValueConstMembersIterator temp(*this);
++*this;
return temp;
}
bool operator==(ValueConstMembersIterator const& other) const {
return it_ == other.it_;
}
bool operator!=(ValueConstMembersIterator const& other) const {
return it_ != other.it_;
}
ConstMemberProxy operator*() const {
return ConstMemberProxy{it_.name(), *it_};
}

private:
ValueConstIterator it_;
};

/** \brief Range-based for loop adapter for object members.
*/
class ValueMembersView {
public:
ValueMembersView(ValueIterator begin, ValueIterator end)
: begin_(begin), end_(end) {}
ValueMembersIterator begin() const { return ValueMembersIterator(begin_); }
ValueMembersIterator end() const { return ValueMembersIterator(end_); }

private:
ValueIterator begin_;
ValueIterator end_;
};

/** \brief Range-based for loop adapter for object members.
*/
class ValueConstMembersView {
public:
ValueConstMembersView(ValueConstIterator begin, ValueConstIterator end)
: begin_(begin), end_(end) {}
ValueConstMembersIterator begin() const {
return ValueConstMembersIterator(begin_);
}
ValueConstMembersIterator end() const {
return ValueConstMembersIterator(end_);
}

private:
ValueConstIterator begin_;
ValueConstIterator end_;
};

inline ValueMembersView Value::members() {
return ValueMembersView(begin(), end());
}
inline ValueConstMembersView Value::members() const {
return ValueConstMembersView(begin(), end());
}

inline void swap(Value& a, Value& b) { a.swap(b); }

inline const Value& Value::front() const { return *begin(); }
Expand Down
48 changes: 48 additions & 0 deletions src/test_lib_json/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3924,6 +3924,54 @@ JSONTEST_FIXTURE_LOCAL(BomTest, notSkipBom) {

struct IteratorTest : JsonTest::TestCase {};

JSONTEST_FIXTURE_LOCAL(IteratorTest, members) {
Json::Value j;
j["k1"] = "a";
j["k2"] = "b";

std::vector<std::string> keys;
std::vector<std::string> values;

for (const auto& member : j.members()) {
keys.push_back(member.name);
values.push_back(member.value.asString());
}

JSONTEST_ASSERT((keys == std::vector<std::string>{"k1", "k2"}));
JSONTEST_ASSERT((values == std::vector<std::string>{"a", "b"}));

// Test modification through value reference
for (const auto& member : j.members()) {
member.value = "c";
}

JSONTEST_ASSERT(j["k1"].asString() == "c");

// Test const members
const Json::Value& cj = j;
keys.clear();
values.clear();

for (const auto& member : cj.members()) {
keys.push_back(member.name);
values.push_back(member.value.asString());
}

JSONTEST_ASSERT((keys == std::vector<std::string>{"k1", "k2"}));
JSONTEST_ASSERT((values == std::vector<std::string>{"c", "c"}));

#if __cplusplus >= 201703L
keys.clear();
values.clear();
for (auto const& [k, v] : cj.members()) {
keys.push_back(k);
values.push_back(v.asString());
}
JSONTEST_ASSERT((keys == std::vector<std::string>{"k1", "k2"}));
JSONTEST_ASSERT((values == std::vector<std::string>{"c", "c"}));
#endif
}

JSONTEST_FIXTURE_LOCAL(IteratorTest, convert) {
Json::Value j;
const Json::Value& cj = j;
Expand Down
Loading