From 6dceab583c8ad1d5de3755bc7f67d6fe1f7be8f4 Mon Sep 17 00:00:00 2001
From: Niels <niels.lohmann@gmail.com>
Date: Sat, 11 Apr 2015 15:30:19 +0200
Subject: [PATCH] implemented issue #57: erase with iterators

---
 src/json.hpp      | 146 ++++++++++++---
 src/json.hpp.re2c | 146 ++++++++++++---
 test/unit.cpp     | 465 +++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 707 insertions(+), 50 deletions(-)

diff --git a/src/json.hpp b/src/json.hpp
index b83ae8b4..b8bd4f60 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -7,26 +7,6 @@
 @see https://github.com/nlohmann/json
 */
 
-/*!
-@defgroup container Container
-@brief C++ Container concept
-
-A Container is an object used to store other objects and taking care of the
-management of the memory used by the objects it contains.
-
-@see http://en.cppreference.com/w/cpp/concept/Container
-
-@defgroup reversiblecontainer Reversible Container
-@brief C++ Reversible Container concept
-@ingroup container
-
-A ReversibleContainer is a Container that has iterators that meet the
-requirements of either BidirectionalIterator or RandomAccessIterator. Such
-iterators allow a ReversibleContainer to be iterated over in reverse.
-
-@see http://en.cppreference.com/w/cpp/concept/ReversibleContainer
-*/
-
 #ifndef _NLOHMANN_JSON
 #define _NLOHMANN_JSON
 
@@ -871,15 +851,12 @@ class basic_json
             case (value_t::array):
             {
                 T to_vector;
-                //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)
                 {
                     return i.get<typename T::value_type>();
                 });
                 return to_vector;
-
-                //   return T(m_value.array->begin(), m_value.array->end());
             }
             default:
             {
@@ -1187,6 +1164,129 @@ class basic_json
         return m_value.object->operator[](key);
     }
 
+    /// remove element given an iterator
+    template <class T, typename
+              std::enable_if<
+                  std::is_same<T, basic_json::iterator>::value or
+                  std::is_same<T, basic_json::const_iterator>::value
+                  , int>::type
+              = 0>
+    inline T erase(T pos)
+    {
+        // make sure iterator fits the current value
+        if (this != pos.m_object or m_type != pos.m_object->m_type)
+        {
+            throw std::runtime_error("iterator does not fit current value");
+        }
+
+        T result = end();
+
+        switch (m_type)
+        {
+            case value_t::number_integer:
+            case value_t::number_float:
+            case value_t::boolean:
+            case value_t::string:
+            {
+                if (pos.m_it.generic_iterator != 0)
+                {
+                    throw std::out_of_range("iterator out of range");
+                }
+
+                if (m_type == value_t::string)
+                {
+                    delete m_value.string;
+                    m_value.string = nullptr;
+                }
+
+                m_type = value_t::null;
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
+                break;
+            }
+
+            default:
+            {
+                throw std::runtime_error("cannot use erase with " + type_name());
+            }
+        }
+
+        return result;
+    }
+
+    /// remove elements given an iterator range
+    template <class T, typename
+              std::enable_if<
+                  std::is_same<T, basic_json::iterator>::value or
+                  std::is_same<T, basic_json::const_iterator>::value
+                  , int>::type
+              = 0>
+    inline T erase(T first, T last)
+    {
+        // make sure iterator fits the current value
+        if (this != first.m_object or this != last.m_object or
+                m_type != first.m_object->m_type or m_type != last.m_object->m_type)
+        {
+            throw std::runtime_error("iterators do not fit current value");
+        }
+
+        T result = end();
+
+        switch (m_type)
+        {
+            case value_t::number_integer:
+            case value_t::number_float:
+            case value_t::boolean:
+            case value_t::string:
+            {
+                if (first.m_it.generic_iterator != 0 or last.m_it.generic_iterator != 1)
+                {
+                    throw std::out_of_range("iterators out of range");
+                }
+
+                if (m_type == value_t::string)
+                {
+                    delete m_value.string;
+                    m_value.string = nullptr;
+                }
+
+                m_type = value_t::null;
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
+                                              last.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
+                                             last.m_it.array_iterator);
+                break;
+            }
+
+            default:
+            {
+                throw std::runtime_error("cannot use erase with " + type_name());
+            }
+        }
+
+        return result;
+    }
+
     /// remove element from an object given a key
     inline size_type erase(const typename object_t::key_type& key)
     {
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 6a23b51e..a7e7bf9c 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -7,26 +7,6 @@
 @see https://github.com/nlohmann/json
 */
 
-/*!
-@defgroup container Container
-@brief C++ Container concept
-
-A Container is an object used to store other objects and taking care of the
-management of the memory used by the objects it contains.
-
-@see http://en.cppreference.com/w/cpp/concept/Container
-
-@defgroup reversiblecontainer Reversible Container
-@brief C++ Reversible Container concept
-@ingroup container
-
-A ReversibleContainer is a Container that has iterators that meet the
-requirements of either BidirectionalIterator or RandomAccessIterator. Such
-iterators allow a ReversibleContainer to be iterated over in reverse.
-
-@see http://en.cppreference.com/w/cpp/concept/ReversibleContainer
-*/
-
 #ifndef _NLOHMANN_JSON
 #define _NLOHMANN_JSON
 
@@ -871,15 +851,12 @@ class basic_json
             case (value_t::array):
             {
                 T to_vector;
-                //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)
                 {
                     return i.get<typename T::value_type>();
                 });
                 return to_vector;
-
-                //   return T(m_value.array->begin(), m_value.array->end());
             }
             default:
             {
@@ -1187,6 +1164,129 @@ class basic_json
         return m_value.object->operator[](key);
     }
 
