From 96806af2dc0955ac8c3dc129fcb0694374c9a300 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Fri, 28 Aug 2026 14:48:58 +0000 Subject: [PATCH] docs: note which Abseil containers can be used as template arguments Checked against Abseil release 20250127.0 with the same workload as the other entries on the page (DOM access, dump, parse, CBOR/MessagePack/UBJSON round-trip, flatten, hash), with and without JSON_DIAGNOSTICS. absl::flat_hash_map and absl::node_hash_map work as ObjectType through an adapter that restores the template argument order and makes erase(iterator) return the following iterator, which Abseil's returns as void. The page now carries that adapter, and notes that absl::flat_hash_map does not keep references to the mapped values valid across insertions while absl::node_hash_map does. Both have a capacity() member, so JSON_DIAGNOSTICS already refreshes the parent pointers conservatively for them. absl::btree_map and absl::InlinedVector cannot be used at all: object_t and array_t are formed while basic_json is still incomplete, and both inspect their value type at class scope. std::map and std::vector are required by the standard to tolerate this, third-party containers generally are not, so the page states the constraint on its own rather than only per container. absl::InlinedVector does work as BinaryType, where it is instantiated with a complete type. absl::FixedArray and absl::Cord are not usable. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E Signed-off-by: Niels Lohmann --- .../features/types/template_parameters.md | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/docs/mkdocs/docs/features/types/template_parameters.md b/docs/mkdocs/docs/features/types/template_parameters.md index cf8e687bf..1f3935244 100644 --- a/docs/mkdocs/docs/features/types/template_parameters.md +++ b/docs/mkdocs/docs/features/types/template_parameters.md @@ -5,7 +5,7 @@ never formally states what a type passed for one of these parameters has to prov the way the library uses the resulting [`object_t`](../../api/basic_json/object_t.md), [`array_t`](../../api/basic_json/array_t.md), [`string_t`](../../api/basic_json/string_t.md), etc. This page collects these requirements so they do not have to be discovered by trial and error. Each section also lists the concrete -types that are known to work for that parameter. +types that are known to work for that parameter; the Abseil entries were checked against release 20250127.0. ## How to read this page @@ -28,7 +28,7 @@ Requirements are split into two groups: | Template parameter | Default | Notable substitutes | |-------------------------------------------------------------------|-----------------------------------|------------------------------------------------------------------------| -| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), `tsl::ordered_map` | +| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), Abseil hash maps | | [`ArrayType`](#arraytype) | `std::vector` | vector-like containers only | | [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` | | [`BooleanType`](#booleantype) | `bool` | none worth using | @@ -40,6 +40,14 @@ Requirements are split into two groups: | [`BinaryType`](#binarytype) | `#!cpp std::vector` | `#!cpp std::vector` | | [`CustomBaseClass`](#custombaseclass) | `void` | any default-constructible class | +!!! warning "Third-party containers and incomplete types" + + `object_t` and `array_t` are formed inside the definition of `basic_json`, i.e. while `basic_json` is still an + incomplete type. `#!cpp std::map` and `#!cpp std::vector` are required by the standard to support incomplete + value types; most third-party containers are not, and inspecting the value type at class scope (for instance with + `#!cpp std::is_trivially_move_assignable`) makes them unusable as `ObjectType` or `ArrayType`. This rules out + `absl::btree_map` and `absl::InlinedVector`, among others, no matter how their template arguments are adapted. + ## `ObjectType` `ObjectType` is instantiated as @@ -112,6 +120,45 @@ using unordered_json = nlohmann::basic_json; The same pattern (ignoring the third argument) is how [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) and similar containers are integrated; see [Object Order](../object_order.md). +#### Abseil hash maps + +`absl::flat_hash_map` and `absl::node_hash_map` tolerate an incomplete value type, but they take a hash function as +their third template argument and their `erase(iterator)` returns `#!cpp void` rather than the following iterator. An +adapter that fixes both makes them usable: + +```cpp +template class Map> +struct absl_object +{ + template + struct type : Map, std::equal_to, Allocator> + { + using base_t = Map, std::equal_to, Allocator>; + using base_t::base_t; + using iterator = typename base_t::iterator; + using base_t::erase; + + iterator erase(iterator pos) + { + iterator next = std::next(pos); + base_t::erase(pos); + return next; + } + }; +}; + +template +using flat_hash_object = typename absl_object::template type; + +using flat_hash_json = nlohmann::basic_json; +``` + +`absl::node_hash_map` keeps references to the mapped values valid across insertions; `absl::flat_hash_map` does not, +which makes it behave like [`ordered_json`](../../api/ordered_json.md) with respect to +[iterator invalidation](../../api/basic_json/index.md#iterator-invalidation). Both expose a `capacity()` member +function, so [`JSON_DIAGNOSTICS`](../../api/macros/json_diagnostics.md) treats them conservatively and keeps the +parent pointers correct either way. + #### Iteration order The library never relies on the container's iteration order for correctness; it does determine the order in which @@ -138,6 +185,8 @@ The library does not sort or de-duplicate keys itself; the behavior described in | [`nlohmann::ordered_map`](../../api/ordered_map.md) | full; used by [`ordered_json`](../../api/ordered_json.md) | | `#!cpp std::unordered_map`, through the adapter shown above | full | | [`tsl::ordered_map`](https://github.com/Tessil/ordered-map), [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) | through the same adapter pattern; see [Object Order](../object_order.md) | +| `absl::flat_hash_map`, `absl::node_hash_map`, through the adapter shown above | full | +| `absl::btree_map` | not usable; requires a complete value type | | `#!cpp std::multimap`, `#!cpp std::unordered_multimap` | not usable; `emplace` does not return `#!cpp std::pair` | ## `ArrayType` @@ -183,6 +232,8 @@ using array_t = ArrayType>; | `#!cpp std::vector` (default) | full | | `#!cpp std::deque` | only when wrapped in a type that adds a `capacity()` member function | | `#!cpp std::list` | not usable; no `operator[]`, no `capacity()`, and no random-access iterators | +| `absl::InlinedVector` | not usable; requires a complete value type | +| `absl::FixedArray` | not usable; the size is fixed at construction | ## `StringType` @@ -241,6 +292,7 @@ using array_t = ArrayType>; | a custom string class in a user-defined namespace | full, if the requirements above are met | | `#!cpp std::pmr::string`, `#!cpp std::basic_string` with a custom allocator | not usable beyond the DOM, `dump`, and `parse` -- see below | | `#!cpp std::wstring`, `#!cpp std::u16string`, `#!cpp std::u32string` | not usable; the character type is not one byte wide | +| `absl::Cord` | not usable; no `value_type`, and the storage is not contiguous | !!! warning "`std::basic_string` with a non-default allocator" @@ -436,6 +488,7 @@ such a container to a `basic_json` value. | `#!cpp std::vector` (default) | full | | `#!cpp std::vector` | full | | `#!cpp std::vector` | full | +| `absl::InlinedVector` | full | | `#!cpp std::string` | not usable; `binary_t::container_type` and `string_t` would be the same type, which makes the [`swap`](../../api/basic_json/swap.md) overloads ambiguous | | containers whose `value_type` is wider than one byte | not usable |