* Make basic_json::insert() provide its documented strong exception guarantee (#5986) A failed allocation while the new elements were copied left the value changed, and with JSON_DIAGNOSTICS stale parent pointers behind. The extra work now happens almost only on the failure path: - Array insert(pos, cnt, val) and insert(pos, first, last) append the copies at the end and rotate them into place. If a copy throws, the appended elements are erased again (noexcept) and the parents are set. Moving a basic_json cannot throw, so reallocation is safe. This is no slower than vector::insert (faster for numbers), needs no temporary array, and keeps the elements that libstdc++ lost on reallocation. - Array insert(pos, val) copies a non-scalar value first and inserts it by move, except at the end, where vector::insert is strong. Scalars keep the direct path. - The object range insert(first, last) into an empty object uses the container's own range insertion and clears the object on an exception. Into a non-empty object it inserts member by member and logs one bit per source member (a new member is detected by the size change; no allocation for the first 256 members); on an exception the members added are erased by walking the source range. No assumption that key_type is string_t. - The parents are set when a failed insertion is undone. The regression test fails every allocation of every insert() overload in turn, for json and ordered_json, and checks the value and its parents. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy and -Wnrvo findings in insert rollback helpers Declare the rollback helpers non-movable, replace the nested conditional and C array in object_insert_rollback, silence the intentional copy in the allocator test, and avoid the missed NRVO in the test factory lambda. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N5NasAgbDGG28MqXjQvLqp Signed-off-by: Claude <noreply@anthropic.com> * insert(): strong guarantee for single elements, basic for the rest Rework of the previous two commits, following the discussion in #5996: the standard containers give the strong guarantee only for a single element (std::vector: at the end, or if the exception does not come from copying T; std::map: single-element insert), and copying a basic_json allocates. Rebuilding a rollback for the count, range and object-range overloads on top of array_t/object_t made the code verbose and slower. - insert(pos, const basic_json&): at end() forward as before (the standard covers it); elsewhere copy first and insert by move, as libstdc++ does internally (libc++ shifts the tail before copying). - insert_iterator() and object insert(first, last) set the parents in a scope guard, so a failed insertion leaves a valid value with JSON_DIAGNOSTICS too (the next assert_invariant() aborted before). - Docs: strong guarantee for (1), basic guarantee for (2)-(5); the argument checks throw before anything is changed. - Test: allocation-failure sweep checks the strong guarantee for (1) and validity, type and parents for the other overloads. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * insert(): document the exception safety of overloads 2 to 5 once The four basic-guarantee statements were identical up to array/object. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * insert(): simplify the wording of the basic guarantee Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Signed-off-by: Claude <noreply@anthropic.com> Co-authored-by: Claude <noreply@anthropic.com>
8.2 KiB
nlohmann::basic_json::insert
// (1)
iterator insert(const_iterator pos, const basic_json& val);
iterator insert(const_iterator pos, basic_json&& val);
// (2)
iterator insert(const_iterator pos, size_type cnt, const basic_json& val);
// (3)
iterator insert(const_iterator pos, const_iterator first, const_iterator last);
// (4)
iterator insert(const_iterator pos, initializer_list_t ilist);
// (5)
void insert(const_iterator first, const_iterator last);
- Inserts element
valinto an array before iteratorpos. - Inserts
cntcopies ofvalinto an array before iteratorpos. - Inserts elements from range
[first, last)into an array before iteratorpos. - Inserts elements from initializer list
ilistinto an array before iteratorpos. - Inserts elements from range
[first, last)into an object.
Iterator invalidation
For all cases where an element is added to an array, a reallocation can happen, in which case all iterators
(including the end() iterator) and all references to the elements are invalidated. Otherwise, only the
end() iterator is invalidated. Also, any iterator or reference after the insertion point will point to the
same index, which is now a different value.
For ordered_json, also adding an element to an object can yield a reallocation which again
invalidates all iterators and all references. Also, any iterator or reference after the insertion point will point to
the same index, which is now a different value.
Parameters
pos(in)- iterator before which the content will be inserted; may be the
end()iterator val(in)- value to insert
cnt(in)- number of copies of
valto insert first(in)- the start of the range of elements to insert
last(in)- the end of the range of elements to insert
ilist(in)- initializer list to insert the values from
Return value
- iterator pointing to the inserted
val. - iterator pointing to the first element inserted, or
posif#!cpp cnt==0 - iterator pointing to the first element inserted, or
posif#!cpp first==last - iterator pointing to the first element inserted, or
posifilistis empty - (none)
Exception safety
Overload (1) gives the strong guarantee: if an exception is thrown, there are no changes in the JSON value.
Overloads (2) to (5) give the basic guarantee: if an exception is thrown while the elements or members are copied
(for example, std::bad_alloc), the JSON value may be partially modified, but it stays a valid array or object.
The exceptions listed below are thrown before the JSON value is changed.
Exceptions
- The function can throw the following exceptions:
- Throws
type_error.309if called on JSON values other than arrays; example:"cannot use insert() with string" - Throws
invalid_iterator.202if called on an iterator which does not belong to the current JSON value; example:"iterator does not fit current value"
- Throws
- The function can throw the following exceptions:
- Throws
type_error.309if called on JSON values other than arrays; example:"cannot use insert() with string" - Throws
invalid_iterator.202if called on an iterator which does not belong to the current JSON value; example:"iterator does not fit current value"
- Throws
- The function can throw the following exceptions:
- Throws
type_error.309if called on JSON values other than arrays; example:"cannot use insert() with string" - Throws
invalid_iterator.202if called on an iterator which does not belong to the current JSON value; example:"iterator does not fit current value" - Throws
invalid_iterator.210iffirstandlastdo not belong to the same JSON value; example:"iterators do not fit" - Throws
invalid_iterator.211iffirstorlastare iterators into container for which insert is called; example:"passed iterators may not belong to container" - Throws
invalid_iterator.202iffirstorlastdo not point to an array; example:"iterators first and last must point to arrays"
- Throws
- The function can throw the following exceptions:
- Throws
type_error.309if called on JSON values other than arrays; example:"cannot use insert() with string" - Throws
invalid_iterator.202if called on an iterator which does not belong to the current JSON value; example:"iterator does not fit current value"
- Throws
- The function can throw the following exceptions:
- Throws
type_error.309if called on JSON values other than objects; example:"cannot use insert() with string" - Throws
invalid_iterator.202iffirstorlastdo not point to an object; example:"iterators first and last must point to objects" - Throws
invalid_iterator.210iffirstandlastdo not belong to the same JSON value; example:"iterators do not fit"
- Throws
Complexity
- Constant plus linear in the distance between
posand end of the container. - Linear in
cntplus linear in the distance betweenposand end of the container. - Linear in
#!cpp std::distance(first, last)plus linear in the distance betweenposand end of the container. - Linear in
ilist.size()plus linear in the distance betweenposand end of the container. O(N*log(size() + N)), whereNis the number of elements to insert.
Examples
??? example "Example: (1) insert element into array"
The example shows how `insert()` is used.
```cpp
--8<-- "examples/insert.cpp"
```
Output:
```json
--8<-- "examples/insert.output"
```
??? example "Example: (2) insert copies of element into array"
The example shows how `insert()` is used.
```cpp
--8<-- "examples/insert__count.cpp"
```
Output:
```json
--8<-- "examples/insert__count.output"
```
??? example "Example: (3) insert a range of elements into an array"
The example shows how `insert()` is used.
```cpp
--8<-- "examples/insert__range.cpp"
```
Output:
```json
--8<-- "examples/insert__range.output"
```
??? example "Example: (4) insert elements from an initializer list into an array"
The example shows how `insert()` is used.
```cpp
--8<-- "examples/insert__ilist.cpp"
```
Output:
```json
--8<-- "examples/insert__ilist.output"
```
??? example "Example: (5) insert a range of elements into an object"
The example shows how `insert()` is used.
```cpp
--8<-- "examples/insert__range_object.cpp"
```
Output:
```json
--8<-- "examples/insert__range_object.output"
```
See also
emplace- add a value to an objectemplace_back- add a value to an arraypush_back- add a value to an array/objectupdate- merges objects- Modifying values - the article on modifying values
Version history
- Added in version 1.0.0.
- Added in version 1.0.0.
- Added in version 1.0.0.
- Added in version 1.0.0. Fixed in version 3.13.0 to copy the values before inserting; before, an
ilistthat referred to elements of the array being inserted into could insert wrong values, because the range insert could move from or shift an element before it was copied. - Added in version 3.0.0.