Skip to content

Commit

Permalink
proposal for emplace/emplace_back (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
nlohmann committed Nov 11, 2016
1 parent 9ca00e4 commit 1be73b9
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 1 deletion.
23 changes: 23 additions & 0 deletions doc/examples/emplace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <json.hpp>

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';
}
1 change: 1 addition & 0 deletions doc/examples/emplace.link
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/Qg5Ogrh8yFrwT2GY"><b>online</b></a>
4 changes: 4 additions & 0 deletions doc/examples/emplace.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{"one":1,"two":2}
null
{"one":1,"three":3,"two":2}
{"A":"a","B":"b"}
23 changes: 23 additions & 0 deletions doc/examples/emplace_back.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <json.hpp>

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';
}
1 change: 1 addition & 0 deletions doc/examples/emplace_back.link
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<a target="_blank" href="http://melpon.org/wandbox/permlink/jdch45YEMX94DvlH"><b>online</b></a>
4 changes: 4 additions & 0 deletions doc/examples/emplace_back.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[1,2,3,4,5]
null
[1,2,3,4,5,6]
["first",["second","second","second"]]
11 changes: 10 additions & 1 deletion doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ The container functions known from STL have been extended to support the differe
<td class="ok_green">@link nlohmann::basic_json::max_size `max_size` @endlink (returns `0`)</td>
</tr>
<tr>
<td rowspan="5">modifiers</td>
<td rowspan="6">modifiers</td>
<td>`clear`</td>
<td class="ok_green">@link nlohmann::basic_json::clear `clear` @endlink</td>
<td class="ok_green">@link nlohmann::basic_json::clear `clear` @endlink</td>
Expand Down Expand Up @@ -233,6 +233,15 @@ The container functions known from STL have been extended to support the differe
<td class="nok_throws">throws `std::domain_error`</td>
<td class="ok_green">@link nlohmann::basic_json::push_back(const typename object_t::value_type & val) `push_back` @endlink (creates object)<br>@link nlohmann::basic_json::push_back(const nlohmann::basic_json &) `push_back` @endlink (creates array)</td>
</tr>
<tr>
<td>`emplace` / `emplace_back`</td>
<td class="ok_green">@link nlohmann::basic_json::emplace() `emplace` @endlink</td>
<td class="ok_green">@link nlohmann::basic_json::emplace_back() `emplace_back` @endlink</td>
<td class="nok_throws">throws `std::domain_error`</td>
<td class="nok_throws">throws `std::domain_error`</td>
<td class="nok_throws">throws `std::domain_error`</td>
<td class="ok_green">@link nlohmann::basic_json::emplace() `emplace` @endlink (creates object)<br>@link nlohmann::basic_json::emplace_back() `emplace_back` @endlink (creates array)</td>
</tr>
<tr>
<td>`swap`</td>
<td class="ok_green">@link nlohmann::basic_json::swap `swap` @endlink</td>
Expand Down
84 changes: 84 additions & 0 deletions src/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<class... Args>
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>(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<class... Args>
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>(args)...);
}

/*!
@brief inserts element
Expand Down
84 changes: 84 additions & 0 deletions src/json.hpp.re2c
Original file line number Diff line number Diff line change
Expand Up @@ -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<class... Args>
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>(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<class... Args>
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>(args)...);
}

/*!
@brief inserts element

Expand Down
68 changes: 68 additions & 0 deletions test/src/unit-modifiers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

0 comments on commit 1be73b9

Please sign in to comment.