diff --git a/README.md b/README.md index 11d350ef..3d5d93bc 100644 --- a/README.md +++ b/README.md @@ -398,7 +398,7 @@ $ make $ ./json_unit "*" =============================================================================== -All tests passed (3341888 assertions in 28 test cases) +All tests passed (3341947 assertions in 28 test cases) ``` For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml). diff --git a/src/json.hpp b/src/json.hpp index a2760d60..ef507653 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -6152,43 +6152,35 @@ class basic_json } }; - /*! - @brief wrapper to access iterator member functions in range-based for - This class allows to access @ref iterator::key() and @ref iterator::value() - during range-based for loops. In these loops, a reference to the JSON - values is returned, so there is no access to the underlying iterator. - */ - class iterator_wrapper + private: + /// proxy class for the iterator_wrapper functions + template + class iteration_proxy { private: - /// the container to iterate - typename basic_json::reference container; - /// the type of the iterator to use while iteration - using json_iterator = decltype(std::begin(container)); - - /// internal iterator wrapper - class iterator_wrapper_internal + /// helper class for iteration + class iteration_proxy_internal { private: /// the iterator - json_iterator anchor; + IteratorType anchor; /// an index for arrays size_t array_index = 0; public: - /// construct wrapper given an iterator - iterator_wrapper_internal(json_iterator i) : anchor(i) + iteration_proxy_internal(IteratorType it) + : anchor(it) {} /// dereference operator (needed for range-based for) - iterator_wrapper_internal& operator*() + iteration_proxy_internal& operator*() { return *this; } /// increment operator (needed for range-based for) - iterator_wrapper_internal& operator++() + iteration_proxy_internal& operator++() { ++anchor; ++array_index; @@ -6197,7 +6189,7 @@ class basic_json } /// inequality operator (needed for range-based for) - bool operator!= (const iterator_wrapper_internal& o) + bool operator!= (const iteration_proxy_internal& o) { return anchor != o.anchor; } @@ -6228,31 +6220,57 @@ class basic_json } /// return value of the iterator - typename json_iterator::reference value() const + typename IteratorType::reference value() const { return anchor.value(); } }; + /// the container to iterate + typename IteratorType::reference container; + public: - /// construct iterator wrapper from a container - iterator_wrapper(typename basic_json::reference cont) + /// construct iteration proxy from a container + iteration_proxy(typename IteratorType::reference cont) : container(cont) {} /// return iterator begin (needed for range-based for) - iterator_wrapper_internal begin() + iteration_proxy_internal begin() { - return iterator_wrapper_internal(container.begin()); + return iteration_proxy_internal(container.begin()); } /// return iterator end (needed for range-based for) - iterator_wrapper_internal end() + iteration_proxy_internal end() { - return iterator_wrapper_internal(container.end()); + return iteration_proxy_internal(container.end()); } }; + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This functuion allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a reference + to the JSON values is returned, so there is no access to the underlying + iterator. + */ + static iteration_proxy iterator_wrapper(reference cont) + { + return iteration_proxy(cont); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + static iteration_proxy iterator_wrapper(const_reference cont) + { + return iteration_proxy(cont); + } + + private: ////////////////////// // lexer and parser // diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 44fc320f..d3d9f15a 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -6152,43 +6152,35 @@ class basic_json } }; - /*! - @brief wrapper to access iterator member functions in range-based for - This class allows to access @ref iterator::key() and @ref iterator::value() - during range-based for loops. In these loops, a reference to the JSON - values is returned, so there is no access to the underlying iterator. - */ - class iterator_wrapper + private: + /// proxy class for the iterator_wrapper functions + template + class iteration_proxy { private: - /// the container to iterate - typename basic_json::reference container; - /// the type of the iterator to use while iteration - using json_iterator = decltype(std::begin(container)); - - /// internal iterator wrapper - class iterator_wrapper_internal + /// helper class for iteration + class iteration_proxy_internal { private: /// the iterator - json_iterator anchor; + IteratorType anchor; /// an index for arrays size_t array_index = 0; public: - /// construct wrapper given an iterator - iterator_wrapper_internal(json_iterator i) : anchor(i) + iteration_proxy_internal(IteratorType it) + : anchor(it) {} /// dereference operator (needed for range-based for) - iterator_wrapper_internal& operator*() + iteration_proxy_internal& operator*() { return *this; } /// increment operator (needed for range-based for) - iterator_wrapper_internal& operator++() + iteration_proxy_internal& operator++() { ++anchor; ++array_index; @@ -6197,7 +6189,7 @@ class basic_json } /// inequality operator (needed for range-based for) - bool operator!= (const iterator_wrapper_internal& o) + bool operator!= (const iteration_proxy_internal& o) { return anchor != o.anchor; } @@ -6228,31 +6220,57 @@ class basic_json } /// return value of the iterator - typename json_iterator::reference value() const + typename IteratorType::reference value() const { return anchor.value(); } }; + /// the container to iterate + typename IteratorType::reference container; + public: - /// construct iterator wrapper from a container - iterator_wrapper(typename basic_json::reference cont) + /// construct iteration proxy from a container + iteration_proxy(typename IteratorType::reference cont) : container(cont) {} /// return iterator begin (needed for range-based for) - iterator_wrapper_internal begin() + iteration_proxy_internal begin() { - return iterator_wrapper_internal(container.begin()); + return iteration_proxy_internal(container.begin()); } /// return iterator end (needed for range-based for) - iterator_wrapper_internal end() + iteration_proxy_internal end() { - return iterator_wrapper_internal(container.end()); + return iteration_proxy_internal(container.end()); } }; + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This functuion allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a reference + to the JSON values is returned, so there is no access to the underlying + iterator. + */ + static iteration_proxy iterator_wrapper(reference cont) + { + return iteration_proxy(cont); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + static iteration_proxy iterator_wrapper(const_reference cont) + { + return iteration_proxy(cont); + } + + private: ////////////////////// // lexer and parser // diff --git a/test/unit.cpp b/test/unit.cpp index c7ce8a47..7b4323d3 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -9653,6 +9653,141 @@ TEST_CASE("iterator_wrapper") } } + SECTION("const object") + { + SECTION("value") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const value") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + const json j = {{"A", 1}, {"B", 2}}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "A"); + CHECK(i.value() == json(1)); + break; + } + + case 2: + { + CHECK(i.key() == "B"); + CHECK(i.value() == json(2)); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + SECTION("array") { SECTION("value") @@ -9799,6 +9934,141 @@ TEST_CASE("iterator_wrapper") } } + SECTION("const array") + { + SECTION("value") + { + const json j = {"A", "B"}; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("reference") + { + const json j = {"A", "B"}; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const value") + { + const json j = {"A", "B"}; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + + SECTION("const reference") + { + const json j = {"A", "B"}; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + switch (counter++) + { + case 1: + { + CHECK(i.key() == "0"); + CHECK(i.value() == "A"); + break; + } + + case 2: + { + CHECK(i.key() == "1"); + CHECK(i.value() == "B"); + break; + } + + default: + { + break; + } + } + } + + CHECK(counter == 3); + } + } + SECTION("primitive") { SECTION("value") @@ -9826,9 +10096,15 @@ TEST_CASE("iterator_wrapper") ++counter; CHECK(i.key() == ""); CHECK(i.value() == json(1)); + + // change value + i.value() = json(2); } CHECK(counter == 2); + + // check if value has changed + CHECK(j == json(2)); } SECTION("const value") @@ -9846,7 +10122,7 @@ TEST_CASE("iterator_wrapper") CHECK(counter == 2); } - SECTION("reference") + SECTION("const reference") { json j = 1; int counter = 1; @@ -9861,6 +10137,69 @@ TEST_CASE("iterator_wrapper") CHECK(counter == 2); } } + + SECTION("const primitive") + { + SECTION("value") + { + const json j = 1; + int counter = 1; + + for (auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("reference") + { + const json j = 1; + int counter = 1; + + for (auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const value") + { + const json j = 1; + int counter = 1; + + for (const auto i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + + SECTION("const reference") + { + const json j = 1; + int counter = 1; + + for (const auto& i : json::iterator_wrapper(j)) + { + ++counter; + CHECK(i.key() == ""); + CHECK(i.value() == json(1)); + } + + CHECK(counter == 2); + } + } } TEST_CASE("compliance tests from json.org")