+    /// remove element given an iterator
+    template <class T, typename
+              std::enable_if<
+                  std::is_same<T, basic_json::iterator>::value or
+                  std::is_same<T, basic_json::const_iterator>::value
+                  , int>::type
+              = 0>
+    inline T erase(T pos)
+    {
+        // make sure iterator fits the current value
+        if (this != pos.m_object or m_type != pos.m_object->m_type)
+        {
+            throw std::runtime_error("iterator does not fit current value");
+        }
+
+        T result = end();
+
+        switch (m_type)
+        {
+            case value_t::number_integer:
+            case value_t::number_float:
+            case value_t::boolean:
+            case value_t::string:
+            {
+                if (pos.m_it.generic_iterator != 0)
+                {
+                    throw std::out_of_range("iterator out of range");
+                }
+
+                if (m_type == value_t::string)
+                {
+                    delete m_value.string;
+                    m_value.string = nullptr;
+                }
+
+                m_type = value_t::null;
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);
+                break;
+            }
+
+            default:
+            {
+                throw std::runtime_error("cannot use erase with " + type_name());
+            }
+        }
+
+        return result;
+    }
+
+    /// remove elements given an iterator range
+    template <class T, typename
+              std::enable_if<
+                  std::is_same<T, basic_json::iterator>::value or
+                  std::is_same<T, basic_json::const_iterator>::value
+                  , int>::type
+              = 0>
+    inline T erase(T first, T last)
+    {
+        // make sure iterator fits the current value
+        if (this != first.m_object or this != last.m_object or
+                m_type != first.m_object->m_type or m_type != last.m_object->m_type)
+        {
+            throw std::runtime_error("iterators do not fit current value");
+        }
+
+        T result = end();
+
+        switch (m_type)
+        {
+            case value_t::number_integer:
+            case value_t::number_float:
+            case value_t::boolean:
+            case value_t::string:
+            {
+                if (first.m_it.generic_iterator != 0 or last.m_it.generic_iterator != 1)
+                {
+                    throw std::out_of_range("iterators out of range");
+                }
+
+                if (m_type == value_t::string)
+                {
+                    delete m_value.string;
+                    m_value.string = nullptr;
+                }
+
+                m_type = value_t::null;
+                break;
+            }
+
+            case value_t::object:
+            {
+                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,
+                                              last.m_it.object_iterator);
+                break;
+            }
+
+            case value_t::array:
+            {
+                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,
+                                             last.m_it.array_iterator);
+                break;
+            }
+
+            default:
+            {
+                throw std::runtime_error("cannot use erase with " + type_name());
+            }
+        }
+
+        return result;
+    }
+
     /// remove element from an object given a key
     inline size_type erase(const typename object_t::key_type& key)
     {
diff --git a/test/unit.cpp b/test/unit.cpp
index a5abe59e..5b20218a 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -2294,7 +2294,7 @@ TEST_CASE("element access")
 
         SECTION("remove specified element")
         {
-            SECTION("remove element")
+            SECTION("remove element by index")
             {
                 {
                     json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
@@ -2337,7 +2337,112 @@ TEST_CASE("element access")
                 }
             }
 
-            SECTION("access on non-object type")
+            SECTION("remove element by iterator")
+            {
+                SECTION("erase(begin())")
+                {
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::iterator it2 = jarray.erase(jarray.begin());
+                        CHECK(jarray == json({true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+                        CHECK(*it2 == json(true));
+                    }
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::const_iterator it2 = jarray.erase(jarray.cbegin());
+                        CHECK(jarray == json({true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+                        CHECK(*it2 == json(true));
+                    }
+                }
+
+                SECTION("erase(begin(), end())")
+                {
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::iterator it2 = jarray.erase(jarray.begin(), jarray.end());
+                        CHECK(jarray == json::array());
+                        CHECK(it2 == jarray.end());
+                    }
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cend());
+                        CHECK(jarray == json::array());
+                        CHECK(it2 == jarray.cend());
+                    }
+                }
+
+                SECTION("erase(begin(), begin())")
+                {
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::iterator it2 = jarray.erase(jarray.begin(), jarray.begin());
+                        CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+                        CHECK(*it2 == json(1));
+                    }
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::const_iterator it2 = jarray.erase(jarray.cbegin(), jarray.cbegin());
+                        CHECK(jarray == json({1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}}));
+                        CHECK(*it2 == json(1));
+                    }
+                }
+
+                SECTION("erase at offset")
+                {
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::iterator it = jarray.begin() + 3;
+                        json::iterator it2 = jarray.erase(it);
+                        CHECK(jarray == json({1, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
+                        CHECK(*it2 == json(42.23));
+                    }
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::const_iterator it = jarray.cbegin() + 3;
+                        json::const_iterator it2 = jarray.erase(it);
+                        CHECK(jarray == json({1, true, nullptr, 42.23, json::object(), {1, 2, 3}}));
+                        CHECK(*it2 == json(42.23));
+                    }
+                }
+
+                SECTION("erase subrange")
+                {
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::iterator it2 = jarray.erase(jarray.begin() + 2, jarray.begin() + 5);
+                        CHECK(jarray == json({1, true, json::object(), {1, 2, 3}}));
+                        CHECK(*it2 == json::object());
+                    }
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json::const_iterator it2 = jarray.erase(jarray.cbegin() + 2, jarray.cbegin() + 5);
+                        CHECK(jarray == json({1, true, json::object(), {1, 2, 3}}));
+                        CHECK(*it2 == json::object());
+                    }
+                }
+
+                SECTION("different arrays")
+                {
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json jarray2 = {"foo", "bar"};
+                        CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::runtime_error);
+                        CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::runtime_error);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::runtime_error);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::runtime_error);
+                    }
+                    {
+                        json jarray = {1, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
+                        json jarray2 = {"foo", "bar"};
+                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::runtime_error);
+                        CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::runtime_error);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::runtime_error);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::runtime_error);
+                    }
+                }
+            }
+
+            SECTION("remove element by index in non-array type")
             {
                 SECTION("null")
                 {
@@ -2569,7 +2674,7 @@ TEST_CASE("element access")
 
         SECTION("remove specified element")
         {
-            SECTION("remove element")
+            SECTION("remove element by key")
             {
                 CHECK(j.find("integer") != j.end());
                 CHECK(j.erase("integer") == 1);
@@ -2607,7 +2712,112 @@ TEST_CASE("element access")
                 CHECK(j.erase("array") == 0);
             }
 
-            SECTION("access on non-object type")
+            SECTION("remove element by iterator")
+            {
+                SECTION("erase(begin())")
+                {
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        json::iterator it2 = jobject.erase(jobject.begin());
+                        CHECK(jobject == json({{"b", 1}, {"c", 17}}));
+                        CHECK(*it2 == json(1));
+                    }
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        json::const_iterator it2 = jobject.erase(jobject.cbegin());
+                        CHECK(jobject == json({{"b", 1}, {"c", 17}}));
+                        CHECK(*it2 == json(1));
+                    }
+                }
+
+                SECTION("erase(begin(), end())")
+                {
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        json::iterator it2 = jobject.erase(jobject.begin(), jobject.end());
+                        CHECK(jobject == json::object());
+                        CHECK(it2 == jobject.end());
+                    }
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cend());
+                        CHECK(jobject == json::object());
+                        CHECK(it2 == jobject.cend());
+                    }
+                }
+
+                SECTION("erase(begin(), begin())")
+                {
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        json::iterator it2 = jobject.erase(jobject.begin(), jobject.begin());
+                        CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17}}));
+                        CHECK(*it2 == json("a"));
+                    }
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        json::const_iterator it2 = jobject.erase(jobject.cbegin(), jobject.cbegin());
+                        CHECK(jobject == json({{"a", "a"}, {"b", 1}, {"c", 17}}));
+                        CHECK(*it2 == json("a"));
+                    }
+                }
+
+                SECTION("erase at offset")
+                {
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        json::iterator it = jobject.find("b");
+                        json::iterator it2 = jobject.erase(it);
+                        CHECK(jobject == json({{"a", "a"}, {"c", 17}}));
+                        CHECK(*it2 == json(17));
+                    }
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        json::const_iterator it = jobject.find("b");
+                        json::const_iterator it2 = jobject.erase(it);
+                        CHECK(jobject == json({{"a", "a"}, {"c", 17}}));
+                        CHECK(*it2 == json(17));
+                    }
+                }
+
+                SECTION("erase subrange")
+                {
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}};
+                        json::iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e"));
+                        CHECK(jobject == json({{"a", "a"}, {"e", true}}));
+                        CHECK(*it2 == json(true));
+                    }
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}};
+                        json::const_iterator it2 = jobject.erase(jobject.find("b"), jobject.find("e"));
+                        CHECK(jobject == json({{"a", "a"}, {"e", true}}));
+                        CHECK(*it2 == json(true));
+                    }
+                }
+
+                SECTION("different arrays")
+                {
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}};
+                        json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::runtime_error);
+                        CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::runtime_error);
+                        CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::runtime_error);
+                        CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::runtime_error);
+                    }
+                    {
+                        json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}};
+                        json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}};
+                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::runtime_error);
+                        CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::runtime_error);
+                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::runtime_error);
+                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::runtime_error);
+                    }
+                }
+            }
+
+            SECTION("remove element by key in non-object type")
             {
                 SECTION("null")
                 {
@@ -2807,6 +3017,253 @@ TEST_CASE("element access")
             }
         }
     }
