From aeb4f87a278776b9166c6eb8484264a84b19c7af Mon Sep 17 00:00:00 2001
From: Niels Lohmann <niels.lohmann@gmail.com>
Date: Mon, 28 Nov 2016 18:33:46 +0100
Subject: [PATCH] :sparkles: added return value for emplace (#349)

---
 doc/examples/emplace.cpp    |  9 ++++++++-
 doc/examples/emplace.link   |  2 +-
 doc/examples/emplace.output |  2 ++
 src/json.hpp                | 26 +++++++++++++++++++-------
 src/json.hpp.re2c           | 26 +++++++++++++++++++-------
 test/src/unit-modifiers.cpp | 37 +++++++++++++++++++++++++++++++++----
 6 files changed, 82 insertions(+), 20 deletions(-)

diff --git a/doc/examples/emplace.cpp b/doc/examples/emplace.cpp
index 6014a361..c3b3c3e3 100644
--- a/doc/examples/emplace.cpp
+++ b/doc/examples/emplace.cpp
@@ -13,11 +13,18 @@ int main()
     std::cout << null << '\n';
 
     // add values
-    object.emplace("three", 3);
+    auto res1 = object.emplace("three", 3);
     null.emplace("A", "a");
     null.emplace("B", "b");
 
+    // the following call will not add an object, because there is already
+    // a value stored at key "B"
+    auto res2 = null.emplace("B", "c");
+
     // print values
     std::cout << object << '\n';
+    std::cout << *res1.first << " " << std::boolalpha << res1.second << '\n';
+
     std::cout << null << '\n';
+    std::cout << *res2.first << " " << std::boolalpha << res2.second << '\n';
 }
diff --git a/doc/examples/emplace.link b/doc/examples/emplace.link
index e78fd0e5..a9366c32 100644
--- a/doc/examples/emplace.link
+++ b/doc/examples/emplace.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Qg5Ogrh8yFrwT2GY"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/B6ILaoysGMliouEO"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/emplace.output b/doc/examples/emplace.output
index 2bc9c002..83d6f773 100644
--- a/doc/examples/emplace.output
+++ b/doc/examples/emplace.output
@@ -1,4 +1,6 @@
 {"one":1,"two":2}
 null
 {"one":1,"three":3,"two":2}
+3 true
 {"A":"a","B":"b"}
+"b" false
diff --git a/src/json.hpp b/src/json.hpp
index fdc57dff..36afd56b 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -5075,15 +5075,20 @@ class basic_json
     }
 
     /*!
-    @brief add an object to an object
+    @brief add an object to an object if key does not exist
 
-    Creates a JSON value from the passed parameters @a args to the JSON
-    object. If the function is called on a JSON null value, an empty object
-    is created before appending the value created from @a args.
+    Inserts a new element into a JSON object constructed in-place with the given
+    @a args if there is no element with the key in the container. If the
+    function is called on a JSON null value, an empty object is created before
+    appending the value created from @a args.
 
     @param[in] args arguments to forward to a constructor of @ref basic_json
     @tparam Args compatible types to create a @ref basic_json object
 
+    @return a pair consisting of an iterator to the inserted element, or the
+            already-existing element if no insertion happened, and a bool
+            denoting whether the insertion took place.
+
     @throw std::domain_error when called on a type other than JSON object or
     null; example: `"cannot use emplace() with number"`
 
@@ -5091,12 +5096,13 @@ class basic_json
 
     @liveexample{The example shows how `emplace()` can be used to add elements
     to a JSON object. Note how the `null` value was silently converted to a
-    JSON object.,emplace}
+    JSON object. Further note how no value is added if there was already one
+    value stored with the same key.,emplace}
 
     @since version 2.0.8
     */
     template<class... Args>
-    void emplace(Args&& ... args)
+    std::pair<iterator, bool> emplace(Args&& ... args)
     {
         // emplace only works for null objects or arrays
         if (not(is_null() or is_object()))
@@ -5113,7 +5119,13 @@ class basic_json
         }
 
         // add element to array (perfect forwarding)
