Merge branch 'develop' into feature/userdefined_exceptions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c0488b3..72802b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) # define the project -project(nlohmann_json VERSION 2.0.0 LANGUAGES CXX) +project(nlohmann_json VERSION 2.0.2 LANGUAGES CXX) enable_testing()
diff --git a/ChangeLog.md b/ChangeLog.md index 901e37c..8062f3b 100644 --- a/ChangeLog.md +++ b/ChangeLog.md
@@ -1,9 +1,8 @@ # Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased](https://github.com/nlohmann/json/tree/HEAD) - -[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...HEAD) +## [v2.0.2](https://github.com/nlohmann/json/releases/tag/v2.0.2) (2016-07-30) +[Full Changelog](https://github.com/nlohmann/json/compare/v2.0.1...v2.0.2) - value\(\) does not work with \_json\_pointer types [\#283](https://github.com/nlohmann/json/issues/283) - Easy serialization of classes [\#280](https://github.com/nlohmann/json/issues/280)
diff --git a/Makefile b/Makefile index 98f0768..15d00f6 100644 --- a/Makefile +++ b/Makefile
@@ -95,7 +95,9 @@ # changelog ########################################################################## +NEXT_VERSION ?= "unreleased" + ChangeLog.md: - github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s + github_changelog_generator -o ChangeLog.md --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION) gsed -i 's|https://github.com/nlohmann/json/releases/tag/HEAD|https://github.com/nlohmann/json/tree/HEAD|' ChangeLog.md gsed -i '2i All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).' ChangeLog.md
diff --git a/README.md b/README.md index e16d881..6b6b6f9 100644 --- a/README.md +++ b/README.md
@@ -4,7 +4,7 @@ [](https://ci.appveyor.com/project/nlohmann/json) [](https://coveralls.io/r/nlohmann/json) [](https://scan.coverity.com/projects/nlohmann-json) -[](http://melpon.org/wandbox/permlink/wuiuqYiYqRTdI3rG) +[](http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN) [](http://nlohmann.github.io/json) [](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT) [](https://github.com/nlohmann/json/releases)
diff --git a/doc/Doxyfile b/doc/Doxyfile index e74a8a8..e0055d6 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile
@@ -5,7 +5,7 @@ #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "JSON for Modern C++" -PROJECT_NUMBER = 2.0.0 +PROJECT_NUMBER = 2.0.2 PROJECT_BRIEF = PROJECT_LOGO = OUTPUT_DIRECTORY = .
diff --git a/doc/examples/README.link b/doc/examples/README.link index ed559e1..0e87a79 100644 --- a/doc/examples/README.link +++ b/doc/examples/README.link
@@ -1 +1 @@ -<a target="_blank" href="http://melpon.org/wandbox/permlink/k5KRwVJ42VggxxzX"><b>online</b></a> \ No newline at end of file +<a target="_blank" href="http://melpon.org/wandbox/permlink/p5o4znPnGHJpDVqN"><b>online</b></a> \ No newline at end of file
diff --git a/doc/index.md b/doc/index.md index 5373d4f..cf32a7a 100644 --- a/doc/index.md +++ b/doc/index.md
@@ -268,4 +268,4 @@ @author [Niels Lohmann](http://nlohmann.me) @see https://github.com/nlohmann/json to download the source code -@version 2.0.0 +@version 2.0.2
diff --git a/doc/json.gif b/doc/json.gif index 46f005d..62a1a2e 100644 --- a/doc/json.gif +++ b/doc/json.gif Binary files differ
diff --git a/src/json.hpp b/src/json.hpp index 4de4ff4..a95717c 100644 --- a/src/json.hpp +++ b/src/json.hpp
@@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.0.0 +| | |__ | | | | | | version 2.0.2 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License <http://opensource.org/licenses/MIT>. @@ -190,6 +190,13 @@ JSON values can be used like STL containers and provide reverse iterator access. +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + @internal @note ObjectType trick from http://stackoverflow.com/a/9860911 @endinternal @@ -985,6 +992,21 @@ } }; + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const + { + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); + } public: ////////////////////////// @@ -1124,7 +1146,9 @@ */ basic_json(const value_t value_type) : m_type(value_type), m_value(value_type) - {} + { + assert_invariant(); + } /*! @brief create a null object (implicitly) @@ -1132,6 +1156,9 @@ Create a `null` JSON value. This is the implicit version of the `null` value constructor as it takes no parameters. + @note The class invariant is satisfied, because it poses no requirements + for null values. + @complexity Constant. @exceptionsafety No-throw guarantee: this constructor never throws @@ -1176,7 +1203,9 @@ */ basic_json(std::nullptr_t) noexcept : basic_json(value_t::null) - {} + { + assert_invariant(); + } /*! @brief create an object (explicit) @@ -1199,7 +1228,9 @@ */ basic_json(const object_t& val) : m_type(value_t::object), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an object (implicit) @@ -1238,6 +1269,7 @@ using std::begin; using std::end; m_value.object = create<object_t>(begin(val), end(val)); + assert_invariant(); } /*! @@ -1261,7 +1293,9 @@ */ basic_json(const array_t& val) : m_type(value_t::array), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an array (implicit) @@ -1305,6 +1339,7 @@ using std::begin; using std::end; m_value.array = create<array_t>(begin(val), end(val)); + assert_invariant(); } /*! @@ -1330,7 +1365,9 @@ */ basic_json(const string_t& val) : m_type(value_t::string), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create a string (explicit) @@ -1354,7 +1391,9 @@ */ basic_json(const typename string_t::value_type* val) : basic_json(string_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a string (implicit) @@ -1385,7 +1424,9 @@ = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a boolean (explicit) @@ -1403,7 +1444,9 @@ */ basic_json(boolean_t val) noexcept : m_type(value_t::boolean), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an integer number (explicit) @@ -1436,7 +1479,9 @@ = 0> basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an integer number from an enum type (explicit) @@ -1466,7 +1511,9 @@ basic_json(const int val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create an integer number (implicit) @@ -1503,7 +1550,9 @@ basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create an unsigned integer number (explicit) @@ -1530,7 +1579,9 @@ = 0> basic_json(const number_unsigned_t val) noexcept : m_type(value_t::number_unsigned), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an unsigned number (implicit) @@ -1562,7 +1613,9 @@ basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast<number_unsigned_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create a floating-point number (explicit) @@ -1597,6 +1650,8 @@ m_type = value_t::null; m_value = json_value(); } + + assert_invariant(); } /*! @@ -1637,7 +1692,9 @@ > basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a container (array or object) from an initializer list @@ -1742,8 +1799,6 @@ m_type = value_t::object; m_value = value_t::object; - assert(m_value.object != nullptr); - std::for_each(init.begin(), init.end(), [this](const basic_json & element) { m_value.object->emplace(*(element[0].m_value.string), element[1]); @@ -1755,6 +1810,8 @@ m_type = value_t::array; m_value.array = create<array_t>(init); } + + assert_invariant(); } /*! @@ -1859,6 +1916,7 @@ : m_type(value_t::array) { m_value.array = create<array_t>(cnt, val); + assert_invariant(); } /*! @@ -1879,12 +1937,14 @@ @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) + @pre Iterators @a first and @a last must be initialized. + @throw invalid_iterator (101) if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` - @throw invalid_iterator (104) if iterators are for a primitive type (number, - boolean, or string) where an out of range error can be detected easily; - example: `"iterators out of range"` + @throw invalid_iterator (104) if iterators are for a primitive type + (number, boolean, or string) where an out of range error can be detected + easily; example: `"iterators out of range"` @throw std::bad_alloc if allocation for object, array, or string fails @throw invalid_iterator (106) if called with a null value; example: `"cannot construct with iterators from null"` @@ -1902,14 +1962,20 @@ std::is_same<InputIT, typename basic_json_t::const_iterator>::value , int>::type = 0> - basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type) + basic_json(InputIT first, InputIT last) { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + // make sure iterator fits the current value if (first.m_object != last.m_object) { throw invalid_iterator(101, "iterators are not compatible"); } + // copy type from first iterator + m_type = first.m_object->m_type; + // check if iterator range is complete for primitive values switch (m_type) { @@ -1936,35 +2002,30 @@ { case value_t::number_integer: { - assert(first.m_object != nullptr); m_value.number_integer = first.m_object->m_value.number_integer; break; } case value_t::number_unsigned: { - assert(first.m_object != nullptr); m_value.number_unsigned = first.m_object->m_value.number_unsigned; break; } case value_t::number_float: { - assert(first.m_object != nullptr); m_value.number_float = first.m_object->m_value.number_float; break; } case value_t::boolean: { - assert(first.m_object != nullptr); m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { - assert(first.m_object != nullptr); m_value = *first.m_object->m_value.string; break; } @@ -1988,6 +2049,8 @@ first.m_object->type_name()); } } + + assert_invariant(); } /*! @@ -2013,6 +2076,7 @@ explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) { *this = parser(i, cb).parse(); + assert_invariant(); } /////////////////////////////////////// @@ -2044,25 +2108,25 @@ basic_json(const basic_json& other) : m_type(other.m_type) { + // check of passed value is valid + other.assert_invariant(); + switch (m_type) { case value_t::object: { - assert(other.m_value.object != nullptr); m_value = *other.m_value.object; break; } case value_t::array: { - assert(other.m_value.array != nullptr); m_value = *other.m_value.array; break; } case value_t::string: { - assert(other.m_value.string != nullptr); m_value = *other.m_value.string; break; } @@ -2096,6 +2160,8 @@ break; } } + + assert_invariant(); } /*! @@ -2120,9 +2186,14 @@ : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { + // check that passed value is valid + other.assert_invariant(); + // invalidate payload other.m_type = value_t::null; other.m_value = {}; + + assert_invariant(); } /*! @@ -2155,9 +2226,14 @@ std::is_nothrow_move_assignable<json_value>::value ) { + // check that passed value is valid + other.assert_invariant(); + using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); + + assert_invariant(); return *this; } @@ -2178,6 +2254,8 @@ */ ~basic_json() { + assert_invariant(); + switch (m_type) { case value_t::object: @@ -2670,7 +2748,6 @@ { if (is_object()) { - assert(m_value.object != nullptr); return T(m_value.object->begin(), m_value.object->end()); } else @@ -2684,7 +2761,6 @@ { if (is_object()) { - assert(m_value.object != nullptr); return *(m_value.object); } else @@ -2707,7 +2783,6 @@ if (is_array()) { T to_vector; - assert(m_value.array != nullptr); std::transform(m_value.array->begin(), m_value.array->end(), std::inserter(to_vector, to_vector.end()), [](basic_json i) { @@ -2732,7 +2807,6 @@ if (is_array()) { std::vector<T> to_vector; - assert(m_value.array != nullptr); to_vector.reserve(m_value.array->size()); std::transform(m_value.array->begin(), m_value.array->end(), std::inserter(to_vector, to_vector.end()), [](basic_json i) @@ -2757,7 +2831,6 @@ { if (is_array()) { - assert(m_value.array != nullptr); return T(m_value.array->begin(), m_value.array->end()); } else @@ -2771,7 +2844,6 @@ { if (is_array()) { - assert(m_value.array != nullptr); return *(m_value.array); } else @@ -2789,7 +2861,6 @@ { if (is_string()) { - assert(m_value.string != nullptr); return *m_value.string; } else @@ -3266,7 +3337,6 @@ { try { - assert(m_value.array != nullptr); return m_value.array->at(idx); } catch (std::out_of_range&) @@ -3310,7 +3380,6 @@ { try { - assert(m_value.array != nullptr); return m_value.array->at(idx); } catch (std::out_of_range&) @@ -3358,7 +3427,6 @@ { try { - assert(m_value.object != nullptr); return m_value.object->at(key); } catch (std::out_of_range&) @@ -3406,7 +3474,6 @@ { try { - assert(m_value.object != nullptr); return m_value.object->at(key); } catch (std::out_of_range&) @@ -3453,13 +3520,13 @@ { m_type = value_t::array; m_value.array = create<array_t>(); + assert_invariant(); } // operator[] only works for arrays if (is_array()) { // fill up array with null values if given idx is outside range - assert(m_value.array != nullptr); if (idx >= m_value.array->size()) { m_value.array->insert(m_value.array->end(), @@ -3499,7 +3566,6 @@ // const operator[] only works for arrays if (is_array()) { - assert(m_value.array != nullptr); return m_value.array->operator[](idx); } else @@ -3542,12 +3608,12 @@ { m_type = value_t::object; m_value.object = create<object_t>(); + assert_invariant(); } // operator[] only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->operator[](key); } else @@ -3588,7 +3654,6 @@ // const operator[] only works for objects if (is_object()) { - assert(m_value.object != nullptr); assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -3701,12 +3766,12 @@ { m_type = value_t::object; m_value = value_t::object; + assert_invariant(); } // at only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->operator[](key); } else @@ -3748,7 +3813,6 @@ // at only works for objects if (is_object()) { - assert(m_value.object != nullptr); assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -4076,24 +4140,25 @@ if (is_string()) { - delete m_value.string; + AllocatorType<string_t> alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; + assert_invariant(); break; } case value_t::object: { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); break; } case value_t::array: { - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); break; } @@ -4184,17 +4249,19 @@ if (is_string()) { - delete m_value.string; + AllocatorType<string_t> alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; + assert_invariant(); break; } case value_t::object: { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); break; @@ -4202,7 +4269,6 @@ case value_t::array: { - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); break; @@ -4251,7 +4317,6 @@ // this erase only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->erase(key); } else @@ -4294,7 +4359,6 @@ throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); } - assert(m_value.array != nullptr); m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx)); } else @@ -4337,7 +4401,6 @@ if (is_object()) { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } @@ -4354,7 +4417,6 @@ if (is_object()) { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } @@ -4382,7 +4444,6 @@ size_type count(typename object_t::key_type key) const { // return 0 for all nonobject types - assert(not is_object() or m_value.object != nullptr); return is_object() ? m_value.object->count(key) : 0; } @@ -4758,14 +4819,12 @@ case value_t::array: { // delegate call to array_t::empty() - assert(m_value.array != nullptr); return m_value.array->empty(); } case value_t::object: { // delegate call to object_t::empty() - assert(m_value.object != nullptr); return m_value.object->empty(); } @@ -4828,14 +4887,12 @@ case value_t::array: { // delegate call to array_t::size() - assert(m_value.array != nullptr); return m_value.array->size(); } case value_t::object: { // delegate call to object_t::size() - assert(m_value.object != nullptr); return m_value.object->size(); } @@ -4890,14 +4947,12 @@ case value_t::array: { // delegate call to array_t::max_size() - assert(m_value.array != nullptr); return m_value.array->max_size(); } case value_t::object: { // delegate call to object_t::max_size() - assert(m_value.object != nullptr); return m_value.object->max_size(); } @@ -4974,21 +5029,18 @@ case value_t::string: { - assert(m_value.string != nullptr); m_value.string->clear(); break; } case value_t::array: { - assert(m_value.array != nullptr); m_value.array->clear(); break; } case value_t::object: { - assert(m_value.object != nullptr); m_value.object->clear(); break; } @@ -5033,10 +5085,10 @@ { m_type = value_t::array; m_value = value_t::array; + assert_invariant(); } // add element to array (move semantics) - assert(m_value.array != nullptr); m_value.array->push_back(std::move(val)); // invalidate object val.m_type = value_t::null; @@ -5069,10 +5121,10 @@ { m_type = value_t::array; m_value = value_t::array; + assert_invariant(); } // add element to array - assert(m_value.array != nullptr); m_value.array->push_back(val); } @@ -5119,10 +5171,10 @@ { m_type = value_t::object; m_value = value_t::object; + assert_invariant(); } // add element to array - assert(m_value.object != nullptr); m_value.object->insert(val); } @@ -5219,7 +5271,6 @@ // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } @@ -5275,7 +5326,6 @@ // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } @@ -5343,7 +5393,6 @@ // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert( pos.m_it.array_iterator, first.m_it.array_iterator, @@ -5391,7 +5440,6 @@ // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); return result; } @@ -5422,6 +5470,7 @@ { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); + assert_invariant(); } /*! @@ -5449,7 +5498,6 @@ // swap only works for arrays if (is_array()) { - assert(m_value.array != nullptr); std::swap(*(m_value.array), other); } else @@ -5483,7 +5531,6 @@ // swap only works for objects if (is_object()) { - assert(m_value.object != nullptr); std::swap(*(m_value.object), other); } else @@ -5517,7 +5564,6 @@ // swap only works for strings if (is_string()) { - assert(m_value.string != nullptr); std::swap(*(m_value.string), other); } else @@ -5604,14 +5650,10 @@ { case value_t::array: { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); return *lhs.m_value.array == *rhs.m_value.array; } case value_t::object: { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); return *lhs.m_value.object == *rhs.m_value.object; } case value_t::null: @@ -5620,8 +5662,6 @@ } case value_t::string: { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); return *lhs.m_value.string == *rhs.m_value.string; } case value_t::boolean: @@ -5794,14 +5834,10 @@ { case value_t::array: { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); return *lhs.m_value.array < *rhs.m_value.array; } case value_t::object: { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); return *lhs.m_value.object < *rhs.m_value.object; } case value_t::null: @@ -5810,8 +5846,6 @@ } case value_t::string: { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); return *lhs.m_value.string < *rhs.m_value.string; } case value_t::boolean: @@ -6361,8 +6395,6 @@ { case value_t::object: { - assert(m_value.object != nullptr); - if (m_value.object->empty()) { o << "{}"; @@ -6403,8 +6435,6 @@ case value_t::array: { - assert(m_value.array != nullptr); - if (m_value.array->empty()) { o << "[]"; @@ -6443,7 +6473,6 @@ case value_t::string: { - assert(m_value.string != nullptr); o << string_t("\"") << escape_string(*m_value.string) << "\""; return; } @@ -6692,6 +6721,12 @@ This class implements a const iterator for the @ref basic_json class. From this class, the @ref iterator class is derived. + @note An iterator is called *initialized* when a pointer to a JSON value + has been set (e.g., by a constructor or a copy assignment). If the + iterator is default-constructed, it is *uninitialized* and most + methods are undefined. The library uses assertions to detect calls + on uninitialized iterators. + @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any @@ -6719,7 +6754,12 @@ /// default constructor const_iterator() = default; - /// constructor for a given JSON instance + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ explicit const_iterator(pointer object) noexcept : m_object(object) { @@ -6747,40 +6787,53 @@ } } - /// copy constructor given a nonconst iterator + /*! + @brief copy constructor given a non-const iterator + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ explicit const_iterator(const iterator& other) noexcept : m_object(other.m_object) { - assert(m_object != nullptr); - - switch (m_object->m_type) + if (m_object != nullptr) { - case basic_json::value_t::object: + switch (m_object->m_type) { - m_it.object_iterator = other.m_it.object_iterator; - break; - } + case basic_json::value_t::object: + { + m_it.object_iterator = other.m_it.object_iterator; + break; + } - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } + case basic_json::value_t::array: + { + m_it.array_iterator = other.m_it.array_iterator; + break; + } - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; + default: + { + m_it.primitive_iterator = other.m_it.primitive_iterator; + break; + } } } } - /// copy constructor + /*! + @brief copy constructor + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ const_iterator(const const_iterator& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} - /// copy assignment + /*! + @brief copy assignment + @param[in,out] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ const_iterator& operator=(const_iterator other) noexcept( std::is_nothrow_move_constructible<pointer>::value and std::is_nothrow_move_assignable<pointer>::value and @@ -6794,7 +6847,10 @@ } private: - /// set the iterator to the first value + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ void set_begin() noexcept { assert(m_object != nullptr); @@ -6803,14 +6859,12 @@ { case basic_json::value_t::object: { - assert(m_object->m_value.object != nullptr); m_it.object_iterator = m_object->m_value.object->begin(); break; } case basic_json::value_t::array: { - assert(m_object->m_value.array != nullptr); m_it.array_iterator = m_object->m_value.array->begin(); break; } @@ -6830,7 +6884,10 @@ } } - /// set the iterator past the last value + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ void set_end() noexcept { assert(m_object != nullptr); @@ -6839,14 +6896,12 @@ { case basic_json::value_t::object: { - assert(m_object->m_value.object != nullptr); m_it.object_iterator = m_object->m_value.object->end(); break; } case basic_json::value_t::array: { - assert(m_object->m_value.array != nullptr); m_it.array_iterator = m_object->m_value.array->end(); break; } @@ -6860,7 +6915,10 @@ } public: - /// return a reference to the value pointed to by the iterator + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference operator*() const { assert(m_object != nullptr); @@ -6869,14 +6927,12 @@ { case basic_json::value_t::object: { - assert(m_object->m_value.object); assert(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case basic_json::value_t::array: { - assert(m_object->m_value.array); assert(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } @@ -6900,7 +6956,10 @@ } } - /// dereference the iterator + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ pointer operator->() const { assert(m_object != nullptr); @@ -6909,14 +6968,12 @@ { case basic_json::value_t::object: { - assert(m_object->m_value.object); assert(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case basic_json::value_t::array: { - assert(m_object->m_value.array); assert(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } @@ -6935,7 +6992,10 @@ } } - /// post-increment (it++) + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator++(int) { auto result = *this; @@ -6943,7 +7003,10 @@ return result; } - /// pre-increment (++it) + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator++() { assert(m_object != nullptr); @@ -6972,7 +7035,10 @@ return *this; } - /// post-decrement (it--) + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator--(int) { auto result = *this; @@ -6980,7 +7046,10 @@ return result; } - /// pre-decrement (--it) + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator--() { assert(m_object != nullptr); @@ -7009,7 +7078,10 @@ return *this; } - /// comparison: equal + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator==(const const_iterator& other) const { // if objects are not the same, the comparison is undefined @@ -7039,13 +7111,19 @@ } } - /// comparison: not equal + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator!=(const const_iterator& other) const { return not operator==(other); } - /// comparison: smaller + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator<(const const_iterator& other) const { // if objects are not the same, the comparison is undefined @@ -7075,25 +7153,37 @@ } } - /// comparison: less than or equal + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator<=(const const_iterator& other) const { return not other.operator < (*this); } - /// comparison: greater than + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator>(const const_iterator& other) const { return not operator<=(other); } - /// comparison: greater than or equal + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator>=(const const_iterator& other) const { return not operator<(other); } - /// add to iterator + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator+=(difference_type i) { assert(m_object != nullptr); @@ -7121,13 +7211,19 @@ return *this; } - /// subtract from iterator + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator-=(difference_type i) { return operator+=(-i); } - /// add to iterator + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator+(difference_type i) { auto result = *this; @@ -7135,7 +7231,10 @@ return result; } - /// subtract from iterator + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator-(difference_type i) { auto result = *this; @@ -7143,7 +7242,10 @@ return result; } - /// return difference + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ difference_type operator-(const const_iterator& other) const { assert(m_object != nullptr); @@ -7167,7 +7269,10 @@ } } - /// access to successor + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference operator[](difference_type n) const { assert(m_object != nullptr); @@ -7203,7 +7308,10 @@ } } - /// return the key of an object iterator + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ typename object_t::key_type key() const { assert(m_object != nullptr); @@ -7218,7 +7326,10 @@ } } - /// return the value of an iterator + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference value() const { return operator*(); @@ -8931,6 +9042,7 @@ basic_json parse() { basic_json result = parse_internal(true); + result.assert_invariant(); expect(lexer::token_type::end_of_input); @@ -8953,7 +9065,7 @@ { // explicitly set result to object to cope with {} result.m_type = value_t::object; - result.m_value = json_value(value_t::object); + result.m_value = value_t::object; } // read next token @@ -9031,7 +9143,7 @@ { // explicitly set result to object to cope with [] result.m_type = value_t::array; - result.m_value = json_value(value_t::array); + result.m_value = value_t::array; } // read next token @@ -9730,7 +9842,6 @@ basic_json result; // iterate the JSON object values - assert(value.m_value.object != nullptr); for (const auto& element : *value.m_value.object) { if (not element.second.is_primitive())
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 7a41102..e8949d6 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c
@@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.0.0 +| | |__ | | | | | | version 2.0.2 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License <http://opensource.org/licenses/MIT>. @@ -190,6 +190,13 @@ JSON values can be used like STL containers and provide reverse iterator access. +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + @internal @note ObjectType trick from http://stackoverflow.com/a/9860911 @endinternal @@ -985,6 +992,21 @@ } }; + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + */ + void assert_invariant() const + { + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); + } public: ////////////////////////// @@ -1124,7 +1146,9 @@ */ basic_json(const value_t value_type) : m_type(value_type), m_value(value_type) - {} + { + assert_invariant(); + } /*! @brief create a null object (implicitly) @@ -1132,6 +1156,9 @@ Create a `null` JSON value. This is the implicit version of the `null` value constructor as it takes no parameters. + @note The class invariant is satisfied, because it poses no requirements + for null values. + @complexity Constant. @exceptionsafety No-throw guarantee: this constructor never throws @@ -1176,7 +1203,9 @@ */ basic_json(std::nullptr_t) noexcept : basic_json(value_t::null) - {} + { + assert_invariant(); + } /*! @brief create an object (explicit) @@ -1199,7 +1228,9 @@ */ basic_json(const object_t& val) : m_type(value_t::object), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an object (implicit) @@ -1238,6 +1269,7 @@ using std::begin; using std::end; m_value.object = create<object_t>(begin(val), end(val)); + assert_invariant(); } /*! @@ -1261,7 +1293,9 @@ */ basic_json(const array_t& val) : m_type(value_t::array), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an array (implicit) @@ -1305,6 +1339,7 @@ using std::begin; using std::end; m_value.array = create<array_t>(begin(val), end(val)); + assert_invariant(); } /*! @@ -1330,7 +1365,9 @@ */ basic_json(const string_t& val) : m_type(value_t::string), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create a string (explicit) @@ -1354,7 +1391,9 @@ */ basic_json(const typename string_t::value_type* val) : basic_json(string_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a string (implicit) @@ -1385,7 +1424,9 @@ = 0> basic_json(const CompatibleStringType& val) : basic_json(string_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a boolean (explicit) @@ -1403,7 +1444,9 @@ */ basic_json(boolean_t val) noexcept : m_type(value_t::boolean), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an integer number (explicit) @@ -1436,7 +1479,9 @@ = 0> basic_json(const number_integer_t val) noexcept : m_type(value_t::number_integer), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an integer number from an enum type (explicit) @@ -1466,7 +1511,9 @@ basic_json(const int val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create an integer number (implicit) @@ -1503,7 +1550,9 @@ basic_json(const CompatibleNumberIntegerType val) noexcept : m_type(value_t::number_integer), m_value(static_cast<number_integer_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create an unsigned integer number (explicit) @@ -1530,7 +1579,9 @@ = 0> basic_json(const number_unsigned_t val) noexcept : m_type(value_t::number_unsigned), m_value(val) - {} + { + assert_invariant(); + } /*! @brief create an unsigned number (implicit) @@ -1562,7 +1613,9 @@ basic_json(const CompatibleNumberUnsignedType val) noexcept : m_type(value_t::number_unsigned), m_value(static_cast<number_unsigned_t>(val)) - {} + { + assert_invariant(); + } /*! @brief create a floating-point number (explicit) @@ -1597,6 +1650,8 @@ m_type = value_t::null; m_value = json_value(); } + + assert_invariant(); } /*! @@ -1637,7 +1692,9 @@ > basic_json(const CompatibleNumberFloatType val) noexcept : basic_json(number_float_t(val)) - {} + { + assert_invariant(); + } /*! @brief create a container (array or object) from an initializer list @@ -1742,8 +1799,6 @@ m_type = value_t::object; m_value = value_t::object; - assert(m_value.object != nullptr); - std::for_each(init.begin(), init.end(), [this](const basic_json & element) { m_value.object->emplace(*(element[0].m_value.string), element[1]); @@ -1755,6 +1810,8 @@ m_type = value_t::array; m_value.array = create<array_t>(init); } + + assert_invariant(); } /*! @@ -1859,6 +1916,7 @@ : m_type(value_t::array) { m_value.array = create<array_t>(cnt, val); + assert_invariant(); } /*! @@ -1879,12 +1937,14 @@ @param[in] first begin of the range to copy from (included) @param[in] last end of the range to copy from (excluded) + @pre Iterators @a first and @a last must be initialized. + @throw invalid_iterator (101) if iterators are not compatible; that is, do not belong to the same JSON value; example: `"iterators are not compatible"` - @throw invalid_iterator (104) if iterators are for a primitive type (number, - boolean, or string) where an out of range error can be detected easily; - example: `"iterators out of range"` + @throw invalid_iterator (104) if iterators are for a primitive type + (number, boolean, or string) where an out of range error can be detected + easily; example: `"iterators out of range"` @throw std::bad_alloc if allocation for object, array, or string fails @throw invalid_iterator (106) if called with a null value; example: `"cannot construct with iterators from null"` @@ -1902,14 +1962,20 @@ std::is_same<InputIT, typename basic_json_t::const_iterator>::value , int>::type = 0> - basic_json(InputIT first, InputIT last) : m_type(first.m_object->m_type) + basic_json(InputIT first, InputIT last) { + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); + // make sure iterator fits the current value if (first.m_object != last.m_object) { throw invalid_iterator(101, "iterators are not compatible"); } + // copy type from first iterator + m_type = first.m_object->m_type; + // check if iterator range is complete for primitive values switch (m_type) { @@ -1936,35 +2002,30 @@ { case value_t::number_integer: { - assert(first.m_object != nullptr); m_value.number_integer = first.m_object->m_value.number_integer; break; } case value_t::number_unsigned: { - assert(first.m_object != nullptr); m_value.number_unsigned = first.m_object->m_value.number_unsigned; break; } case value_t::number_float: { - assert(first.m_object != nullptr); m_value.number_float = first.m_object->m_value.number_float; break; } case value_t::boolean: { - assert(first.m_object != nullptr); m_value.boolean = first.m_object->m_value.boolean; break; } case value_t::string: { - assert(first.m_object != nullptr); m_value = *first.m_object->m_value.string; break; } @@ -1988,6 +2049,8 @@ first.m_object->type_name()); } } + + assert_invariant(); } /*! @@ -2013,6 +2076,7 @@ explicit basic_json(std::istream& i, const parser_callback_t cb = nullptr) { *this = parser(i, cb).parse(); + assert_invariant(); } /////////////////////////////////////// @@ -2044,25 +2108,25 @@ basic_json(const basic_json& other) : m_type(other.m_type) { + // check of passed value is valid + other.assert_invariant(); + switch (m_type) { case value_t::object: { - assert(other.m_value.object != nullptr); m_value = *other.m_value.object; break; } case value_t::array: { - assert(other.m_value.array != nullptr); m_value = *other.m_value.array; break; } case value_t::string: { - assert(other.m_value.string != nullptr); m_value = *other.m_value.string; break; } @@ -2096,6 +2160,8 @@ break; } } + + assert_invariant(); } /*! @@ -2120,9 +2186,14 @@ : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { + // check that passed value is valid + other.assert_invariant(); + // invalidate payload other.m_type = value_t::null; other.m_value = {}; + + assert_invariant(); } /*! @@ -2155,9 +2226,14 @@ std::is_nothrow_move_assignable<json_value>::value ) { + // check that passed value is valid + other.assert_invariant(); + using std::swap; swap(m_type, other.m_type); swap(m_value, other.m_value); + + assert_invariant(); return *this; } @@ -2178,6 +2254,8 @@ */ ~basic_json() { + assert_invariant(); + switch (m_type) { case value_t::object: @@ -2670,7 +2748,6 @@ { if (is_object()) { - assert(m_value.object != nullptr); return T(m_value.object->begin(), m_value.object->end()); } else @@ -2684,7 +2761,6 @@ { if (is_object()) { - assert(m_value.object != nullptr); return *(m_value.object); } else @@ -2707,7 +2783,6 @@ if (is_array()) { T to_vector; - assert(m_value.array != nullptr); std::transform(m_value.array->begin(), m_value.array->end(), std::inserter(to_vector, to_vector.end()), [](basic_json i) { @@ -2732,7 +2807,6 @@ if (is_array()) { std::vector<T> to_vector; - assert(m_value.array != nullptr); to_vector.reserve(m_value.array->size()); std::transform(m_value.array->begin(), m_value.array->end(), std::inserter(to_vector, to_vector.end()), [](basic_json i) @@ -2757,7 +2831,6 @@ { if (is_array()) { - assert(m_value.array != nullptr); return T(m_value.array->begin(), m_value.array->end()); } else @@ -2771,7 +2844,6 @@ { if (is_array()) { - assert(m_value.array != nullptr); return *(m_value.array); } else @@ -2789,7 +2861,6 @@ { if (is_string()) { - assert(m_value.string != nullptr); return *m_value.string; } else @@ -3266,7 +3337,6 @@ { try { - assert(m_value.array != nullptr); return m_value.array->at(idx); } catch (std::out_of_range&) @@ -3310,7 +3380,6 @@ { try { - assert(m_value.array != nullptr); return m_value.array->at(idx); } catch (std::out_of_range&) @@ -3358,7 +3427,6 @@ { try { - assert(m_value.object != nullptr); return m_value.object->at(key); } catch (std::out_of_range&) @@ -3406,7 +3474,6 @@ { try { - assert(m_value.object != nullptr); return m_value.object->at(key); } catch (std::out_of_range&) @@ -3453,13 +3520,13 @@ { m_type = value_t::array; m_value.array = create<array_t>(); + assert_invariant(); } // operator[] only works for arrays if (is_array()) { // fill up array with null values if given idx is outside range - assert(m_value.array != nullptr); if (idx >= m_value.array->size()) { m_value.array->insert(m_value.array->end(), @@ -3499,7 +3566,6 @@ // const operator[] only works for arrays if (is_array()) { - assert(m_value.array != nullptr); return m_value.array->operator[](idx); } else @@ -3542,12 +3608,12 @@ { m_type = value_t::object; m_value.object = create<object_t>(); + assert_invariant(); } // operator[] only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->operator[](key); } else @@ -3588,7 +3654,6 @@ // const operator[] only works for objects if (is_object()) { - assert(m_value.object != nullptr); assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -3701,12 +3766,12 @@ { m_type = value_t::object; m_value = value_t::object; + assert_invariant(); } // at only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->operator[](key); } else @@ -3748,7 +3813,6 @@ // at only works for objects if (is_object()) { - assert(m_value.object != nullptr); assert(m_value.object->find(key) != m_value.object->end()); return m_value.object->find(key)->second; } @@ -4076,24 +4140,25 @@ if (is_string()) { - delete m_value.string; + AllocatorType<string_t> alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; + assert_invariant(); break; } case value_t::object: { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); break; } case value_t::array: { - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); break; } @@ -4184,17 +4249,19 @@ if (is_string()) { - delete m_value.string; + AllocatorType<string_t> alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); m_value.string = nullptr; } m_type = value_t::null; + assert_invariant(); break; } case value_t::object: { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, last.m_it.object_iterator); break; @@ -4202,7 +4269,6 @@ case value_t::array: { - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, last.m_it.array_iterator); break; @@ -4251,7 +4317,6 @@ // this erase only works for objects if (is_object()) { - assert(m_value.object != nullptr); return m_value.object->erase(key); } else @@ -4294,7 +4359,6 @@ throw std::out_of_range("array index " + std::to_string(idx) + " is out of range"); } - assert(m_value.array != nullptr); m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx)); } else @@ -4337,7 +4401,6 @@ if (is_object()) { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } @@ -4354,7 +4417,6 @@ if (is_object()) { - assert(m_value.object != nullptr); result.m_it.object_iterator = m_value.object->find(key); } @@ -4382,7 +4444,6 @@ size_type count(typename object_t::key_type key) const { // return 0 for all nonobject types - assert(not is_object() or m_value.object != nullptr); return is_object() ? m_value.object->count(key) : 0; } @@ -4758,14 +4819,12 @@ case value_t::array: { // delegate call to array_t::empty() - assert(m_value.array != nullptr); return m_value.array->empty(); } case value_t::object: { // delegate call to object_t::empty() - assert(m_value.object != nullptr); return m_value.object->empty(); } @@ -4828,14 +4887,12 @@ case value_t::array: { // delegate call to array_t::size() - assert(m_value.array != nullptr); return m_value.array->size(); } case value_t::object: { // delegate call to object_t::size() - assert(m_value.object != nullptr); return m_value.object->size(); } @@ -4890,14 +4947,12 @@ case value_t::array: { // delegate call to array_t::max_size() - assert(m_value.array != nullptr); return m_value.array->max_size(); } case value_t::object: { // delegate call to object_t::max_size() - assert(m_value.object != nullptr); return m_value.object->max_size(); } @@ -4974,21 +5029,18 @@ case value_t::string: { - assert(m_value.string != nullptr); m_value.string->clear(); break; } case value_t::array: { - assert(m_value.array != nullptr); m_value.array->clear(); break; } case value_t::object: { - assert(m_value.object != nullptr); m_value.object->clear(); break; } @@ -5033,10 +5085,10 @@ { m_type = value_t::array; m_value = value_t::array; + assert_invariant(); } // add element to array (move semantics) - assert(m_value.array != nullptr); m_value.array->push_back(std::move(val)); // invalidate object val.m_type = value_t::null; @@ -5069,10 +5121,10 @@ { m_type = value_t::array; m_value = value_t::array; + assert_invariant(); } // add element to array - assert(m_value.array != nullptr); m_value.array->push_back(val); } @@ -5119,10 +5171,10 @@ { m_type = value_t::object; m_value = value_t::object; + assert_invariant(); } // add element to array - assert(m_value.object != nullptr); m_value.object->insert(val); } @@ -5219,7 +5271,6 @@ // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); return result; } @@ -5275,7 +5326,6 @@ // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); return result; } @@ -5343,7 +5393,6 @@ // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert( pos.m_it.array_iterator, first.m_it.array_iterator, @@ -5391,7 +5440,6 @@ // insert to array and return iterator iterator result(this); - assert(m_value.array != nullptr); result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); return result; } @@ -5422,6 +5470,7 @@ { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); + assert_invariant(); } /*! @@ -5449,7 +5498,6 @@ // swap only works for arrays if (is_array()) { - assert(m_value.array != nullptr); std::swap(*(m_value.array), other); } else @@ -5483,7 +5531,6 @@ // swap only works for objects if (is_object()) { - assert(m_value.object != nullptr); std::swap(*(m_value.object), other); } else @@ -5517,7 +5564,6 @@ // swap only works for strings if (is_string()) { - assert(m_value.string != nullptr); std::swap(*(m_value.string), other); } else @@ -5604,14 +5650,10 @@ { case value_t::array: { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); return *lhs.m_value.array == *rhs.m_value.array; } case value_t::object: { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); return *lhs.m_value.object == *rhs.m_value.object; } case value_t::null: @@ -5620,8 +5662,6 @@ } case value_t::string: { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); return *lhs.m_value.string == *rhs.m_value.string; } case value_t::boolean: @@ -5794,14 +5834,10 @@ { case value_t::array: { - assert(lhs.m_value.array != nullptr); - assert(rhs.m_value.array != nullptr); return *lhs.m_value.array < *rhs.m_value.array; } case value_t::object: { - assert(lhs.m_value.object != nullptr); - assert(rhs.m_value.object != nullptr); return *lhs.m_value.object < *rhs.m_value.object; } case value_t::null: @@ -5810,8 +5846,6 @@ } case value_t::string: { - assert(lhs.m_value.string != nullptr); - assert(rhs.m_value.string != nullptr); return *lhs.m_value.string < *rhs.m_value.string; } case value_t::boolean: @@ -6361,8 +6395,6 @@ { case value_t::object: { - assert(m_value.object != nullptr); - if (m_value.object->empty()) { o << "{}"; @@ -6403,8 +6435,6 @@ case value_t::array: { - assert(m_value.array != nullptr); - if (m_value.array->empty()) { o << "[]"; @@ -6443,7 +6473,6 @@ case value_t::string: { - assert(m_value.string != nullptr); o << string_t("\"") << escape_string(*m_value.string) << "\""; return; } @@ -6692,6 +6721,12 @@ This class implements a const iterator for the @ref basic_json class. From this class, the @ref iterator class is derived. + @note An iterator is called *initialized* when a pointer to a JSON value + has been set (e.g., by a constructor or a copy assignment). If the + iterator is default-constructed, it is *uninitialized* and most + methods are undefined. The library uses assertions to detect calls + on uninitialized iterators. + @requirement The class satisfies the following concept requirements: - [RandomAccessIterator](http://en.cppreference.com/w/cpp/concept/RandomAccessIterator): The iterator that can be moved to point (forward and backward) to any @@ -6719,7 +6754,12 @@ /// default constructor const_iterator() = default; - /// constructor for a given JSON instance + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ explicit const_iterator(pointer object) noexcept : m_object(object) { @@ -6747,40 +6787,53 @@ } } - /// copy constructor given a nonconst iterator + /*! + @brief copy constructor given a non-const iterator + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ explicit const_iterator(const iterator& other) noexcept : m_object(other.m_object) { - assert(m_object != nullptr); - - switch (m_object->m_type) + if (m_object != nullptr) { - case basic_json::value_t::object: + switch (m_object->m_type) { - m_it.object_iterator = other.m_it.object_iterator; - break; - } + case basic_json::value_t::object: + { + m_it.object_iterator = other.m_it.object_iterator; + break; + } - case basic_json::value_t::array: - { - m_it.array_iterator = other.m_it.array_iterator; - break; - } + case basic_json::value_t::array: + { + m_it.array_iterator = other.m_it.array_iterator; + break; + } - default: - { - m_it.primitive_iterator = other.m_it.primitive_iterator; - break; + default: + { + m_it.primitive_iterator = other.m_it.primitive_iterator; + break; + } } } } - /// copy constructor + /*! + @brief copy constructor + @param[in] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ const_iterator(const const_iterator& other) noexcept : m_object(other.m_object), m_it(other.m_it) {} - /// copy assignment + /*! + @brief copy assignment + @param[in,out] other iterator to copy from + @note It is not checked whether @a other is initialized. + */ const_iterator& operator=(const_iterator other) noexcept( std::is_nothrow_move_constructible<pointer>::value and std::is_nothrow_move_assignable<pointer>::value and @@ -6794,7 +6847,10 @@ } private: - /// set the iterator to the first value + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ void set_begin() noexcept { assert(m_object != nullptr); @@ -6803,14 +6859,12 @@ { case basic_json::value_t::object: { - assert(m_object->m_value.object != nullptr); m_it.object_iterator = m_object->m_value.object->begin(); break; } case basic_json::value_t::array: { - assert(m_object->m_value.array != nullptr); m_it.array_iterator = m_object->m_value.array->begin(); break; } @@ -6830,7 +6884,10 @@ } } - /// set the iterator past the last value + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ void set_end() noexcept { assert(m_object != nullptr); @@ -6839,14 +6896,12 @@ { case basic_json::value_t::object: { - assert(m_object->m_value.object != nullptr); m_it.object_iterator = m_object->m_value.object->end(); break; } case basic_json::value_t::array: { - assert(m_object->m_value.array != nullptr); m_it.array_iterator = m_object->m_value.array->end(); break; } @@ -6860,7 +6915,10 @@ } public: - /// return a reference to the value pointed to by the iterator + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference operator*() const { assert(m_object != nullptr); @@ -6869,14 +6927,12 @@ { case basic_json::value_t::object: { - assert(m_object->m_value.object); assert(m_it.object_iterator != m_object->m_value.object->end()); return m_it.object_iterator->second; } case basic_json::value_t::array: { - assert(m_object->m_value.array); assert(m_it.array_iterator != m_object->m_value.array->end()); return *m_it.array_iterator; } @@ -6900,7 +6956,10 @@ } } - /// dereference the iterator + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ pointer operator->() const { assert(m_object != nullptr); @@ -6909,14 +6968,12 @@ { case basic_json::value_t::object: { - assert(m_object->m_value.object); assert(m_it.object_iterator != m_object->m_value.object->end()); return &(m_it.object_iterator->second); } case basic_json::value_t::array: { - assert(m_object->m_value.array); assert(m_it.array_iterator != m_object->m_value.array->end()); return &*m_it.array_iterator; } @@ -6935,7 +6992,10 @@ } } - /// post-increment (it++) + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator++(int) { auto result = *this; @@ -6943,7 +7003,10 @@ return result; } - /// pre-increment (++it) + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator++() { assert(m_object != nullptr); @@ -6972,7 +7035,10 @@ return *this; } - /// post-decrement (it--) + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator--(int) { auto result = *this; @@ -6980,7 +7046,10 @@ return result; } - /// pre-decrement (--it) + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator--() { assert(m_object != nullptr); @@ -7009,7 +7078,10 @@ return *this; } - /// comparison: equal + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator==(const const_iterator& other) const { // if objects are not the same, the comparison is undefined @@ -7039,13 +7111,19 @@ } } - /// comparison: not equal + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator!=(const const_iterator& other) const { return not operator==(other); } - /// comparison: smaller + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator<(const const_iterator& other) const { // if objects are not the same, the comparison is undefined @@ -7075,25 +7153,37 @@ } } - /// comparison: less than or equal + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator<=(const const_iterator& other) const { return not other.operator < (*this); } - /// comparison: greater than + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator>(const const_iterator& other) const { return not operator<=(other); } - /// comparison: greater than or equal + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ bool operator>=(const const_iterator& other) const { return not operator<(other); } - /// add to iterator + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator+=(difference_type i) { assert(m_object != nullptr); @@ -7121,13 +7211,19 @@ return *this; } - /// subtract from iterator + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator& operator-=(difference_type i) { return operator+=(-i); } - /// add to iterator + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator+(difference_type i) { auto result = *this; @@ -7135,7 +7231,10 @@ return result; } - /// subtract from iterator + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ const_iterator operator-(difference_type i) { auto result = *this; @@ -7143,7 +7242,10 @@ return result; } - /// return difference + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ difference_type operator-(const const_iterator& other) const { assert(m_object != nullptr); @@ -7167,7 +7269,10 @@ } } - /// access to successor + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference operator[](difference_type n) const { assert(m_object != nullptr); @@ -7203,7 +7308,10 @@ } } - /// return the key of an object iterator + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ typename object_t::key_type key() const { assert(m_object != nullptr); @@ -7218,7 +7326,10 @@ } } - /// return the value of an iterator + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ reference value() const { return operator*(); @@ -8228,6 +8339,7 @@ basic_json parse() { basic_json result = parse_internal(true); + result.assert_invariant(); expect(lexer::token_type::end_of_input); @@ -8250,7 +8362,7 @@ { // explicitly set result to object to cope with {} result.m_type = value_t::object; - result.m_value = json_value(value_t::object); + result.m_value = value_t::object; } // read next token @@ -8328,7 +8440,7 @@ { // explicitly set result to object to cope with [] result.m_type = value_t::array; - result.m_value = json_value(value_t::array); + result.m_value = value_t::array; } // read next token @@ -9027,7 +9139,6 @@ basic_json result; // iterate the JSON object values - assert(value.m_value.object != nullptr); for (const auto& element : *value.m_value.object) { if (not element.second.is_primitive())
diff --git a/test/src/fuzz.cpp b/test/src/fuzz.cpp index c203845..f40059d 100644 --- a/test/src/fuzz.cpp +++ b/test/src/fuzz.cpp
@@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (fuzz test support) -| | |__ | | | | | | version 3.0.0 +| | |__ | | | | | | version 2.0.2 |_____|_____|_____|_|___| https://github.com/nlohmann/json Run "make fuzz_testing" and follow the instructions.
diff --git a/test/src/unit.cpp b/test/src/unit.cpp index b7688a4..75f689f 100644 --- a/test/src/unit.cpp +++ b/test/src/unit.cpp
@@ -1,7 +1,7 @@ /* __ _____ _____ _____ __| | __| | | | JSON for Modern C++ (test suite) -| | |__ | | | | | | version 3.0.0 +| | |__ | | | | | | version 2.0.2 |_____|_____|_____|_|___| https://github.com/nlohmann/json Licensed under the MIT License <http://opensource.org/licenses/MIT>.