+
+    SECTION("other values")
+    {
+        SECTION("erase with one valid iterator")
+        {
+            SECTION("string")
+            {
+                {
+                    json j = "foo";
+                    json::iterator it = j.erase(j.begin());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+                {
+                    json j = "bar";
+                    json::const_iterator it = j.erase(j.cbegin());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+            }
+
+            SECTION("number (boolean)")
+            {
+                {
+                    json j = false;
+                    json::iterator it = j.erase(j.begin());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+                {
+                    json j = true;
+                    json::const_iterator it = j.erase(j.cbegin());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+            }
+
+            SECTION("number (integer)")
+            {
+                {
+                    json j = 17;
+                    json::iterator it = j.erase(j.begin());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+                {
+                    json j = 17;
+                    json::const_iterator it = j.erase(j.cbegin());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+            }
+
+            SECTION("number (floating point)")
+            {
+                {
+                    json j = 23.42;
+                    json::iterator it = j.erase(j.begin());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+                {
+                    json j = 23.42;
+                    json::const_iterator it = j.erase(j.cbegin());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+            }
+        }
+
+        SECTION("erase with one invalid iterator")
+        {
+            SECTION("string")
+            {
+                {
+                    json j = "foo";
+                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
+                }
+                {
+                    json j = "bar";
+                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
+                }
+            }
+
+            SECTION("number (boolean)")
+            {
+                {
+                    json j = false;
+                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
+                }
+                {
+                    json j = true;
+                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
+                }
+            }
+
+            SECTION("number (integer)")
+            {
+                {
+                    json j = 17;
+                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
+                }
+                {
+                    json j = 17;
+                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
+                }
+            }
+
+            SECTION("number (floating point)")
+            {
+                {
+                    json j = 23.42;
+                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
+                }
+                {
+                    json j = 23.42;
+                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
+                }
+            }
+        }
+
+        SECTION("erase with two valid iterators")
+        {
+            SECTION("string")
+            {
+                {
+                    json j = "foo";
+                    json::iterator it = j.erase(j.begin(), j.end());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+                {
+                    json j = "bar";
+                    json::const_iterator it = j.erase(j.cbegin(), j.cend());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+            }
+
+            SECTION("number (boolean)")
+            {
+                {
+                    json j = false;
+                    json::iterator it = j.erase(j.begin(), j.end());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+                {
+                    json j = true;
+                    json::const_iterator it = j.erase(j.cbegin(), j.cend());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+            }
+
+            SECTION("number (integer)")
+            {
+                {
+                    json j = 17;
+                    json::iterator it = j.erase(j.begin(), j.end());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+                {
+                    json j = 17;
+                    json::const_iterator it = j.erase(j.cbegin(), j.cend());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+            }
+
+            SECTION("number (floating point)")
+            {
+                {
+                    json j = 23.42;
+                    json::iterator it = j.erase(j.begin(), j.end());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+                {
+                    json j = 23.42;
+                    json::const_iterator it = j.erase(j.cbegin(), j.cend());
+                    CHECK(j.type() == json::value_t::null);
+                    CHECK(it == j.end());
+                }
+            }
+        }
+
+        SECTION("erase with two invalid iterators")
+        {
+            SECTION("string")
+            {
+                {
+                    json j = "foo";
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
+                }
+                {
+                    json j = "bar";
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
+                }
+            }
+
+            SECTION("number (boolean)")
+            {
+                {
+                    json j = false;
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
+                }
+                {
+                    json j = true;
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
+                }
+            }
+
+            SECTION("number (integer)")
+            {
+                {
+                    json j = 17;
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
+                }
+                {
+                    json j = 17;
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
+                }
+            }
+
+            SECTION("number (floating point)")
+            {
+                {
+                    json j = 23.42;
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
+                }
+                {
+                    json j = 23.42;
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
+                }
+            }
+        }
+    }
 }
 
 TEST_CASE("iterators")