mirror of
https://github.com/nlohmann/json.git
synced 2026-08-29 04:17:32 +00:00
docs: list the types that are known to work for each template parameter
Follow up on the template parameter requirements page: state, for every template parameter, which concrete types work and where they stop working. Each entry was verified by compiling and running a common workload (DOM access, dump, parse, CBOR/MessagePack round-trip, flatten, hash) against that instantiation. Findings worth calling out: - ObjectType no longer needs a key_compare member type, so the std::unordered_map adapter only has to restore the template argument order. A hash-ordered ObjectType works everywhere except unflatten(), which reconstructs an array only when it meets the reference token 0 before the other indices. - ArrayType: std::deque works when wrapped to add capacity(); std::list does not. - StringType: std::pmr::string and std::basic_string with a custom allocator compile for the DOM, dump, and parse, but not for the binary readers, flatten, or diff, because the library assigns std::string values to string_t and int_to_string cannot be overloaded for a type in namespace std. - NumberFloatType: long double works for dump and parse but not for the binary formats, which have no encoding for it. - BinaryType: std::vector<std::byte> supports assignment, get, and the binary formats, but neither dump nor std::hash<basic_json>. Also record the object_comparator_t fix in its version history. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b1c9a68b9b
commit
06feaa8d04
@@ -52,6 +52,13 @@ represent a byte array in modern C++.
|
||||
|
||||
The default values for `BinaryType` is `#!cpp std::vector<std::uint8_t>`.
|
||||
|
||||
#### Supported byte types
|
||||
|
||||
`#!cpp std::vector<std::uint8_t>` and `#!cpp std::vector<char>` are fully supported. With
|
||||
`#!cpp std::vector<std::byte>`, assignment, [`get`](get.md), and the [binary formats](../../features/binary_formats/index.md)
|
||||
work, but [`dump`](dump.md) and [`std::hash<basic_json>`](std_hash.md) do not compile, because `#!cpp std::byte` neither
|
||||
converts to an integer nor is hashable as one.
|
||||
|
||||
#### Custom BinaryType behavior
|
||||
|
||||
When a custom `BinaryType` is configured (other than the default `#!cpp std::vector<std::uint8_t>`), you can assign
|
||||
|
||||
@@ -25,7 +25,9 @@ type to use.
|
||||
`NumberFloatType`
|
||||
: the type to store floating-point numbers. Parsing and serialization are implemented in terms of
|
||||
`#!cpp std::strtof`/`#!cpp std::strtod`/`#!cpp std::strtold` and `#!cpp std::snprintf`, so the type must be
|
||||
`#!cpp float`, `#!cpp double`, or `#!cpp long double`. See
|
||||
`#!cpp float`, `#!cpp double`, or `#!cpp long double`. The
|
||||
[binary formats](../../features/binary_formats/index.md) additionally require `#!cpp float` or `#!cpp double`,
|
||||
because they have no encoding for `#!cpp long double`. See
|
||||
[Template Parameter Requirements](../../features/types/template_parameters.md#numberfloattype).
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -30,3 +30,5 @@ and [`default_object_comparator_t`](default_object_comparator_t.md) otherwise.
|
||||
- Added in version 3.0.0.
|
||||
- Changed to be conditionally defined as `#!cpp typename object_t::key_compare` or `default_object_comparator_t` in
|
||||
version 3.11.0.
|
||||
- Fixed the fallback to `default_object_comparator_t`, which previously failed to compile for object types without a
|
||||
`key_compare` member type, in version 3.13.0.
|
||||
|
||||
@@ -19,10 +19,10 @@ To store objects in C++, a type is defined by the template parameters described
|
||||
|
||||
`ObjectType`
|
||||
: the container to store objects. Its template parameters must have the same order and meaning as those of
|
||||
`std::map`; in particular, the third parameter is a comparator and the container must provide a `key_compare`
|
||||
member type. `#!cpp std::unordered_map` satisfies neither and cannot be used without a wrapper -- see
|
||||
`std::map`; in particular, the third parameter is a comparator. `#!cpp std::unordered_map`, whose third parameter
|
||||
is a hash function, therefore needs an adapter -- see
|
||||
[Template Parameter Requirements](../../features/types/template_parameters.md#objecttype) for the full list of
|
||||
requirements and a wrapper example.
|
||||
requirements, an adapter example, and the containers that are known to work.
|
||||
|
||||
`StringType`
|
||||
: the type of the keys or names (e.g., `std::string`). The comparison function `std::less<StringType>` is used to
|
||||
|
||||
@@ -4,7 +4,8 @@ Class [`basic_json`](../../api/basic_json/index.md) is configurable through elev
|
||||
never formally states what a type passed for one of these parameters has to provide -- the requirements are implied by
|
||||
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.
|
||||
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.
|
||||
|
||||
## How to read this page
|
||||
|
||||
@@ -30,13 +31,13 @@ Requirements are split into two groups:
|
||||
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), `tsl::ordered_map` |
|
||||
| [`ArrayType`](#arraytype) | `std::vector` | vector-like containers only |
|
||||
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
|
||||
| [`BooleanType`](#booleantype) | `bool` | (none) |
|
||||
| [`BooleanType`](#booleantype) | `bool` | none worth using |
|
||||
| [`NumberIntegerType`](#numberintegertype-and-numberunsignedtype) | `std::int64_t` | any signed integer type |
|
||||
| [`NumberUnsignedType`](#numberintegertype-and-numberunsignedtype) | `std::uint64_t` | any unsigned integer type |
|
||||
| [`NumberFloatType`](#numberfloattype) | `double` | `float`, `long double` |
|
||||
| [`NumberFloatType`](#numberfloattype) | `double` | `float` (`long double`: no binary formats) |
|
||||
| [`AllocatorType`](#allocatortype) | `std::allocator` | stateless allocators |
|
||||
| [`JSONSerializer`](#jsonserializer) | `adl_serializer` | serializers with the same interface |
|
||||
| [`BinaryType`](#binarytype) | `#!cpp std::vector<std::uint8_t>` | contiguous byte containers |
|
||||
| [`BinaryType`](#binarytype) | `#!cpp std::vector<std::uint8_t>` | `#!cpp std::vector<char>` |
|
||||
| [`CustomBaseClass`](#custombaseclass) | `void` | any default-constructible class |
|
||||
|
||||
## `ObjectType`
|
||||
@@ -58,7 +59,10 @@ i.e., the template arguments follow the order and meaning of `std::map`.
|
||||
- The template must be usable with **four** type arguments in the order shown above. The third argument is a
|
||||
**comparator**; containers that expect something else in this position (e.g., a hash function) need an alias template
|
||||
or wrapper -- see [Notes](#notes).
|
||||
- Member types `key_type`, `mapped_type`, `value_type`, `iterator`, and **`key_compare`**.
|
||||
- An optional member type `key_compare`. If it is present it becomes
|
||||
[`object_comparator_t`](../../api/basic_json/object_comparator_t.md); otherwise
|
||||
[`default_object_comparator_t`](../../api/basic_json/default_object_comparator_t.md) is used.
|
||||
- Member types `key_type`, `mapped_type`, `value_type`, and `iterator`.
|
||||
- `value_type` must behave like `#!cpp std::pair<const key_type, mapped_type>`; the library accesses `.first` and
|
||||
`.second` on it.
|
||||
- `iterator` must be default-constructible and satisfy
|
||||
@@ -87,25 +91,11 @@ The overloads of [`at`](../../api/basic_json/at.md), [`operator[]`](../../api/ba
|
||||
|
||||
### Notes
|
||||
|
||||
#### `key_compare` is mandatory
|
||||
#### `std::unordered_map` needs an adapter
|
||||
|
||||
[`object_comparator_t`](../../api/basic_json/object_comparator_t.md) is defined as
|
||||
|
||||
```cpp
|
||||
using type = typename std::conditional<has_key_compare<object_t>::value,
|
||||
typename object_t::key_compare,
|
||||
default_object_comparator_t>::type;
|
||||
```
|
||||
|
||||
Both type arguments of `#!cpp std::conditional` have to name valid types, so `#!cpp object_t::key_compare` must exist
|
||||
even when `has_key_compare<object_t>` evaluates to `#!cpp false`. A container without a `key_compare` member type
|
||||
therefore fails to compile.
|
||||
|
||||
#### `std::unordered_map` cannot be used directly
|
||||
|
||||
`#!cpp std::unordered_map` fails on both counts: it has no `key_compare` member type, and its third template parameter
|
||||
is a hash function rather than a comparator. It can be used through a wrapper that fixes the argument order and adds
|
||||
the missing member type:
|
||||
`#!cpp std::unordered_map` cannot be passed directly: its third template parameter is a hash function, but
|
||||
`basic_json` passes a comparator in that position. An alias template or wrapper that restores the expected argument
|
||||
order makes it usable:
|
||||
|
||||
```cpp
|
||||
template<class Key, class T, class IgnoredCompare, class Allocator>
|
||||
@@ -114,7 +104,6 @@ struct unordered_map_object
|
||||
{
|
||||
using base_t = std::unordered_map<Key, T, std::hash<Key>, std::equal_to<Key>, Allocator>;
|
||||
using base_t::base_t;
|
||||
using key_compare = std::equal_to<Key>;
|
||||
};
|
||||
|
||||
using unordered_json = nlohmann::basic_json<unordered_map_object>;
|
||||
@@ -123,6 +112,14 @@ using unordered_json = nlohmann::basic_json<unordered_map_object>;
|
||||
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).
|
||||
|
||||
#### Hash-ordered containers and `unflatten`
|
||||
|
||||
[`unflatten`](../../api/basic_json/unflatten.md) rebuilds an array only if it encounters the reference token `0`
|
||||
before the other indices of that array. Sorted containers (`#!cpp std::map`) and insertion-ordered containers
|
||||
(`nlohmann::ordered_map`) both iterate the flattened object in an order that satisfies this. A container with an
|
||||
unspecified iteration order does not, and `#!cpp j.flatten().unflatten()` may then return objects with the keys
|
||||
`#!json "0"`, `#!json "1"`, ... where the original had arrays.
|
||||
|
||||
#### `capacity()` marks a container as insertion-ordered
|
||||
|
||||
With [`JSON_DIAGNOSTICS`](../../api/macros/json_diagnostics.md) enabled, the library detects insertion-ordered maps by
|
||||
@@ -135,6 +132,16 @@ conservatively -- this is correct, but slower.
|
||||
The library does not sort or de-duplicate keys itself; the behavior described in
|
||||
[`object_t`](../../api/basic_json/object_t.md) is entirely the behavior of the chosen container.
|
||||
|
||||
### Compatible containers
|
||||
|
||||
| Container | Support |
|
||||
|--------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
|
||||
| `#!cpp std::map` (default) | full |
|
||||
| [`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 except [`unflatten`](../../api/basic_json/unflatten.md) |
|
||||
| [`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) |
|
||||
| `#!cpp std::multimap`, `#!cpp std::unordered_multimap` | not usable; `emplace` does not return `#!cpp std::pair<iterator, bool>` |
|
||||
|
||||
## `ArrayType`
|
||||
|
||||
`ArrayType` is instantiated as
|
||||
@@ -171,6 +178,14 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
usable when wrapped in a type that adds a `capacity()` member function; `#!cpp std::list` additionally lacks
|
||||
`operator[]` and random-access iterators and cannot be used at all.
|
||||
|
||||
### Compatible containers
|
||||
|
||||
| Container | Support |
|
||||
|-------------------------------|----------------------------------------------------------------------------------|
|
||||
| `#!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 |
|
||||
|
||||
## `StringType`
|
||||
|
||||
`StringType` is used **both** for JSON string values and for the keys of JSON objects
|
||||
@@ -220,6 +235,25 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
The unit test `tests/src/unit-alt-string.cpp` contains `alt_string`, a minimal string type that satisfies the
|
||||
requirements needed for the tested subset of the API. It is a good starting point for a custom `StringType`.
|
||||
|
||||
### Compatible types
|
||||
|
||||
| Type | Support |
|
||||
|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------|
|
||||
| `#!cpp std::string` (default) | full |
|
||||
| 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 |
|
||||
|
||||
!!! warning "`std::basic_string` with a non-default allocator"
|
||||
|
||||
In several places the library builds a `#!cpp std::string` and assigns it to a `string_t`: `int_to_string()` (used
|
||||
by [`flatten`](../../api/basic_json/flatten.md) and [`diff`](../../api/basic_json/diff.md)) and the UBJSON
|
||||
high-precision number reader, which every binary reader instantiates. A `#!cpp std::basic_string` with a different
|
||||
allocator is not assignable from `#!cpp std::string`, and because such a type lives in namespace `std`, the ADL
|
||||
customization point `int_to_string()` cannot be provided for it either. Only the DOM,
|
||||
[`dump`](../../api/basic_json/dump.md), and [`parse`](../../api/basic_json/parse.md) compile with such a type.
|
||||
Wrap it in a class of your own namespace if you need a custom allocator.
|
||||
|
||||
## `BooleanType`
|
||||
|
||||
`boolean_t` is stored **directly** inside `basic_json`, as a member of an anonymous union.
|
||||
@@ -235,6 +269,12 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
|
||||
There is little reason to use anything other than `#!cpp bool` here.
|
||||
|
||||
### Compatible types
|
||||
|
||||
`#!cpp bool` is the only meaningful choice. Other trivially copyable types that convert to and from `#!cpp bool` (for
|
||||
example `#!cpp std::uint8_t`) do compile and behave correctly, but they gain nothing and make the
|
||||
[`get`](../../api/basic_json/get.md) overloads harder to reason about.
|
||||
|
||||
## `NumberIntegerType` and `NumberUnsignedType`
|
||||
|
||||
Both types are stored **directly** inside `basic_json`'s union.
|
||||
@@ -259,6 +299,15 @@ is stored as [`number_float_t`](../../api/basic_json/number_float_t.md) instead.
|
||||
therefore silently changes parse results rather than raising an error. See
|
||||
[Number Handling](number_handling.md) for details.
|
||||
|
||||
### Compatible types
|
||||
|
||||
| Type pair | Support |
|
||||
|----------------------------------------------------------------------------------------------|---------------------------------------------------------------------|
|
||||
| `#!cpp std::int64_t` / `#!cpp std::uint64_t` (default) | full |
|
||||
| `#!cpp std::int32_t` / `#!cpp std::uint32_t`, `#!cpp long long` / `#!cpp unsigned long long` | full; narrower types change which literals the parser can represent |
|
||||
| any other pair of standard signed/unsigned integer types | full |
|
||||
| class types, `#!cpp bool`, enumerations | not usable; `#!cpp std::is_integral` must hold |
|
||||
|
||||
## `NumberFloatType`
|
||||
|
||||
`number_float_t` is stored **directly** inside `basic_json`'s union.
|
||||
@@ -283,6 +332,21 @@ If `#!cpp std::numeric_limits<NumberFloatType>` describes an IEEE 754 binary32 o
|
||||
Grisu2 algorithm, which produces the shortest representation that round-trips. Otherwise the `snprintf` fallback with
|
||||
`max_digits10` digits is used.
|
||||
|
||||
### Required for the binary formats
|
||||
|
||||
`NumberFloatType` must be `#!cpp float` or `#!cpp double`. The writers for
|
||||
[CBOR, MessagePack, UBJSON, BJData, and BSON](../binary_formats/index.md) map a floating-point value onto an IEEE 754
|
||||
binary32 or binary64 field and have no encoding for `#!cpp long double`.
|
||||
|
||||
### Compatible types
|
||||
|
||||
| Type | Support |
|
||||
|-----------------------------|-----------------------------------------------------------------------------------------------------|
|
||||
| `#!cpp double` (default) | full; short round-trip output through Grisu2 |
|
||||
| `#!cpp float` | full; short round-trip output through Grisu2 |
|
||||
| `#!cpp long double` | `dump` and `parse` only; the binary format writers do not compile, as they only handle IEEE 754 binary32 and binary64 |
|
||||
| any other type | not usable |
|
||||
|
||||
## `AllocatorType`
|
||||
|
||||
`AllocatorType` is instantiated with **one** argument, for each of `object_t`, `array_t`, `string_t`, `binary_t`,
|
||||
@@ -304,6 +368,14 @@ Grisu2 algorithm, which produces the shortest representation that round-trips. O
|
||||
[`basic_json::pointer`](../../api/basic_json/index.md#container-types), and iterators are constructed from raw
|
||||
`#!cpp basic_json*` values. The `pointer` type must therefore be a plain pointer; fancy pointers are not supported.
|
||||
|
||||
### Compatible types
|
||||
|
||||
| Type | Support |
|
||||
|-----------------------------------------------------------------|----------------------------------------------------|
|
||||
| `#!cpp std::allocator` (default) | full |
|
||||
| a custom stateless allocator template | full |
|
||||
| stateful allocators, e.g. `#!cpp std::pmr::polymorphic_allocator`| not usable; see the requirements above |
|
||||
|
||||
## `JSONSerializer`
|
||||
|
||||
`JSONSerializer` is instantiated as `JSONSerializer<T, void>` and defaults to
|
||||
@@ -323,6 +395,14 @@ Grisu2 algorithm, which produces the shortest representation that round-trips. O
|
||||
specializations, `to_json` must be available for `boolean_t`, `number_integer_t`, `number_unsigned_t`,
|
||||
`number_float_t`, `string_t`, `object_t`, `array_t`, and `binary_t` of the *source* specialization.
|
||||
|
||||
### Compatible types
|
||||
|
||||
| Type | Support |
|
||||
|-------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||
| [`nlohmann::adl_serializer`](../../api/adl_serializer/index.md) (default) | full |
|
||||
| a class template deriving from `adl_serializer` | full; the usual way to change behavior while keeping the defaults |
|
||||
| an unrelated template with the same interface | full, but it has to handle every type the library converts |
|
||||
|
||||
## `BinaryType`
|
||||
|
||||
`BinaryType` is not a JSON type; it is used for the byte strings of the
|
||||
@@ -351,6 +431,16 @@ using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
|
||||
See [`binary_t`](../../api/basic_json/binary_t.md) for how a non-default `BinaryType` changes the meaning of assigning
|
||||
such a container to a `basic_json` value.
|
||||
|
||||
### Compatible containers
|
||||
|
||||
| Container | Support |
|
||||
|------------------------------------------|-----------------------------------------------------------------------------------------------|
|
||||
| `#!cpp std::vector<std::uint8_t>` (default) | full |
|
||||
| `#!cpp std::vector<char>` | full |
|
||||
| `#!cpp std::vector<std::byte>` | assignment, [`get`](../../api/basic_json/get.md), and the binary formats work, but [`dump`](../../api/basic_json/dump.md) and [`std::hash<basic_json>`](../../api/basic_json/std_hash.md) do not compile |
|
||||
| `#!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 |
|
||||
|
||||
## `CustomBaseClass`
|
||||
|
||||
`CustomBaseClass` is an extension point: unless it is `#!cpp void` (the default, which selects the empty
|
||||
@@ -372,6 +462,13 @@ argument-dependent lookup.
|
||||
|
||||
See [`json_base_class_t`](../../api/basic_json/json_base_class_t.md) for an example.
|
||||
|
||||
### Compatible types
|
||||
|
||||
| Type | Support |
|
||||
|----------------------------------------------------------|--------------------------------------------------------------|
|
||||
| `#!cpp void` (default) | an empty base class is used; no effect on `basic_json` |
|
||||
| any default-constructible, non-`final` class | full; see [`json_base_class_t`](../../api/basic_json/json_base_class_t.md) |
|
||||
|
||||
## Cross-specialization conversions
|
||||
|
||||
Converting a value from one `basic_json` specialization into another (see the
|
||||
|
||||
Reference in New Issue
Block a user