diff --git a/header_only/json.h b/header_only/json.h index ba992d64..ed932b9d 100644 --- a/header_only/json.h +++ b/header_only/json.h @@ -42,11 +42,12 @@ due to alignment. */ class json { - public: + private: // forward declaration to friend this class class iterator; class const_iterator; + public: // container types using value_type = json; using reference = json&; @@ -371,15 +372,15 @@ class json /// the payload value value_ {}; - public: + private: /// an iterator - class iterator : public std::iterator + class iterator : private std::iterator { friend class json; friend class json::const_iterator; public: iterator() = default; - iterator(json*); + iterator(json*, bool); iterator(const iterator&); ~iterator(); @@ -402,16 +403,18 @@ class json array_t::iterator* vi_ = nullptr; /// an iterator for JSON objects object_t::iterator* oi_ = nullptr; + /// whether iterator points to a valid object + bool invalid = true; }; /// a const iterator - class const_iterator : public std::iterator + class const_iterator : private std::iterator { friend class json; public: const_iterator() = default; - const_iterator(const json*); + const_iterator(const json*, bool); const_iterator(const const_iterator&); const_iterator(const json::iterator&); ~const_iterator(); @@ -435,6 +438,8 @@ class json array_t::const_iterator* vi_ = nullptr; /// an iterator for JSON objects object_t::const_iterator* oi_ = nullptr; + /// whether iterator reached past the end + bool invalid = true; }; private: @@ -1769,50 +1774,28 @@ json::const_iterator json::find(const std::string& key) const json::iterator json::find(const char* key) { - if (type_ != value_t::object) + auto result = end(); + + if (type_ == value_t::object) { - return end(); - } - else - { - const object_t::iterator i = value_.object->find(key); - if (i != value_.object->end()) - { - json::iterator result(this); - delete result.oi_; - result.oi_ = nullptr; - result.oi_ = new object_t::iterator(i); - return result; - } - else - { - return end(); - } + result.oi_ = new object_t::iterator(value_.object->find(key)); + result.invalid = (*(result.oi_) == value_.object->end()); } + + return result; } json::const_iterator json::find(const char* key) const { - if (type_ != value_t::object) + auto result = cend(); + + if (type_ == value_t::object) { - return end(); - } - else - { - const object_t::const_iterator i = value_.object->find(key); - if (i != value_.object->end()) - { - json::const_iterator result(this); - delete result.oi_; - result.oi_ = nullptr; - result.oi_ = new object_t::const_iterator(i); - return result; - } - else - { - return end(); - } + result.oi_ = new object_t::const_iterator(value_.object->find(key)); + result.invalid = (*(result.oi_) == value_.object->cend()); } + + return result; } bool json::operator==(const json& o) const noexcept @@ -1896,90 +1879,78 @@ bool json::operator!=(const json& o) const noexcept json::iterator json::begin() noexcept { - return json::iterator(this); + return json::iterator(this, true); } json::iterator json::end() noexcept { - return json::iterator(); + return json::iterator(this, false); } json::const_iterator json::begin() const noexcept { - return json::const_iterator(this); + return json::const_iterator(this, true); } json::const_iterator json::end() const noexcept { - return json::const_iterator(); + return json::const_iterator(this, false); } json::const_iterator json::cbegin() const noexcept { - return json::const_iterator(this); + return json::const_iterator(this, true); } json::const_iterator json::cend() const noexcept { - return json::const_iterator(); + return json::const_iterator(this, false); } -json::iterator::iterator(json* j) : object_(j) +json::iterator::iterator(json* j, bool begin) + : object_(j), invalid(not begin or j == nullptr) { if (object_ != nullptr) { if (object_->type_ == json::value_t::array) { - if (object_->empty()) + if (begin) { - object_ = nullptr; + vi_ = new array_t::iterator(object_->value_.array->begin()); + invalid = (*vi_ == object_->value_.array->end()); } else { - vi_ = new array_t::iterator(object_->value_.array->begin()); + vi_ = new array_t::iterator(object_->value_.array->end()); } } else if (object_->type_ == json::value_t::object) { - if (object_->empty()) + if (begin) { - object_ = nullptr; + oi_ = new object_t::iterator(object_->value_.object->begin()); + invalid = (*oi_ == object_->value_.object->end()); } else { - oi_ = new object_t::iterator(object_->value_.object->begin()); + oi_ = new object_t::iterator(object_->value_.object->end()); } } } } -json::iterator::iterator(const json::iterator& o) : object_(o.object_) +json::iterator::iterator(const json::iterator& o) + : object_(o.object_), invalid(o.invalid) { - if (object_ != nullptr) + if (o.vi_ != nullptr) { - if (object_->type_ == json::value_t::array) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - vi_ = new array_t::iterator(object_->value_.array->begin()); - } - } - else if (object_->type_ == json::value_t::object) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - oi_ = new object_t::iterator(object_->value_.object->begin()); - } - } + vi_ = new array_t::iterator(*(o.vi_)); + } + + if (o.oi_ != nullptr) + { + oi_ = new object_t::iterator(*(o.oi_)); } } @@ -1994,29 +1965,29 @@ json::iterator& json::iterator::operator=(json::iterator o) std::swap(object_, o.object_); std::swap(vi_, o.vi_); std::swap(oi_, o.oi_); + std::swap(invalid, o.invalid); return *this; } bool json::iterator::operator==(const json::iterator& o) const { - if (object_ != o.object_) + if (object_ != nullptr and o.object_ != nullptr) { - return false; - } - - if (object_ != nullptr) - { - if (object_->type_ == json::value_t::array) + if (object_->type_ == json::value_t::array and o.object_->type_ == json::value_t::array) { - return (vi_ == o.vi_); + return (*vi_ == *(o.vi_)); } - if (object_->type_ == json::value_t::object) + if (object_->type_ == json::value_t::object and o.object_->type_ == json::value_t::object) { - return (oi_ == o.oi_); + return (*oi_ == *(o.oi_)); + } + + if (invalid == o.invalid and object_ == o.object_) + { + return true; } } - - return true; + return false; } bool json::iterator::operator!=(const json::iterator& o) const @@ -2026,44 +1997,38 @@ bool json::iterator::operator!=(const json::iterator& o) const json::iterator& json::iterator::operator++() { - // iterator cannot be incremented - if (object_ == nullptr) + if (object_ != nullptr) { - return *this; + switch (object_->type_) + { + case (json::value_t::array): + { + std::advance(*vi_, 1); + invalid = (*vi_ == object_->value_.array->end()); + break; + } + case (json::value_t::object): + { + std::advance(*oi_, 1); + invalid = (*oi_ == object_->value_.object->end()); + break; + } + default: + { + invalid = true; + break; + } + } } - switch (object_->type_) - { - case (json::value_t::array): - { - if (++(*vi_) == object_->value_.array->end()) - { - object_ = nullptr; - } - break; - } - case (json::value_t::object): - { - if (++(*oi_) == object_->value_.object->end()) - { - object_ = nullptr; - } - break; - } - default: - { - object_ = nullptr; - } - } return *this; } json& json::iterator::operator*() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { - throw std::runtime_error("cannot get value"); + throw std::out_of_range("cannot get value"); } switch (object_->type_) @@ -2085,10 +2050,9 @@ json& json::iterator::operator*() const json* json::iterator::operator->() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { - throw std::runtime_error("cannot get value"); + throw std::out_of_range("cannot get value"); } switch (object_->type_) @@ -2110,20 +2074,17 @@ json* json::iterator::operator->() const std::string json::iterator::key() const { - if (object_ != nullptr and object_->type_ == json::value_t::object) + if (object_ == nullptr or invalid or object_->type_ != json::value_t::object) { - return (*oi_)->first; - } - else - { - throw std::out_of_range("cannot get key"); + throw std::out_of_range("cannot get value"); } + + return (*oi_)->first; } json& json::iterator::value() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { throw std::out_of_range("cannot get value"); } @@ -2146,90 +2107,61 @@ json& json::iterator::value() const } -json::const_iterator::const_iterator(const json* j) : object_(j) +json::const_iterator::const_iterator(const json* j, bool begin) + : object_(j), invalid(not begin or j == nullptr) { if (object_ != nullptr) { if (object_->type_ == json::value_t::array) { - if (object_->empty()) + if (begin) { - object_ = nullptr; + vi_ = new array_t::const_iterator(object_->value_.array->cbegin()); + invalid = (*vi_ == object_->value_.array->cend()); } else { - vi_ = new array_t::const_iterator(object_->value_.array->begin()); + vi_ = new array_t::const_iterator(object_->value_.array->cend()); } } else if (object_->type_ == json::value_t::object) { - if (object_->empty()) + if (begin) { - object_ = nullptr; + oi_ = new object_t::const_iterator(object_->value_.object->cbegin()); + invalid = (*oi_ == object_->value_.object->cend()); } else { - oi_ = new object_t::const_iterator(object_->value_.object->begin()); + oi_ = new object_t::const_iterator(object_->value_.object->cend()); } } } } -json::const_iterator::const_iterator(const json::const_iterator& o) : object_(o.object_) +json::const_iterator::const_iterator(const json::const_iterator& o) + : object_(o.object_), invalid(o.invalid) { - if (object_ != nullptr) + if (o.vi_ != nullptr) { - if (object_->type_ == json::value_t::array) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - vi_ = new array_t::const_iterator(object_->value_.array->begin()); - } - } - else if (object_->type_ == json::value_t::object) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - oi_ = new object_t::const_iterator(object_->value_.object->begin()); - } - } + vi_ = new array_t::const_iterator(*(o.vi_)); + } + if (o.oi_ != nullptr) + { + oi_ = new object_t::const_iterator(*(o.oi_)); } } -json::const_iterator::const_iterator(const json::iterator& o) : object_(o.object_) +json::const_iterator::const_iterator(const json::iterator& o) + : object_(o.object_), invalid(o.invalid) { - if (object_ != nullptr) + if (o.vi_ != nullptr) { - if (object_->type_ == json::value_t::array) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - vi_ = new array_t::const_iterator(object_->value_.array->begin()); - } - } - else if (object_->type_ == json::value_t::object) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - oi_ = new object_t::const_iterator(object_->value_.object->begin()); - } - } + vi_ = new array_t::const_iterator(*(o.vi_)); + } + if (o.oi_ != nullptr) + { + oi_ = new object_t::const_iterator(*(o.oi_)); } } @@ -2244,29 +2176,29 @@ json::const_iterator& json::const_iterator::operator=(json::const_iterator o) std::swap(object_, o.object_); std::swap(vi_, o.vi_); std::swap(oi_, o.oi_); + std::swap(invalid, o.invalid); return *this; } bool json::const_iterator::operator==(const json::const_iterator& o) const { - if (object_ != o.object_) + if (object_ != nullptr and o.object_ != nullptr) { - return false; - } - - if (object_ != nullptr) - { - if (object_->type_ == json::value_t::array) + if (object_->type_ == json::value_t::array and o.object_->type_ == json::value_t::array) { - return (vi_ == o.vi_); + return (*vi_ == *(o.vi_)); } - if (object_->type_ == json::value_t::object) + if (object_->type_ == json::value_t::object and o.object_->type_ == json::value_t::object) { - return (oi_ == o.oi_); + return (*oi_ == *(o.oi_)); + } + if (invalid == o.invalid and object_ == o.object_) + { + return true; } } - return true; + return false; } bool json::const_iterator::operator!=(const json::const_iterator& o) const @@ -2276,44 +2208,38 @@ bool json::const_iterator::operator!=(const json::const_iterator& o) const json::const_iterator& json::const_iterator::operator++() { - // iterator cannot be incremented - if (object_ == nullptr) + if (object_ != nullptr) { - return *this; + switch (object_->type_) + { + case (json::value_t::array): + { + std::advance(*vi_, 1); + invalid = (*vi_ == object_->value_.array->end()); + break; + } + case (json::value_t::object): + { + std::advance(*oi_, 1); + invalid = (*oi_ == object_->value_.object->end()); + break; + } + default: + { + invalid = true; + break; + } + } } - switch (object_->type_) - { - case (json::value_t::array): - { - if (++(*vi_) == object_->value_.array->end()) - { - object_ = nullptr; - } - break; - } - case (json::value_t::object): - { - if (++(*oi_) == object_->value_.object->end()) - { - object_ = nullptr; - } - break; - } - default: - { - object_ = nullptr; - } - } return *this; } const json& json::const_iterator::operator*() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { - throw std::runtime_error("cannot get value"); + throw std::out_of_range("cannot get value"); } switch (object_->type_) @@ -2335,10 +2261,9 @@ const json& json::const_iterator::operator*() const const json* json::const_iterator::operator->() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { - throw std::runtime_error("cannot get value"); + throw std::out_of_range("cannot get value"); } switch (object_->type_) @@ -2360,20 +2285,17 @@ const json* json::const_iterator::operator->() const std::string json::const_iterator::key() const { - if (object_ != nullptr and object_->type_ == json::value_t::object) + if (object_ == nullptr or invalid or object_->type_ != json::value_t::object) { - return (*oi_)->first; - } - else - { - throw std::out_of_range("cannot get key"); + throw std::out_of_range("cannot get value"); } + + return (*oi_)->first; } const json& json::const_iterator::value() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { throw std::out_of_range("cannot get value"); } diff --git a/src/json.cc b/src/json.cc index 69c0e0cf..c6e13f80 100644 --- a/src/json.cc +++ b/src/json.cc @@ -1245,50 +1245,28 @@ json::const_iterator json::find(const std::string& key) const json::iterator json::find(const char* key) { - if (type_ != value_t::object) + auto result = end(); + + if (type_ == value_t::object) { - return end(); - } - else - { - const object_t::iterator i = value_.object->find(key); - if (i != value_.object->end()) - { - json::iterator result(this); - delete result.oi_; - result.oi_ = nullptr; - result.oi_ = new object_t::iterator(i); - return result; - } - else - { - return end(); - } + result.oi_ = new object_t::iterator(value_.object->find(key)); + result.invalid = (*(result.oi_) == value_.object->end()); } + + return result; } json::const_iterator json::find(const char* key) const { - if (type_ != value_t::object) + auto result = cend(); + + if (type_ == value_t::object) { - return end(); - } - else - { - const object_t::const_iterator i = value_.object->find(key); - if (i != value_.object->end()) - { - json::const_iterator result(this); - delete result.oi_; - result.oi_ = nullptr; - result.oi_ = new object_t::const_iterator(i); - return result; - } - else - { - return end(); - } + result.oi_ = new object_t::const_iterator(value_.object->find(key)); + result.invalid = (*(result.oi_) == value_.object->cend()); } + + return result; } bool json::operator==(const json& o) const noexcept @@ -1372,90 +1350,78 @@ bool json::operator!=(const json& o) const noexcept json::iterator json::begin() noexcept { - return json::iterator(this); + return json::iterator(this, true); } json::iterator json::end() noexcept { - return json::iterator(); + return json::iterator(this, false); } json::const_iterator json::begin() const noexcept { - return json::const_iterator(this); + return json::const_iterator(this, true); } json::const_iterator json::end() const noexcept { - return json::const_iterator(); + return json::const_iterator(this, false); } json::const_iterator json::cbegin() const noexcept { - return json::const_iterator(this); + return json::const_iterator(this, true); } json::const_iterator json::cend() const noexcept { - return json::const_iterator(); + return json::const_iterator(this, false); } -json::iterator::iterator(json* j) : object_(j) +json::iterator::iterator(json* j, bool begin) + : object_(j), invalid(not begin or j == nullptr) { if (object_ != nullptr) { if (object_->type_ == json::value_t::array) { - if (object_->empty()) + if (begin) { - object_ = nullptr; + vi_ = new array_t::iterator(object_->value_.array->begin()); + invalid = (*vi_ == object_->value_.array->end()); } else { - vi_ = new array_t::iterator(object_->value_.array->begin()); + vi_ = new array_t::iterator(object_->value_.array->end()); } } else if (object_->type_ == json::value_t::object) { - if (object_->empty()) + if (begin) { - object_ = nullptr; + oi_ = new object_t::iterator(object_->value_.object->begin()); + invalid = (*oi_ == object_->value_.object->end()); } else { - oi_ = new object_t::iterator(object_->value_.object->begin()); + oi_ = new object_t::iterator(object_->value_.object->end()); } } } } -json::iterator::iterator(const json::iterator& o) : object_(o.object_) +json::iterator::iterator(const json::iterator& o) + : object_(o.object_), invalid(o.invalid) { - if (object_ != nullptr) + if (o.vi_ != nullptr) { - if (object_->type_ == json::value_t::array) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - vi_ = new array_t::iterator(object_->value_.array->begin()); - } - } - else if (object_->type_ == json::value_t::object) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - oi_ = new object_t::iterator(object_->value_.object->begin()); - } - } + vi_ = new array_t::iterator(*(o.vi_)); + } + + if (o.oi_ != nullptr) + { + oi_ = new object_t::iterator(*(o.oi_)); } } @@ -1470,29 +1436,29 @@ json::iterator& json::iterator::operator=(json::iterator o) std::swap(object_, o.object_); std::swap(vi_, o.vi_); std::swap(oi_, o.oi_); + std::swap(invalid, o.invalid); return *this; } bool json::iterator::operator==(const json::iterator& o) const { - if (object_ != o.object_) + if (object_ != nullptr and o.object_ != nullptr) { - return false; - } - - if (object_ != nullptr) - { - if (object_->type_ == json::value_t::array) + if (object_->type_ == json::value_t::array and o.object_->type_ == json::value_t::array) { - return (vi_ == o.vi_); + return (*vi_ == *(o.vi_)); } - if (object_->type_ == json::value_t::object) + if (object_->type_ == json::value_t::object and o.object_->type_ == json::value_t::object) { - return (oi_ == o.oi_); + return (*oi_ == *(o.oi_)); + } + + if (invalid == o.invalid and object_ == o.object_) + { + return true; } } - - return true; + return false; } bool json::iterator::operator!=(const json::iterator& o) const @@ -1502,44 +1468,38 @@ bool json::iterator::operator!=(const json::iterator& o) const json::iterator& json::iterator::operator++() { - // iterator cannot be incremented - if (object_ == nullptr) + if (object_ != nullptr) { - return *this; + switch (object_->type_) + { + case (json::value_t::array): + { + std::advance(*vi_, 1); + invalid = (*vi_ == object_->value_.array->end()); + break; + } + case (json::value_t::object): + { + std::advance(*oi_, 1); + invalid = (*oi_ == object_->value_.object->end()); + break; + } + default: + { + invalid = true; + break; + } + } } - switch (object_->type_) - { - case (json::value_t::array): - { - if (++(*vi_) == object_->value_.array->end()) - { - object_ = nullptr; - } - break; - } - case (json::value_t::object): - { - if (++(*oi_) == object_->value_.object->end()) - { - object_ = nullptr; - } - break; - } - default: - { - object_ = nullptr; - } - } return *this; } json& json::iterator::operator*() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { - throw std::runtime_error("cannot get value"); + throw std::out_of_range("cannot get value"); } switch (object_->type_) @@ -1561,10 +1521,9 @@ json& json::iterator::operator*() const json* json::iterator::operator->() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { - throw std::runtime_error("cannot get value"); + throw std::out_of_range("cannot get value"); } switch (object_->type_) @@ -1586,20 +1545,17 @@ json* json::iterator::operator->() const std::string json::iterator::key() const { - if (object_ != nullptr and object_->type_ == json::value_t::object) + if (object_ == nullptr or invalid or object_->type_ != json::value_t::object) { - return (*oi_)->first; - } - else - { - throw std::out_of_range("cannot get key"); + throw std::out_of_range("cannot get value"); } + + return (*oi_)->first; } json& json::iterator::value() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { throw std::out_of_range("cannot get value"); } @@ -1622,90 +1578,61 @@ json& json::iterator::value() const } -json::const_iterator::const_iterator(const json* j) : object_(j) +json::const_iterator::const_iterator(const json* j, bool begin) + : object_(j), invalid(not begin or j == nullptr) { if (object_ != nullptr) { if (object_->type_ == json::value_t::array) { - if (object_->empty()) + if (begin) { - object_ = nullptr; + vi_ = new array_t::const_iterator(object_->value_.array->cbegin()); + invalid = (*vi_ == object_->value_.array->cend()); } else { - vi_ = new array_t::const_iterator(object_->value_.array->begin()); + vi_ = new array_t::const_iterator(object_->value_.array->cend()); } } else if (object_->type_ == json::value_t::object) { - if (object_->empty()) + if (begin) { - object_ = nullptr; + oi_ = new object_t::const_iterator(object_->value_.object->cbegin()); + invalid = (*oi_ == object_->value_.object->cend()); } else { - oi_ = new object_t::const_iterator(object_->value_.object->begin()); + oi_ = new object_t::const_iterator(object_->value_.object->cend()); } } } } -json::const_iterator::const_iterator(const json::const_iterator& o) : object_(o.object_) +json::const_iterator::const_iterator(const json::const_iterator& o) + : object_(o.object_), invalid(o.invalid) { - if (object_ != nullptr) + if (o.vi_ != nullptr) { - if (object_->type_ == json::value_t::array) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - vi_ = new array_t::const_iterator(object_->value_.array->begin()); - } - } - else if (object_->type_ == json::value_t::object) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - oi_ = new object_t::const_iterator(object_->value_.object->begin()); - } - } + vi_ = new array_t::const_iterator(*(o.vi_)); + } + if (o.oi_ != nullptr) + { + oi_ = new object_t::const_iterator(*(o.oi_)); } } -json::const_iterator::const_iterator(const json::iterator& o) : object_(o.object_) +json::const_iterator::const_iterator(const json::iterator& o) + : object_(o.object_), invalid(o.invalid) { - if (object_ != nullptr) + if (o.vi_ != nullptr) { - if (object_->type_ == json::value_t::array) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - vi_ = new array_t::const_iterator(object_->value_.array->begin()); - } - } - else if (object_->type_ == json::value_t::object) - { - if (object_->empty()) - { - object_ = nullptr; - } - else - { - oi_ = new object_t::const_iterator(object_->value_.object->begin()); - } - } + vi_ = new array_t::const_iterator(*(o.vi_)); + } + if (o.oi_ != nullptr) + { + oi_ = new object_t::const_iterator(*(o.oi_)); } } @@ -1720,29 +1647,29 @@ json::const_iterator& json::const_iterator::operator=(json::const_iterator o) std::swap(object_, o.object_); std::swap(vi_, o.vi_); std::swap(oi_, o.oi_); + std::swap(invalid, o.invalid); return *this; } bool json::const_iterator::operator==(const json::const_iterator& o) const { - if (object_ != o.object_) + if (object_ != nullptr and o.object_ != nullptr) { - return false; - } - - if (object_ != nullptr) - { - if (object_->type_ == json::value_t::array) + if (object_->type_ == json::value_t::array and o.object_->type_ == json::value_t::array) { - return (vi_ == o.vi_); + return (*vi_ == *(o.vi_)); } - if (object_->type_ == json::value_t::object) + if (object_->type_ == json::value_t::object and o.object_->type_ == json::value_t::object) { - return (oi_ == o.oi_); + return (*oi_ == *(o.oi_)); + } + if (invalid == o.invalid and object_ == o.object_) + { + return true; } } - return true; + return false; } bool json::const_iterator::operator!=(const json::const_iterator& o) const @@ -1752,44 +1679,38 @@ bool json::const_iterator::operator!=(const json::const_iterator& o) const json::const_iterator& json::const_iterator::operator++() { - // iterator cannot be incremented - if (object_ == nullptr) + if (object_ != nullptr) { - return *this; + switch (object_->type_) + { + case (json::value_t::array): + { + std::advance(*vi_, 1); + invalid = (*vi_ == object_->value_.array->end()); + break; + } + case (json::value_t::object): + { + std::advance(*oi_, 1); + invalid = (*oi_ == object_->value_.object->end()); + break; + } + default: + { + invalid = true; + break; + } + } } - switch (object_->type_) - { - case (json::value_t::array): - { - if (++(*vi_) == object_->value_.array->end()) - { - object_ = nullptr; - } - break; - } - case (json::value_t::object): - { - if (++(*oi_) == object_->value_.object->end()) - { - object_ = nullptr; - } - break; - } - default: - { - object_ = nullptr; - } - } return *this; } const json& json::const_iterator::operator*() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { - throw std::runtime_error("cannot get value"); + throw std::out_of_range("cannot get value"); } switch (object_->type_) @@ -1811,10 +1732,9 @@ const json& json::const_iterator::operator*() const const json* json::const_iterator::operator->() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { - throw std::runtime_error("cannot get value"); + throw std::out_of_range("cannot get value"); } switch (object_->type_) @@ -1836,20 +1756,17 @@ const json* json::const_iterator::operator->() const std::string json::const_iterator::key() const { - if (object_ != nullptr and object_->type_ == json::value_t::object) + if (object_ == nullptr or invalid or object_->type_ != json::value_t::object) { - return (*oi_)->first; - } - else - { - throw std::out_of_range("cannot get key"); + throw std::out_of_range("cannot get value"); } + + return (*oi_)->first; } const json& json::const_iterator::value() const { - // dereferencing end() is an error - if (object_ == nullptr) + if (object_ == nullptr or invalid) { throw std::out_of_range("cannot get value"); } diff --git a/src/json.h b/src/json.h index fb9a1f7d..a0a8b25e 100644 --- a/src/json.h +++ b/src/json.h @@ -42,11 +42,12 @@ due to alignment. */ class json { - public: + private: // forward declaration to friend this class class iterator; class const_iterator; + public: // container types using value_type = json; using reference = json&; @@ -371,15 +372,15 @@ class json /// the payload value value_ {}; - public: + private: /// an iterator - class iterator : public std::iterator + class iterator : private std::iterator { friend class json; friend class json::const_iterator; public: iterator() = default; - iterator(json*); + iterator(json*, bool); iterator(const iterator&); ~iterator(); @@ -402,16 +403,18 @@ class json array_t::iterator* vi_ = nullptr; /// an iterator for JSON objects object_t::iterator* oi_ = nullptr; + /// whether iterator points to a valid object + bool invalid = true; }; /// a const iterator - class const_iterator : public std::iterator + class const_iterator : private std::iterator { friend class json; public: const_iterator() = default; - const_iterator(const json*); + const_iterator(const json*, bool); const_iterator(const const_iterator&); const_iterator(const json::iterator&); ~const_iterator(); @@ -435,6 +438,8 @@ class json array_t::const_iterator* vi_ = nullptr; /// an iterator for JSON objects object_t::const_iterator* oi_ = nullptr; + /// whether iterator reached past the end + bool invalid = true; }; private: diff --git a/test/json_unit.cc b/test/json_unit.cc index a5f346ba..6596d4c0 100644 --- a/test/json_unit.cc +++ b/test/json_unit.cc @@ -1371,35 +1371,35 @@ TEST_CASE("Iterators") CHECK(* j7.begin() == json("hello")); CHECK(* j7_const.begin() == json("hello")); - CHECK_THROWS_AS(* j1.end(), std::runtime_error); - CHECK_THROWS_AS(* j1.cend(), std::runtime_error); - CHECK_THROWS_AS(* j2.end(), std::runtime_error); - CHECK_THROWS_AS(* j2.cend(), std::runtime_error); - CHECK_THROWS_AS(* j3.end(), std::runtime_error); - CHECK_THROWS_AS(* j3.cend(), std::runtime_error); - CHECK_THROWS_AS(* j4.end(), std::runtime_error); - CHECK_THROWS_AS(* j4.cend(), std::runtime_error); - CHECK_THROWS_AS(* j5.end(), std::runtime_error); - CHECK_THROWS_AS(* j5.cend(), std::runtime_error); - CHECK_THROWS_AS(* j6.end(), std::runtime_error); - CHECK_THROWS_AS(* j6.cend(), std::runtime_error); - CHECK_THROWS_AS(* j7.end(), std::runtime_error); - CHECK_THROWS_AS(* j7.cend(), std::runtime_error); + CHECK_THROWS_AS(* j1.end(), std::out_of_range); + CHECK_THROWS_AS(* j1.cend(), std::out_of_range); + CHECK_THROWS_AS(* j2.end(), std::out_of_range); + CHECK_THROWS_AS(* j2.cend(), std::out_of_range); + CHECK_THROWS_AS(* j3.end(), std::out_of_range); + CHECK_THROWS_AS(* j3.cend(), std::out_of_range); + CHECK_THROWS_AS(* j4.end(), std::out_of_range); + CHECK_THROWS_AS(* j4.cend(), std::out_of_range); + CHECK_THROWS_AS(* j5.end(), std::out_of_range); + CHECK_THROWS_AS(* j5.cend(), std::out_of_range); + CHECK_THROWS_AS(* j6.end(), std::out_of_range); + CHECK_THROWS_AS(* j6.cend(), std::out_of_range); + CHECK_THROWS_AS(* j7.end(), std::out_of_range); + CHECK_THROWS_AS(* j7.cend(), std::out_of_range); - CHECK_THROWS_AS(* j1_const.end(), std::runtime_error); - CHECK_THROWS_AS(* j1_const.cend(), std::runtime_error); - CHECK_THROWS_AS(* j2_const.end(), std::runtime_error); - CHECK_THROWS_AS(* j2_const.cend(), std::runtime_error); - CHECK_THROWS_AS(* j3_const.end(), std::runtime_error); - CHECK_THROWS_AS(* j3_const.cend(), std::runtime_error); - CHECK_THROWS_AS(* j4_const.end(), std::runtime_error); - CHECK_THROWS_AS(* j4_const.cend(), std::runtime_error); - CHECK_THROWS_AS(* j5_const.end(), std::runtime_error); - CHECK_THROWS_AS(* j5_const.cend(), std::runtime_error); - CHECK_THROWS_AS(* j6_const.end(), std::runtime_error); - CHECK_THROWS_AS(* j6_const.cend(), std::runtime_error); - CHECK_THROWS_AS(* j7_const.end(), std::runtime_error); - CHECK_THROWS_AS(* j7_const.cend(), std::runtime_error); + CHECK_THROWS_AS(* j1_const.end(), std::out_of_range); + CHECK_THROWS_AS(* j1_const.cend(), std::out_of_range); + CHECK_THROWS_AS(* j2_const.end(), std::out_of_range); + CHECK_THROWS_AS(* j2_const.cend(), std::out_of_range); + CHECK_THROWS_AS(* j3_const.end(), std::out_of_range); + CHECK_THROWS_AS(* j3_const.cend(), std::out_of_range); + CHECK_THROWS_AS(* j4_const.end(), std::out_of_range); + CHECK_THROWS_AS(* j4_const.cend(), std::out_of_range); + CHECK_THROWS_AS(* j5_const.end(), std::out_of_range); + CHECK_THROWS_AS(* j5_const.cend(), std::out_of_range); + CHECK_THROWS_AS(* j6_const.end(), std::out_of_range); + CHECK_THROWS_AS(* j6_const.cend(), std::out_of_range); + CHECK_THROWS_AS(* j7_const.end(), std::out_of_range); + CHECK_THROWS_AS(* j7_const.cend(), std::out_of_range); // operator -> CHECK(j1.begin()->type() == json::value_t::number); @@ -1432,35 +1432,35 @@ TEST_CASE("Iterators") CHECK(j7_const.begin()->type() == json::value_t::string); CHECK(j7_const.cbegin()->type() == json::value_t::string); - CHECK_THROWS_AS(j1.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j1.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j2.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j2.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j3.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j3.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j4.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j4.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j5.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j5.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j6.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j6.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j7.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j7.cend()->type(), std::runtime_error); + CHECK_THROWS_AS(j1.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j1.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j2.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j2.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j3.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j3.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j4.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j4.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j5.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j5.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j6.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j6.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j7.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j7.cend()->type(), std::out_of_range); - CHECK_THROWS_AS(j1_const.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j1_const.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j2_const.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j2_const.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j3_const.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j3_const.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j4_const.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j4_const.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j5_const.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j5_const.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j6_const.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j6_const.cend()->type(), std::runtime_error); - CHECK_THROWS_AS(j7_const.end()->type(), std::runtime_error); - CHECK_THROWS_AS(j7_const.cend()->type(), std::runtime_error); + CHECK_THROWS_AS(j1_const.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j1_const.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j2_const.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j2_const.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j3_const.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j3_const.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j4_const.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j4_const.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j5_const.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j5_const.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j6_const.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j6_const.cend()->type(), std::out_of_range); + CHECK_THROWS_AS(j7_const.end()->type(), std::out_of_range); + CHECK_THROWS_AS(j7_const.cend()->type(), std::out_of_range); // value CHECK(j1.begin().value().type() == json::value_t::number);