-        m_value.object->emplace(std::forward<Args>(args)...);
+        auto res = m_value.object->emplace(std::forward<Args>(args)...);
+        // create result iterator and set iterator to the result of emplace
+        auto it = begin();
+        it.m_it.object_iterator = res.first;
+
+        // return pair of iterator and boolean
+        return {it, res.second};
     }
 
     /*!
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 383f4b8f..b629dc61 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -5075,15 +5075,20 @@ class basic_json
     }
 
     /*!
-    @brief add an object to an object
+    @brief add an object to an object if key does not exist
 
-    Creates a JSON value from the passed parameters @a args to the JSON
-    object. If the function is called on a JSON null value, an empty object
-    is created before appending the value created from @a args.
+    Inserts a new element into a JSON object constructed in-place with the given
+    @a args if there is no element with the key in the container. If the
+    function is called on a JSON null value, an empty object is created before
+    appending the value created from @a args.
 
     @param[in] args arguments to forward to a constructor of @ref basic_json
     @tparam Args compatible types to create a @ref basic_json object
 
+    @return a pair consisting of an iterator to the inserted element, or the
+            already-existing element if no insertion happened, and a bool
+            denoting whether the insertion took place.
+
     @throw std::domain_error when called on a type other than JSON object or
     null; example: `"cannot use emplace() with number"`
 
@@ -5091,12 +5096,13 @@ class basic_json
 
     @liveexample{The example shows how `emplace()` can be used to add elements
     to a JSON object. Note how the `null` value was silently converted to a
-    JSON object.,emplace}
+    JSON object. Further note how no value is added if there was already one
+    value stored with the same key.,emplace}
 
     @since version 2.0.8
     */
     template<class... Args>
-    void emplace(Args&& ... args)
+    std::pair<iterator, bool> emplace(Args&& ... args)
     {
         // emplace only works for null objects or arrays
         if (not(is_null() or is_object()))
@@ -5113,7 +5119,13 @@ class basic_json
         }
 
         // add element to array (perfect forwarding)
-        m_value.object->emplace(std::forward<Args>(args)...);
+        auto res = m_value.object->emplace(std::forward<Args>(args)...);
+        // create result iterator and set iterator to the result of emplace
+        auto it = begin();
+        it.m_it.object_iterator = res.first;
+
+        // return pair of iterator and boolean
+        return {it, res.second};
     }
 
     /*!
diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp
index 499edde7..e715d55f 100644
--- a/test/src/unit-modifiers.cpp
+++ b/test/src/unit-modifiers.cpp
@@ -302,18 +302,47 @@ TEST_CASE("modifiers")
         {
             SECTION("null")
             {
+                // start with a null value
                 json j;
-                j.emplace("foo", "bar");
-                j.emplace("baz", "bam");
+
+                // add a new key
+                auto res1 = j.emplace("foo", "bar");
+                CHECK(res1.second == true);
+                CHECK(*res1.first == "bar");
+
+                // the null value is changed to an object
                 CHECK(j.type() == json::value_t::object);
+
+                // add a new key
+                auto res2 = j.emplace("baz", "bam");
+                CHECK(res2.second == true);
+                CHECK(*res2.first == "bam");
+
+                // we try to insert at given key - no change
+                auto res3 = j.emplace("baz", "bad");
+                CHECK(res3.second == false);
+                CHECK(*res3.first == "bam");
+
+                // the final object
                 CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
             }
 
             SECTION("object")
             {
+                // start with an object
                 json j = {{"foo", "bar"}};
-                j.emplace("baz", "bam");
-                CHECK(j.type() == json::value_t::object);
+
+                // add a new key
+                auto res1 = j.emplace("baz", "bam");
+                CHECK(res1.second == true);
+                CHECK(*res1.first == "bam");
+
+                // add an existing key
+                auto res2 = j.emplace("foo", "bad");
+                CHECK(res2.second == false);
+                CHECK(*res2.first == "bar");
+
+                // check final object
                 CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}}));
             }
         }