diff --git a/doc/examples/emplace.cpp b/doc/examples/emplace.cpp new file mode 100644 index 00000000..6014a361 --- /dev/null +++ b/doc/examples/emplace.cpp @@ -0,0 +1,23 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // create JSON values + json object = {{"one", 1}, {"two", 2}}; + json null; + + // print values + std::cout << object << '\n'; + std::cout << null << '\n'; + + // add values + object.emplace("three", 3); + null.emplace("A", "a"); + null.emplace("B", "b"); + + // print values + std::cout << object << '\n'; + std::cout << null << '\n'; +} diff --git a/doc/examples/emplace.link b/doc/examples/emplace.link new file mode 100644 index 00000000..e78fd0e5 --- /dev/null +++ b/doc/examples/emplace.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/emplace.output b/doc/examples/emplace.output new file mode 100644 index 00000000..2bc9c002 --- /dev/null +++ b/doc/examples/emplace.output @@ -0,0 +1,4 @@ +{"one":1,"two":2} +null +{"one":1,"three":3,"two":2} +{"A":"a","B":"b"} diff --git a/doc/examples/emplace_back.cpp b/doc/examples/emplace_back.cpp new file mode 100644 index 00000000..4e9ec89a --- /dev/null +++ b/doc/examples/emplace_back.cpp @@ -0,0 +1,23 @@ +#include + +using json = nlohmann::json; + +int main() +{ + // create JSON values + json array = {1, 2, 3, 4, 5}; + json null; + + // print values + std::cout << array << '\n'; + std::cout << null << '\n'; + + // add values + array.emplace_back(6); + null.emplace_back("first"); + null.emplace_back(3, "second"); + + // print values + std::cout << array << '\n'; + std::cout << null << '\n'; +} diff --git a/doc/examples/emplace_back.link b/doc/examples/emplace_back.link new file mode 100644 index 00000000..4363e4c7 --- /dev/null +++ b/doc/examples/emplace_back.link @@ -0,0 +1 @@ +online \ No newline at end of file diff --git a/doc/examples/emplace_back.output b/doc/examples/emplace_back.output new file mode 100644 index 00000000..bdd80d82 --- /dev/null +++ b/doc/examples/emplace_back.output @@ -0,0 +1,4 @@ +[1,2,3,4,5] +null +[1,2,3,4,5,6] +["first",["second","second","second"]] diff --git a/doc/index.md b/doc/index.md index fcb5b432..70161d0e 100644 --- a/doc/index.md +++ b/doc/index.md @@ -197,7 +197,7 @@ The container functions known from STL have been extended to support the differe @link nlohmann::basic_json::max_size `max_size` @endlink (returns `0`) - modifiers + modifiers `clear` @link nlohmann::basic_json::clear `clear` @endlink @link nlohmann::basic_json::clear `clear` @endlink @@ -233,6 +233,15 @@ The container functions known from STL have been extended to support the differe throws `std::domain_error` @link nlohmann::basic_json::push_back(const typename object_t::value_type & val) `push_back` @endlink (creates object)
@link nlohmann::basic_json::push_back(const nlohmann::basic_json &) `push_back` @endlink (creates array) + + `emplace` / `emplace_back` + @link nlohmann::basic_json::emplace() `emplace` @endlink + @link nlohmann::basic_json::emplace_back() `emplace_back` @endlink + throws `std::domain_error` + throws `std::domain_error` + throws `std::domain_error` + @link nlohmann::basic_json::emplace() `emplace` @endlink (creates object)
@link nlohmann::basic_json::emplace_back() `emplace_back` @endlink (creates array) + `swap` @link nlohmann::basic_json::swap `swap` @endlink diff --git a/src/json.hpp b/src/json.hpp index a302bb02..3c8d3f82 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -5053,6 +5053,90 @@ class basic_json return *this; } + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + 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 + + @throw std::domain_error when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8 + */ + template + void emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + throw std::domain_error("cannot use emplace_back() with " + type_name()); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + m_value.array->emplace_back(std::forward(args)...); + } + + /*! + @brief add an object to an object + + 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. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @throw std::domain_error when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @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} + + @since version 2.0.8 + */ + template + void emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (not(is_null() or is_object())) + { + throw std::domain_error("cannot use emplace() with " + type_name()); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + m_value.object->emplace(std::forward(args)...); + } + /*! @brief inserts element diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index b829889d..3173d799 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -5053,6 +5053,90 @@ class basic_json return *this; } + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + 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 + + @throw std::domain_error when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8 + */ + template + void emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + throw std::domain_error("cannot use emplace_back() with " + type_name()); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + m_value.array->emplace_back(std::forward(args)...); + } + + /*! + @brief add an object to an object + + 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. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @throw std::domain_error when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @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} + + @since version 2.0.8 + */ + template + void emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (not(is_null() or is_object())) + { + throw std::domain_error("cannot use emplace() with " + type_name()); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + m_value.object->emplace(std::forward(args)...); + } + /*! @brief inserts element diff --git a/test/src/unit-modifiers.cpp b/test/src/unit-modifiers.cpp index fd7fed56..499edde7 100644 --- a/test/src/unit-modifiers.cpp +++ b/test/src/unit-modifiers.cpp @@ -258,6 +258,74 @@ TEST_CASE("modifiers") } } + SECTION("emplace_back()") + { + SECTION("to array") + { + SECTION("null") + { + json j; + j.emplace_back(1); + j.emplace_back(2); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2})); + } + + SECTION("array") + { + json j = {1, 2, 3}; + j.emplace_back("Hello"); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({1, 2, 3, "Hello"})); + } + + SECTION("multiple values") + { + json j; + j.emplace_back(3, "foo"); + CHECK(j.type() == json::value_t::array); + CHECK(j == json({{"foo", "foo", "foo"}})); + } + } + + SECTION("other type") + { + json j = 1; + CHECK_THROWS_AS(j.emplace_back("Hello"), std::domain_error); + CHECK_THROWS_WITH(j.emplace_back("Hello"), "cannot use emplace_back() with number"); + } + } + + SECTION("emplace()") + { + SECTION("to object") + { + SECTION("null") + { + json j; + j.emplace("foo", "bar"); + j.emplace("baz", "bam"); + CHECK(j.type() == json::value_t::object); + CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}})); + } + + SECTION("object") + { + json j = {{"foo", "bar"}}; + j.emplace("baz", "bam"); + CHECK(j.type() == json::value_t::object); + CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}})); + } + } + + SECTION("other type") + { + json j = 1; + CHECK_THROWS_AS(j.emplace("foo", "bar"), std::domain_error); + CHECK_THROWS_WITH(j.emplace("foo", "bar"), "cannot use emplace() with number"); + } + } + SECTION("operator+=") { SECTION("to array")