Compare commits

..
Author SHA1 Message Date
Niels Lohmann f8a0990582 Document zero-member support in the macro API reference
docs/mkdocs/docs/features/arbitrary_types.md already gained a note, but
the three api/macros pages are where the parameter contract is actually
specified and they still described member as a non-empty list.

State that the list may be empty on each page, and add a note showing
what the zero-member case generates: an empty JSON object for the plain
macros, and base-type-only serialization for the derived ones. Both notes
record that the WITH_NAMES variants do not support this.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-20 09:01:11 +02:00
Niels Lohmann 7e87e5920f Name the zero-member macro bodies by intent, not argument count
The dispatch tag was the literal token 1 or N, pasted onto a macro prefix
to select the zero-member or member-carrying body. For the derived-type
macros that reads wrong: their tag is computed after dropping the leading
Type, so the zero-member body was named _1 while taking two parameters
(Type, BaseType).

Emit EMPTY and MEMBERS instead. The mechanism is unchanged -- the tag is
still a token pasted onto the prefix by NLOHMANN_JSON_CAT -- but the body
names now say what they are rather than encoding an argument count that
only lines up for half of the macros.

Collapse the four duplicated zero-member bodies while here: with no
members there is nothing to default, so each _WITH_DEFAULT_EMPTY body was
a byte-for-byte copy of its plain counterpart. They are now one-line
aliases, leaving a single definition of what an empty object serializes
to per intrusive/non-intrusive and base/derived combination.

No functional change: for both zero-member and member-carrying types the
preprocessed to_json/from_json output is token-for-token identical, and
the arity limits are unchanged (63 members, base and derived).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-19 23:05:45 +02:00
Niels Lohmann 0b28c63bad Fix derived-type macro dispatch capping members at 62 instead of 63
NLOHMANN_JSON_GET_MACRO resolves 64 positional arguments, with NAME at
position 65. NLOHMANN_JSON_TYPE_TAG dispatches on Type plus the member
list, so it resolves correctly up to the 63 members NLOHMANN_JSON_PASTE
supports. NLOHMANN_JSON_DERIVED_TYPE_TAG dispatched on the two-token
Type,BaseType prefix plus the member list, running out one slot early:
at 63 members, position 65 landed on the last member name instead of a
sentinel and NLOHMANN_JSON_CAT built an undefined identifier such as
NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_m63, with the compiler
reporting "unknown type name 'm1'" once per member and nothing pointing
at an argument-count limit.

That silently reduced all six NLOHMANN_DEFINE_DERIVED_TYPE_* macros from
63 members to 62, contradicting the "up to 63 members" contract in
docs/mkdocs/docs/api/macros/nlohmann_define_derived_type.md.

Drop the leading Type and defer to NLOHMANN_JSON_TYPE_TAG so the tag is
computed from BaseType plus the member list, which fits the available
slots. The zero-own-member derived bodies are therefore selected by tag
1 rather than 2, and the sentinel table for the derived tag is no longer
needed.

Add a regression test at the documented maximum for both the plain and
the derived macros; it fails to compile against the previous dispatch.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-19 21:03:08 +02:00
Niels LohmannandClaude 4dcd45b26d Fix clang-tidy misc-const-correctness in unit-udt_macro.cpp
The four zero-member ONLY_SERIALIZE test objects are only ever read
(via to_json), never mutated, so mark them const per clang-tidy.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-12 06:37:49 +00:00
Niels Lohmann 9d83673263 Fix CI failures in zero-member NLOHMANN_DEFINE_TYPE_* macros
Three issues surfaced on PR #5272's real CI that weren't caught by
local testing against a narrower flag set:

- GCC -Werror=noexcept: the four truly-empty from_json bodies (plain
  INTRUSIVE/NON_INTRUSIVE, with and without _WITH_DEFAULT) provably
  never throw but weren't declared noexcept; mark them noexcept
  explicitly. to_json and the derived-type from_json overloads are
  left alone since they genuinely can throw (object assignment /
  delegating to the base class's from_json).
- clang-tidy bugprone-macro-parentheses: false positive on the same
  8 zero-member bodies (Type/BaseType used purely as declarator
  types); suppressed with NOLINTNEXTLINE comments in the same style
  already used elsewhere in this file (see NLOHMANN_JSON_SERIALIZE_ENUM).
- MSVC's traditional preprocessor doesn't fully expand
  NLOHMANN_JSON_CAT(prefix, NLOHMANN_JSON_TYPE_TAG(...))(...) in one
  pass, which broke a pre-existing one-member usage in
  unit-regression2.cpp with syntax errors. Wrap all 12 public
  dispatcher macros in an extra outer NLOHMANN_JSON_EXPAND(...),
  matching the pattern NLOHMANN_JSON_PASTE already uses for the same
  MSVC quirk.

Re-verified against real GCC 16 and Clang at -std=c++11/14/17/20 with
-pedantic -Werror -Wvariadic-macros -Wnoexcept, including the exact
files that failed in CI (unit-udt_macro.cpp, unit-regression2.cpp),
against both the modular headers and the re-amalgamated single header.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 23:55:43 +02:00
Niels Lohmann d969eef216 Support zero-member types in NLOHMANN_DEFINE_TYPE_* macros (#4041)
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type) and its 11 sibling macros produced
broken code for types with no members to serialize. Invoking a variadic
macro so __VA_ARGS__ is empty is only standard-conforming since C++20,
so a plain __VA_OPT__ fix (as tried in #5142) breaks every pre-C++20
build under -pedantic. Instead, make all 12 macros purely variadic and
dispatch on argument count using a sentinel-padded extension of the
existing NLOHMANN_JSON_GET_MACRO idiom, giving full C++11-C++26 support
with no feature-test gate.

Verified against real GCC 16 and Clang at -std=c++11/14/17/20 with
-pedantic -Werror -Wvariadic-macros: zero regressions in the existing
unit-udt_macro.cpp suite plus 12 new zero-member test cases.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 19:55:56 +02:00
130 changed files with 605 additions and 72290 deletions
-81
View File
@@ -1,81 +0,0 @@
name: "Check API documentation"
on:
pull_request:
permissions:
contents: read
jobs:
check_api_docs:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout pull request
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install clang
# Used only as a subprocess for `clang++ -E -v` system-include-path discovery in
# extract_api.py; it does not need to version-match the pinned libclang pip wheel
# below, which does the actual AST parsing. Do not "fix" this to be version-matched.
run: sudo apt-get update && sudo apt-get install -y clang
- name: Install Python dependencies
run: pip install -r tools/api_checker/requirements.txt
- name: Extract API and regenerate the committed surface file
run: |
python3 tools/api_checker/extract_api.py \
--header include/nlohmann/json.hpp \
--include include \
--output /tmp/api_snapshot.json \
--surface-output tools/api_checker/api_surface.json
- name: "Check API documentation (Phase 1: advisory)"
# Surfaces missing/broken @sa links without failing the job while the backlog from the
# initial AST-based rollout is burned down. See tools/api_checker/POLICY.md and the PR
# that introduced this workflow for the two-phase rollout plan.
continue-on-error: true
run: |
python3 tools/api_checker/check_docs.py \
--snapshot /tmp/api_snapshot.json
- name: Check macro documentation (advisory only)
# Cross-checks docs/mkdocs/docs/api/macros/ pages against #define sites. Only checks the
# documented-macro-still-exists direction; never blocks CI. See POLICY.md.
run: python3 tools/api_checker/check_macros.py
- name: Check for uncommitted API surface changes
id: diff
run: |
mkdir -p ${{ github.workspace }}/patch
git diff --patch --no-color -- tools/api_checker/api_surface.json > ${{ github.workspace }}/patch/api_surface.patch
if [ -s ${{ github.workspace }}/patch/api_surface.patch ]; then
echo "tools/api_checker/api_surface.json is out of date. Diff:"
cat ${{ github.workspace }}/patch/api_surface.patch
echo "has_diff=true" >> "$GITHUB_OUTPUT"
else
echo "has_diff=false" >> "$GITHUB_OUTPUT"
fi
# Uploaded so contributors can fix their PR with `git apply api_surface.patch`
# instead of installing libclang locally.
- name: Upload patch
if: steps.diff.outputs.has_diff == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: api-surface-patch
path: patch/api_surface.patch
- name: Fail if API surface file is not up to date
# Unlike the doc-backlog check above, this is purely mechanical regeneration with no
# backlog to phase in -- blocking from the start, matching check_amalgamation.yml's
# precedent. Contributors who add/remove/rename public API must regenerate and commit
# tools/api_checker/api_surface.json as part of their PR.
if: steps.diff.outputs.has_diff == 'true'
run: exit 1
-5
View File
@@ -3,7 +3,6 @@
*.gcno
*.gcda
.DS_Store
__pycache__/
/.idea
/cmake-build-*
@@ -44,9 +43,5 @@ venv
nlohmann_json.spdx
# api_checker: ephemeral, location/doc-status-sensitive working file (not the committed
# release-tracking artifact -- see tools/api_checker/api_surface.json for that)
/tools/api_checker/api_snapshot.json
# Bazel-related
MODULE.bazel.lock
+1 -1
View File
@@ -106,7 +106,7 @@ Thanks everyone!
:books: If you want to **learn more** about how to use the library, check out the rest of the [**README**](#examples), have a look at [**code examples**](https://github.com/nlohmann/json/tree/develop/docs/mkdocs/docs/examples), or browse through the [**help pages**](https://json.nlohmann.me).
:construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/) or have a look at the [quick reference](#quick-reference) below. The public API surface is derived mechanically and checked for documentation coverage by the tooling in [`tools/api_checker/`](tools/api_checker/), whose [POLICY.md](tools/api_checker/POLICY.md) defines what counts as public API and what stability is guaranteed.
:construction: If you want to understand the **API** better, check out the [**API Reference**](https://json.nlohmann.me/api/basic_json/) or have a look at the [quick reference](#quick-reference) below.
:bug: If you found a **bug**, please check the [**FAQ**](https://json.nlohmann.me/home/faq/) if it is a known issue or the result of a design decision. Please also have a look at the [**issue list**](https://github.com/nlohmann/json/issues) before you [**create a new issue**](https://github.com/nlohmann/json/issues/new/choose). Please provide as much information as possible to help us understand and reproduce your issue.
@@ -420,9 +420,7 @@ basic_json(basic_json&& other) noexcept;
1. Since version 1.0.0.
2. Since version 1.0.0.
3. Since version 2.1.0.
4. Since version 3.2.0. Also initializes the position reported by
[`start_pos()`](start_pos.md)/[`end_pos()`](end_pos.md) from `val` when
[`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) is enabled, since version 3.12.0.
4. Since version 3.2.0.
5. Since version 1.0.0.
6. Since version 1.0.0.
7. Since version 1.0.0.
@@ -1,38 +0,0 @@
# <small>nlohmann::basic_json::</small>bjdata_version_t
```cpp
enum class bjdata_version_t
{
draft2,
draft3,
};
```
This enumeration is used in the [`to_bjdata`](to_bjdata.md) function to choose which draft version of
the BJData specification to encode ND-array extensions for:
draft2
: encode using the BJData Draft 2 ND-array format
draft3
: encode using the BJData Draft 3 ND-array format
## Examples
??? example
The example shows how `bjdata_version_t` selects the BJData draft used by `to_bjdata`.
```cpp
--8<-- "examples/bjdata_version_t.cpp"
```
Output:
```
--8<-- "examples/bjdata_version_t.output"
```
## Version history
- Added in version 3.12.0.
@@ -1,32 +0,0 @@
# <small>nlohmann::basic_json::</small>initializer_list_t
```cpp
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
```
The type used for the initializer-list [constructor](basic_json.md) (overload 5) and for functions
such as [`operator=`](operator=.md) that accept a braced-init-list of JSON values. Each element wraps a
`basic_json` value or something convertible to one, deferring the decision of whether the list should be
parsed as a JSON array or a JSON object to the constructor itself.
See the [constructor](basic_json.md) documentation for how `initializer_list_t` values are interpreted.
## Examples
??? example
The example shows how an `initializer_list_t` is used to construct a JSON value.
```cpp
--8<-- "examples/initializer_list_t.cpp"
```
Output:
```
--8<-- "examples/initializer_list_t.output"
```
## Version history
- Since version 1.0.0.
@@ -1,31 +0,0 @@
# <small>nlohmann::basic_json::</small>json_sax_t
```cpp
using json_sax_t = json_sax<basic_json>;
```
The [`json_sax`](../json_sax/index.md) interface bound to this `basic_json` specialization, i.e. with
`BasicJsonType` fixed to `basic_json`. Used as the SAX interface type by [`sax_parse`](sax_parse.md) and
other SAX-based parsing functions.
See [`nlohmann::json_sax`](../json_sax/index.md) for more information.
## Examples
??? example
The example shows the type `json_sax_t`.
```cpp
--8<-- "examples/json_sax_t.cpp"
```
Output:
```
--8<-- "examples/json_sax_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -51,5 +51,3 @@ Linear.
## Version history
- Added in version 1.0.0.
- The `noexcept` specification was extended to also depend on
[`json_base_class_t`](json_base_class_t.md)'s move-assignment in version 3.11.3.
@@ -85,8 +85,3 @@ Linear in the size of the JSON value.
- Since version 1.0.0.
- Macros `JSON_EXPLICIT`/[`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) added
in version 3.9.0.
- The exclusion of `std::any` from this conversion became conditional on
[`JSON_HAS_STATIC_RTTI`](../macros/json_has_static_rtti.md) in version 3.11.3.
- `std::optional<T>` excluded from this conversion in version 3.13.0; use
[`get<std::optional<T>>()`](get.md)/[`get_to()`](get_to.md) instead (see
[Converting values](../../features/conversions.md)).
@@ -1,32 +0,0 @@
# <small>nlohmann::byte_container_with_subtype::</small>container_type
```cpp
using container_type = BinaryType;
```
The type of the underlying binary container, forwarded from the `BinaryType` template parameter that
`byte_container_with_subtype` is instantiated with. `byte_container_with_subtype` publicly inherits from
`container_type`.
See [`basic_json::binary_t`](../basic_json/binary_t.md) for the type typically used to instantiate
`BinaryType`.
## Examples
??? example
The example shows the type `container_type`.
```cpp
--8<-- "examples/byte_container_with_subtype__container_type.cpp"
```
Output:
```
--8<-- "examples/byte_container_with_subtype__container_type.output"
```
## Version history
- Since version 3.8.0.
@@ -1,45 +0,0 @@
# <small>nlohmann::byte_container_with_subtype::</small>operator==
```cpp
bool operator==(const byte_container_with_subtype& rhs) const;
```
Compares two `byte_container_with_subtype` values for equality by comparing the underlying binary
container, the subtype, and whether a subtype is set.
## Parameters
`rhs` (in)
: value to compare `*this` against
## Return value
whether `*this` and `rhs` are equal
## Exception safety
No-throw guarantee: this function never throws exceptions.
## Complexity
Linear in the size of the underlying binary container.
## Examples
??? example
The example demonstrates comparing `byte_container_with_subtype` values.
```cpp
--8<-- "examples/byte_container_with_subtype__operator_eq.cpp"
```
Output:
```
--8<-- "examples/byte_container_with_subtype__operator_eq.output"
```
## Version history
- Since version 3.8.0.
@@ -1,45 +0,0 @@
# <small>nlohmann::byte_container_with_subtype::</small>operator!=
```cpp
bool operator!=(const byte_container_with_subtype& rhs) const;
```
Compares two `byte_container_with_subtype` values for inequality. Implemented as the negation of
[`operator==`](operator_eq.md).
## Parameters
`rhs` (in)
: value to compare `*this` against
## Return value
whether `*this` and `rhs` are not equal
## Exception safety
No-throw guarantee: this function never throws exceptions.
## Complexity
Linear in the size of the underlying binary container.
## Examples
??? example
The example demonstrates comparing `byte_container_with_subtype` values.
```cpp
--8<-- "examples/byte_container_with_subtype__operator_ne.cpp"
```
Output:
```
--8<-- "examples/byte_container_with_subtype__operator_ne.output"
```
## Version history
- Since version 3.8.0.
@@ -1,28 +0,0 @@
# <small>nlohmann::byte_container_with_subtype::</small>subtype_type
```cpp
using subtype_type = std::uint64_t;
```
The type used to store the optional binary subtype tag. See [`subtype`](subtype.md) and
[`set_subtype`](set_subtype.md).
## Examples
??? example
The example shows the type `subtype_type`.
```cpp
--8<-- "examples/byte_container_with_subtype__subtype_type.cpp"
```
Output:
```
--8<-- "examples/byte_container_with_subtype__subtype_type.output"
```
## Version history
- Since version 3.8.0.
-30
View File
@@ -1,30 +0,0 @@
# <small>nlohmann::json_sax::</small>binary_t
```cpp
using binary_t = typename BasicJsonType::binary_t;
```
The type used by the [`binary`](binary.md) callback for JSON binary values, forwarded from the
`BasicJsonType` template parameter.
See [`basic_json::binary_t`](../basic_json/binary_t.md) for more information.
## Examples
??? example
The example shows the type `binary_t` and its relation to `basic_json::binary_t`.
```cpp
--8<-- "examples/json_sax__binary_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__binary_t.output"
```
## Version history
- Added in version 3.8.0.
-33
View File
@@ -1,33 +0,0 @@
# <small>nlohmann::json_sax::</small>json_sax
```cpp
// (1)
json_sax() = default;
// (2)
json_sax(const json_sax&) = default;
// (3)
json_sax(json_sax&&) noexcept = default;
```
1. Default constructor.
2. Copy constructor.
3. Move constructor.
`json_sax` is a pure abstract base class with no data members of its own, so all three constructors are
defaulted and only exist to make derived SAX consumers explicitly copyable/movable.
## Exception safety
No-throw guarantee: none of these constructors throw exceptions.
## Complexity
Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.2.0.
@@ -1,30 +0,0 @@
# <small>nlohmann::json_sax::</small>number_float_t
```cpp
using number_float_t = typename BasicJsonType::number_float_t;
```
The type used by the [`number_float`](number_float.md) callback for JSON floating-point numbers,
forwarded from the `BasicJsonType` template parameter.
See [`basic_json::number_float_t`](../basic_json/number_float_t.md) for more information.
## Examples
??? example
The example shows the type `number_float_t` and its relation to `basic_json::number_float_t`.
```cpp
--8<-- "examples/json_sax__number_float_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__number_float_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -1,30 +0,0 @@
# <small>nlohmann::json_sax::</small>number_integer_t
```cpp
using number_integer_t = typename BasicJsonType::number_integer_t;
```
The type used by the [`number_integer`](number_integer.md) callback for JSON integer numbers, forwarded
from the `BasicJsonType` template parameter.
See [`basic_json::number_integer_t`](../basic_json/number_integer_t.md) for more information.
## Examples
??? example
The example shows the type `number_integer_t` and its relation to `basic_json::number_integer_t`.
```cpp
--8<-- "examples/json_sax__number_integer_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__number_integer_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -1,30 +0,0 @@
# <small>nlohmann::json_sax::</small>number_unsigned_t
```cpp
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
```
The type used by the [`number_unsigned`](number_unsigned.md) callback for JSON unsigned integer numbers,
forwarded from the `BasicJsonType` template parameter.
See [`basic_json::number_unsigned_t`](../basic_json/number_unsigned_t.md) for more information.
## Examples
??? example
The example shows the type `number_unsigned_t` and its relation to `basic_json::number_unsigned_t`.
```cpp
--8<-- "examples/json_sax__number_unsigned_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__number_unsigned_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -1,29 +0,0 @@
# <small>nlohmann::json_sax::</small>operator=
```cpp
// (1)
json_sax& operator=(const json_sax&) = default;
// (2)
json_sax& operator=(json_sax&&) noexcept = default;
```
1. Copy assignment operator.
2. Move assignment operator.
`json_sax` is a pure abstract base class with no data members of its own, so both assignment operators
are defaulted and only exist to make derived SAX consumers explicitly copy-/move-assignable.
## Exception safety
No-throw guarantee: neither operator throws exceptions.
## Complexity
Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.2.0.
-30
View File
@@ -1,30 +0,0 @@
# <small>nlohmann::json_sax::</small>string_t
```cpp
using string_t = typename BasicJsonType::string_t;
```
The type used by the [`string`](string.md) and [`key`](key.md) callbacks for JSON strings and object
keys, forwarded from the `BasicJsonType` template parameter.
See [`basic_json::string_t`](../basic_json/string_t.md) for more information.
## Examples
??? example
The example shows the type `string_t` and its relation to `basic_json::string_t`.
```cpp
--8<-- "examples/json_sax__string_t.cpp"
```
Output:
```
--8<-- "examples/json_sax__string_t.output"
```
## Version history
- Added in version 3.2.0.
@@ -1,22 +0,0 @@
# <small>nlohmann::json_sax::</small>~json_sax
```cpp
virtual ~json_sax() = default;
```
Destructor. Virtual to allow proper destruction of derived SAX consumer classes through a
pointer/reference to `json_sax`.
## Exception safety
No-throw guarantee: this destructor never throws exceptions.
## Complexity
Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.2.0.
@@ -57,7 +57,8 @@ Summary:
: name of the base type (class, struct) `type` is derived from
`member` (in)
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated
list, which may also be empty
## Default definition
@@ -127,6 +128,20 @@ void to_json(BasicJsonType& j, const B& b) {
- Macros 4, 5, and 6 have the same prerequisites of [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE](nlohmann_define_type_non_intrusive.md).
- Serialization/deserialization of base types must be defined.
!!! info "Derived types without own members"
The member list may be empty. The macro then generates a `to_json`/`from_json` pair that only delegates to
the base type, so `type` serializes exactly like `base_type`:
```cpp
struct derived : base
{
NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(derived, base)
};
```
The `WITH_NAMES` variants do not support this.
!!! warning "Implementation limits"
See Implementation limits for [NLOHMANN_DEFINE_TYPE_INTRUSIVE](nlohmann_define_type_intrusive.md) and
@@ -33,7 +33,8 @@ Summary:
: name of the type (class, struct) to serialize/deserialize
`member` (in)
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated
list, which may also be empty
## Default definition
@@ -58,6 +59,20 @@ See the examples below for the concrete generated code.
[GetNonDefNonCopy]: ../../features/arbitrary_types.md#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
!!! info "Types without members"
The member list may be empty. The macro then generates a `to_json` that produces an empty JSON object
`#!json {}`, and a `from_json` that reads no members:
```cpp
struct marker
{
NLOHMANN_DEFINE_TYPE_INTRUSIVE(marker)
};
```
The `WITH_NAMES` variants do not support this.
!!! warning "Implementation limits"
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
@@ -33,7 +33,8 @@ Summary:
: name of the type (class, struct) to serialize/deserialize
`member` (in)
: name of the (public) member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
: name of the (public) member variable to serialize/deserialize; up to 63 members can be given as a
comma-separated list, which may also be empty
## Default definition
@@ -59,6 +60,18 @@ See the examples below for the concrete generated code.
[GetNonDefNonCopy]: ../../features/arbitrary_types.md#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
!!! info "Types without members"
The member list may be empty. The macro then generates a `to_json` that produces an empty JSON object
`#!json {}`, and a `from_json` that reads no members:
```cpp
struct marker {};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(marker)
```
The `WITH_NAMES` variants do not support this.
!!! warning "Implementation limits"
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
+2 -2
View File
@@ -8,7 +8,7 @@ This type preserves the insertion order of object keys.
## Iterator invalidation
The type is based on [`ordered_map`](ordered_map/index.md) which in turn uses a `std::vector` to store object elements.
The type is based on [`ordered_map`](ordered_map.md) which in turn uses a `std::vector` to store object elements.
Therefore, adding object elements can yield a reallocation in which case all iterators (including the
[`end()`](basic_json/end.md) iterator) and all references to the elements are invalidated. Also, any iterator or
reference after the insertion point will point to the same index, which is now a different value.
@@ -31,7 +31,7 @@ reference after the insertion point will point to the same index, which is now a
## See also
- [ordered_map](ordered_map/index.md)
- [ordered_map](ordered_map.md)
- [Object Order](../features/object_order.md)
## Version history
@@ -6,7 +6,7 @@ template<class Key, class T, class IgnoredLess = std::less<Key>,
struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>;
```
A minimal map-like container that preserves insertion order for use within [`nlohmann::ordered_json`](../ordered_json.md)
A minimal map-like container that preserves insertion order for use within [`nlohmann::ordered_json`](ordered_json.md)
(`nlohmann::basic_json<ordered_map>`).
## Template parameters
@@ -32,12 +32,12 @@ case all iterators (including the `end()` iterator) and all references to the el
- **key_type** - key type (`Key`)
- **mapped_type** - mapped type (`T`)
- [**Container**](Container.md) - base container type (`#!cpp std::vector<std::pair<const Key, T>, Allocator>`)
- **Container** - base container type (`#!cpp std::vector<std::pair<const Key, T>, Allocator>`)
- **iterator**
- **const_iterator**
- **size_type**
- **value_type**
- [**key_compare**](key_compare.md) - key comparison function
- **key_compare** - key comparison function
```cpp
std::equal_to<Key> // until C++14
@@ -46,16 +46,15 @@ std::equal_to<> // since C++14
## Member functions
- [(constructor)](ordered_map.md)
- [(destructor)](~ordered_map.md)
- [**operator=**](operator=.md)
- [**emplace**](emplace.md)
- [**operator\[\]**](operator[].md)
- [**at**](at.md)
- [**erase**](erase.md)
- [**count**](count.md)
- [**find**](find.md)
- [**insert**](insert.md)
- (constructor)
- (destructor)
- **emplace**
- **operator\[\]**
- **at**
- **erase**
- **count**
- **find**
- **insert**
## Examples
@@ -75,9 +74,9 @@ std::equal_to<> // since C++14
## See also
- [ordered_json](../ordered_json.md)
- [ordered_json](ordered_json.md)
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](ordered_json.md).
- Added **key_compare** member in version 3.11.0.
@@ -1,28 +0,0 @@
# <small>nlohmann::ordered_map::</small>Container
```cpp
using Container = std::vector<std::pair<const Key, T>, Allocator>;
```
The base container type that `ordered_map` publicly inherits from. Elements are stored in insertion
order as `#!cpp std::pair<const Key, T>` entries in a `std::vector`.
## Examples
??? example
The example shows the type `Container`.
```cpp
--8<-- "examples/ordered_map__Container.cpp"
```
Output:
```
--8<-- "examples/ordered_map__Container.output"
```
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
-61
View File
@@ -1,61 +0,0 @@
# <small>nlohmann::ordered_map::</small>at
```cpp
// (1)
T& at(const key_type& key);
const T& at(const key_type& key) const;
// (2)
template<class KeyType>
T& at(KeyType&& key);
template<class KeyType>
const T& at(KeyType&& key) const;
```
1. Returns a reference to the value mapped to `key`.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup, e.g. looking up by a `#!cpp const char*` without constructing a temporary
`key_type`). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to find
## Return value
reference to the mapped value of the element with key equal to `key`
## Exceptions
Throws `std::out_of_range` if no element with key `key` exists.
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `at` is used.
```cpp
--8<-- "examples/ordered_map__at.cpp"
```
Output:
```
--8<-- "examples/ordered_map__at.output"
```
## Version history
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
-53
View File
@@ -1,53 +0,0 @@
# <small>nlohmann::ordered_map::</small>count
```cpp
// (1)
size_type count(const key_type& key) const;
// (2)
template<class KeyType>
size_type count(KeyType&& key) const;
```
1. Returns the number of elements with key equal to `key` (0 or 1, since keys are unique).
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the elements to count
## Return value
number of elements with key equal to `key` (0 or 1)
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `count` is used.
```cpp
--8<-- "examples/ordered_map__count.cpp"
```
Output:
```
--8<-- "examples/ordered_map__count.output"
```
## Version history
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
@@ -1,58 +0,0 @@
# <small>nlohmann::ordered_map::</small>emplace
```cpp
// (1)
std::pair<iterator, bool> emplace(const key_type& key, T&& t);
// (2)
template<class KeyType>
std::pair<iterator, bool> emplace(KeyType&& key, T&& t);
```
1. Inserts `#!cpp {key, t}` if no element with an equal key already exists (per [`key_compare`](key_compare.md)),
appending it at the end to preserve insertion order. If an equal key already exists, does nothing.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to insert
`t` (in)
: value of the element to insert
## Return value
pair of an iterator to the (possibly newly inserted) element, and a `bool` that is `true` if insertion
took place and `false` if an element with an equal key already existed
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `emplace` is used.
```cpp
--8<-- "examples/ordered_map__emplace.cpp"
```
Output:
```
--8<-- "examples/ordered_map__emplace.output"
```
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
-75
View File
@@ -1,75 +0,0 @@
# <small>nlohmann::ordered_map::</small>erase
```cpp
// (1)
size_type erase(const key_type& key);
// (2)
template<class KeyType>
size_type erase(KeyType&& key);
// (3)
iterator erase(iterator pos);
// (4)
iterator erase(iterator first, iterator last);
```
1. Removes the element with key equal to `key`, if any, preserving the relative order of the remaining
elements.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
3. Removes the element at `pos`.
4. Removes the elements in range `[first, last)`.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to remove
`pos` (in)
: iterator to the element to remove
`first` (in)
: iterator to the first element to remove
`last` (in)
: iterator one past the last element to remove
## Return value
1. number of elements removed (0 or 1)
2. number of elements removed (0 or 1)
3. iterator following the removed element
4. iterator following the last removed element
## Complexity
Linear in the number of elements (elements after the removed one(s) are shifted to keep storage
contiguous).
## Examples
??? example
The example shows how `erase` is used.
```cpp
--8<-- "examples/ordered_map__erase.cpp"
```
Output:
```
--8<-- "examples/ordered_map__erase.output"
```
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
-56
View File
@@ -1,56 +0,0 @@
# <small>nlohmann::ordered_map::</small>find
```cpp
// (1)
iterator find(const key_type& key);
const_iterator find(const key_type& key) const;
// (2)
template<class KeyType>
iterator find(KeyType&& key);
template<class KeyType>
const_iterator find(KeyType&& key) const;
```
1. Returns an iterator to the element with key equal to `key`, or `end()` if no such element exists.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to find
## Return value
iterator to the element with key equal to `key`, or `end()` if not found
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `find` is used.
```cpp
--8<-- "examples/ordered_map__find.cpp"
```
Output:
```
--8<-- "examples/ordered_map__find.output"
```
## Version history
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
@@ -1,63 +0,0 @@
# <small>nlohmann::ordered_map::</small>insert
```cpp
// (1)
std::pair<iterator, bool> insert(value_type&& value);
std::pair<iterator, bool> insert(const value_type& value);
// (2)
template<typename InputIt>
void insert(InputIt first, InputIt last);
```
1. Inserts `value` if no element with an equal key already exists (per [`key_compare`](key_compare.md)),
appending it at the end to preserve insertion order. If an equal key already exists, does nothing.
2. Inserts the elements from range `[first, last)`, in iteration order, applying the same equal-key rule
as (1) to each element.
## Template parameters
`InputIt`
: an input iterator type
## Parameters
`value` (in)
: value to insert
`first` (in)
: iterator to the first element to insert
`last` (in)
: iterator one past the last element to insert
## Return value
1. pair of an iterator to the (possibly newly inserted) element, and a `bool` that is `true` if insertion
took place and `false` if an element with an equal key already existed
2. (none)
## Complexity
1. Linear in the number of elements.
2. Linear in the distance between `first` and `last`, times linear in the number of elements.
## Examples
??? example
The example shows how `insert` is used.
```cpp
--8<-- "examples/ordered_map__insert.cpp"
```
Output:
```
--8<-- "examples/ordered_map__insert.output"
```
## Version history
- Added in version 3.9.1 to implement [`nlohmann::ordered_json`](../ordered_json.md).
@@ -1,34 +0,0 @@
# <small>nlohmann::ordered_map::</small>key_compare
```cpp
using key_compare = std::equal_to<Key>; // until C++14
using key_compare = std::equal_to<>; // since C++14
```
The comparator used to determine key equality when looking up elements. Unlike `std::map`, `ordered_map`
uses linear search with `key_compare` rather than an ordering relation, since element order reflects
insertion order rather than key order.
Since C++14, the transparent `#!cpp std::equal_to<>` is used, which enables heterogeneous lookup (e.g.
looking up by a `#!cpp const char*` key without constructing a temporary `Key`).
## Examples
??? example
The example shows how `key_compare` is used.
```cpp
--8<-- "examples/ordered_map__key_compare.cpp"
```
Output:
```
--8<-- "examples/ordered_map__key_compare.output"
```
## Version history
- Added in version 3.11.0.
@@ -1,32 +0,0 @@
# <small>nlohmann::ordered_map::</small>operator=
```cpp
// (1)
ordered_map& operator=(const ordered_map& other);
// (2)
ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value);
```
1. Copy assignment operator.
2. Move assignment operator.
## Parameters
`other` (in)
: value to assign from
## Return value
`*this`
## Complexity
1. Linear in the size of `other`.
2. Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
@@ -1,62 +0,0 @@
# <small>nlohmann::ordered_map::</small>operator[]
```cpp
// (1)
T& operator[](const key_type& key);
const T& operator[](const key_type& key) const;
// (2)
template<class KeyType>
T& operator[](KeyType&& key);
template<class KeyType>
const T& operator[](KeyType&& key) const;
```
1. Returns a reference to the value mapped to `key`, inserting a default-constructed `T` (non-`const`
overload only) if no such element exists yet.
2. Same as (1), but for any `KeyType` comparable to `key_type` via [`key_compare`](key_compare.md)
(heterogeneous lookup). Only participates in overload resolution if `KeyType` is usable as a key type.
## Template parameters
`KeyType`
: a type comparable to `key_type` via [`key_compare`](key_compare.md)
## Parameters
`key` (in)
: key of the element to find or insert
## Return value
reference to the mapped value of the element with key equal to `key`
## Exceptions
The `const` overloads throw `std::out_of_range` if no element with key `key` exists (they delegate to
[`at`](at.md)).
## Complexity
Linear in the number of elements.
## Examples
??? example
The example shows how `operator[]` is used.
```cpp
--8<-- "examples/ordered_map__operator_idx.cpp"
```
Output:
```
--8<-- "examples/ordered_map__operator_idx.output"
```
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
- Overload (2) added in version 3.11.0.
@@ -1,66 +0,0 @@
# <small>nlohmann::ordered_map::</small>ordered_map
```cpp
// (1)
ordered_map() noexcept(noexcept(Container()));
// (2)
explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc)));
// (3)
template <class It>
ordered_map(It first, It last, const Allocator& alloc = Allocator());
// (4)
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator());
// (5)
ordered_map(const ordered_map&) = default;
// (6)
ordered_map(ordered_map&&) noexcept(std::is_nothrow_move_constructible<Container>::value) = default;
```
1. Default constructor. Creates an empty `ordered_map`.
2. Creates an empty `ordered_map` using the given allocator.
3. Creates an `ordered_map` from the elements in range `[first, last)`, inserted in iteration order.
4. Creates an `ordered_map` from an initializer list of key/value pairs, inserted in list order.
5. Copy constructor.
6. Move constructor.
These constructors are declared explicitly (rather than inherited via `#!cpp using Container::Container`)
because older compilers (GCC <= 5.5, Xcode <= 9.4) do not handle the inherited constructors correctly.
## Template parameters
`It`
: an input iterator type
## Parameters
`alloc` (in)
: allocator to use for the underlying container
`first` (in)
: iterator to the first element to insert
`last` (in)
: iterator one past the last element to insert
`init` (in)
: initializer list of key/value pairs to insert
## Complexity
1. Constant.
2. Constant.
3. Linear in the distance between `first` and `last`.
4. Linear in the size of `init`.
5. Linear in the size of `other`.
6. Constant.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
@@ -1,17 +0,0 @@
# <small>nlohmann::ordered_map::</small>~ordered_map
```cpp
~ordered_map() = default;
```
Destroys the `ordered_map` and frees all allocated memory.
## Complexity
Linear in the number of elements.
<!-- NOLINT Examples -->
## Version history
- Added in version 3.9.0 to implement [`nlohmann::ordered_json`](../ordered_json.md).
@@ -1,21 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// an empty binary value is encoded differently by the two drafts:
// draft2 omits the optimized type marker for an empty byte array,
// while draft3 always writes it
json j = json::binary({});
// encode using BJData draft2 (the default)
auto v_draft2 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft2);
// encode using BJData draft3
auto v_draft3 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft3);
std::cout << "draft2 size: " << v_draft2.size() << '\n'
<< "draft3 size: " << v_draft3.size() << std::endl;
}
@@ -1,2 +0,0 @@
draft2 size: 4
draft3 size: 6
@@ -1,11 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
int main()
{
std::cout << std::boolalpha
<< std::is_same<byte_container_with_subtype::container_type, std::vector<std::uint8_t>>::value
<< std::endl;
}
@@ -1,15 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
int main()
{
byte_container_with_subtype c1({0xca, 0xfe});
byte_container_with_subtype c2({0xca, 0xfe});
byte_container_with_subtype c3({0xca, 0xfe}, 42);
std::cout << std::boolalpha
<< "c1 == c2: " << (c1 == c2) << '\n'
<< "c1 == c3: " << (c1 == c3) << std::endl;
}
@@ -1,2 +0,0 @@
c1 == c2: true
c1 == c3: false
@@ -1,15 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
int main()
{
byte_container_with_subtype c1({0xca, 0xfe});
byte_container_with_subtype c2({0xca, 0xfe});
byte_container_with_subtype c3({0xca, 0xfe}, 42);
std::cout << std::boolalpha
<< "c1 != c2: " << (c1 != c2) << '\n'
<< "c1 != c3: " << (c1 != c3) << std::endl;
}
@@ -1,2 +0,0 @@
c1 != c2: false
c1 != c3: true
@@ -1,10 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using byte_container_with_subtype = nlohmann::byte_container_with_subtype<std::vector<std::uint8_t>>;
int main()
{
std::cout << std::boolalpha
<< std::is_same<byte_container_with_subtype::subtype_type, std::uint64_t>::value << std::endl;
}
@@ -1,13 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// an initializer_list_t is what a braced-init-list of JSON values is deduced as
json::initializer_list_t init = {"a", 1, 2.0, false};
json j(init);
std::cout << j.dump() << std::endl;
}
@@ -1 +0,0 @@
["a",1,2.0,false]
@@ -1,10 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::binary_t, json::binary_t>::value << std::endl;
}
@@ -1 +0,0 @@
true
@@ -1,10 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::number_float_t, json::number_float_t>::value << std::endl;
}
@@ -1 +0,0 @@
true
@@ -1,10 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::number_integer_t, json::number_integer_t>::value << std::endl;
}
@@ -1 +0,0 @@
true
@@ -1,10 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::number_unsigned_t, json::number_unsigned_t>::value << std::endl;
}
@@ -1 +0,0 @@
true
@@ -1,10 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t::string_t, json::string_t>::value << std::endl;
}
@@ -1 +0,0 @@
true
-10
View File
@@ -1,10 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
std::cout << std::boolalpha
<< std::is_same<json::json_sax_t, nlohmann::json_sax<json>>::value << std::endl;
}
@@ -1 +0,0 @@
true
@@ -1,12 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
using Map = nlohmann::ordered_map<std::string, int>;
std::cout << std::boolalpha
<< "Container is std::vector<std::pair<const Key, T>>: "
<< std::is_same<Map::Container, std::vector<std::pair<const std::string, int>>>::value
<< std::endl;
}
@@ -1 +0,0 @@
Container is std::vector<std::pair<const Key, T>>: true
@@ -1,26 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
m["one"] = 1;
m["two"] = 2;
// access an existing element
std::cout << "m.at(\"one\") = " << m.at("one") << std::endl;
// modify through the reference returned by at()
m.at("two") = 22;
std::cout << "m.at(\"two\") = " << m.at("two") << std::endl;
// accessing a missing key throws
try
{
m.at("three");
}
catch (const std::out_of_range& e)
{
std::cout << "exception: " << e.what() << std::endl;
}
}
@@ -1,3 +0,0 @@
m.at("one") = 1
m.at("two") = 22
exception: key not found
@@ -1,12 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
m["one"] = 1;
std::cout << std::boolalpha
<< "m.count(\"one\") = " << m.count("one") << '\n'
<< "m.count(\"two\") = " << m.count("two") << std::endl;
}
@@ -1,2 +0,0 @@
m.count("one") = 1
m.count("two") = 0
@@ -1,15 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, std::string> m;
// emplace a new element
auto res1 = m.emplace("one", "eins");
std::cout << std::boolalpha << "inserted: " << res1.second << ", value: " << res1.first->second << std::endl;
// emplace with an already-existing key: no-op, returns the existing element
auto res2 = m.emplace("one", "uno");
std::cout << std::boolalpha << "inserted: " << res2.second << ", value: " << res2.first->second << std::endl;
}
@@ -1,2 +0,0 @@
inserted: true, value: eins
inserted: false, value: eins
@@ -1,24 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
m["one"] = 1;
m["two"] = 2;
m["three"] = 3;
// erase by key
std::size_t removed = m.erase("two");
std::cout << "removed by key: " << removed << std::endl;
// erase by iterator
m.erase(m.begin());
std::cout << "remaining: ";
for (const auto& element : m)
{
std::cout << element.first << ' ';
}
std::cout << std::endl;
}
@@ -1,2 +0,0 @@
removed by key: 1
remaining: three
@@ -1,19 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
m["one"] = 1;
auto it = m.find("one");
if (it != m.end())
{
std::cout << "found: " << it->first << " = " << it->second << std::endl;
}
if (m.find("two") == m.end())
{
std::cout << "\"two\" not found" << std::endl;
}
}
@@ -1,2 +0,0 @@
found: one = 1
"two" not found
@@ -1,21 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
// insert a single value
auto res = m.insert({"one", 1});
std::cout << std::boolalpha << "inserted: " << res.second << std::endl;
// insert a range from another container
std::vector<std::pair<const std::string, int>> more = {{"two", 2}, {"three", 3}};
m.insert(more.begin(), more.end());
for (const auto& element : m)
{
std::cout << element.first << ':' << element.second << ' ';
}
std::cout << std::endl;
}
@@ -1,2 +0,0 @@
inserted: true
one:1 two:2 three:3
@@ -1,12 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
using Map = nlohmann::ordered_map<std::string, int>;
Map::key_compare compare{};
std::cout << std::boolalpha
<< "compare(\"a\", \"a\") = " << compare("a", "a") << '\n'
<< "compare(\"a\", \"b\") = " << compare("a", "b") << std::endl;
}
@@ -1,2 +0,0 @@
compare("a", "a") = true
compare("a", "b") = false
@@ -1,14 +0,0 @@
#include <iostream>
#include <nlohmann/json.hpp>
int main()
{
nlohmann::ordered_map<std::string, int> m;
// operator[] inserts a default-constructed value if the key doesn't exist yet
m["one"] = 1;
std::cout << "m[\"one\"] = " << m["one"] << std::endl;
// accessing again just returns the existing value
std::cout << "m[\"one\"] = " << m["one"] << std::endl;
}
@@ -1,2 +0,0 @@
m["one"] = 1
m["one"] = 1
@@ -215,6 +215,24 @@ For _derived_ classes and structs, use the following macros
nlohmann::ordered_json j = p; // keys appear in declaration order: name, address, age
```
!!! note "Zero-member types"
All 12 `NLOHMANN_DEFINE_TYPE_*`/`NLOHMANN_DEFINE_DERIVED_TYPE_*` macros (excluding the `WITH_NAMES` variants)
also accept types with no member variables to serialize, producing/accepting an empty JSON object `{}`
(or, for the derived-type macros, just the base class's own JSON representation):
```cpp
namespace ns {
struct marker {
bool operator==(const marker&) const { return true; }
NLOHMANN_DEFINE_TYPE_INTRUSIVE(marker)
};
}
ns::marker m{};
nlohmann::json j = m; // {}
```
!!! note "No macro for non-default-constructible types"
There is currently no `NLOHMANN_DEFINE_TYPE_*`-style macro for types that are not
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -17,7 +17,7 @@ The main structure is class [nlohmann::basic_json](../api/basic_json/index.md).
- describe template parameters of `basic_json`
- [`json`](../api/json.md)
- [`ordered_json`](../api/ordered_json.md) via [`ordered_map`](../api/ordered_map/index.md)
- [`ordered_json`](../api/ordered_json.md) via [`ordered_map`](../api/ordered_map.md)
## Value storage
+1 -2
View File
@@ -2,8 +2,7 @@
This page summarizes the notable changes of every release and links to the relevant documentation.
The **complete release notes** — including all changes, the download files, and their checksums — are
published on the [GitHub releases page](https://github.com/nlohmann/json/releases). For a raw,
signature-level diff of the public API between releases, see [API Changes](api_changes.md).
published on the [GitHub releases page](https://github.com/nlohmann/json/releases).
## v3.12.0 (2025-04-11)
+1 -30
View File
@@ -52,7 +52,6 @@ nav:
- "FAQ": home/faq.md
- home/exceptions.md
- home/releases.md
- home/api_changes.md
- home/design_goals.md
- home/architecture.md
- home/customers.md
@@ -118,7 +117,6 @@ nav:
- 'begin': api/basic_json/begin.md
- 'binary': api/basic_json/binary.md
- 'binary_t': api/basic_json/binary_t.md
- 'bjdata_version_t': api/basic_json/bjdata_version_t.md
- 'boolean_t': api/basic_json/boolean_t.md
- 'cbegin': api/basic_json/cbegin.md
- 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md
@@ -156,7 +154,6 @@ nav:
- 'get_to': api/basic_json/get_to.md
- 'std::formatter&lt;basic_json&gt;': api/basic_json/std_formatter.md
- 'std::hash&lt;basic_json&gt;': api/basic_json/std_hash.md
- 'initializer_list_t': api/basic_json/initializer_list_t.md
- 'input_format_t': api/basic_json/input_format_t.md
- 'insert': api/basic_json/insert.md
- 'invalid_iterator': api/basic_json/invalid_iterator.md
@@ -175,7 +172,6 @@ nav:
- 'is_structured': api/basic_json/is_structured.md
- 'items': api/basic_json/items.md
- 'json_base_class_t': api/basic_json/json_base_class_t.md
- 'json_sax_t': api/basic_json/json_sax_t.md
- 'json_serializer': api/basic_json/json_serializer.md
- 'max_size': api/basic_json/max_size.md
- 'meta': api/basic_json/meta.md
@@ -232,13 +228,9 @@ nav:
- 'Overview': api/byte_container_with_subtype/index.md
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
- 'clear_subtype': api/byte_container_with_subtype/clear_subtype.md
- 'container_type': api/byte_container_with_subtype/container_type.md
- 'has_subtype': api/byte_container_with_subtype/has_subtype.md
- 'operator==': api/byte_container_with_subtype/operator_eq.md
- 'operator!=': api/byte_container_with_subtype/operator_ne.md
- 'set_subtype': api/byte_container_with_subtype/set_subtype.md
- 'subtype': api/byte_container_with_subtype/subtype.md
- 'subtype_type': api/byte_container_with_subtype/subtype_type.md
- adl_serializer:
- 'Overview': api/adl_serializer/index.md
- 'from_json': api/adl_serializer/from_json.md
@@ -264,46 +256,25 @@ nav:
- 'to_string': api/json_pointer/to_string.md
- json_sax:
- 'Overview': api/json_sax/index.md
- '(Constructor)': api/json_sax/json_sax.md
- '(Destructor)': api/json_sax/~json_sax.md
- 'operator=': api/json_sax/operator=.md
- 'binary': api/json_sax/binary.md
- 'binary_t': api/json_sax/binary_t.md
- 'boolean': api/json_sax/boolean.md
- 'end_array': api/json_sax/end_array.md
- 'end_object': api/json_sax/end_object.md
- 'key': api/json_sax/key.md
- 'null': api/json_sax/null.md
- 'number_float': api/json_sax/number_float.md
- 'number_float_t': api/json_sax/number_float_t.md
- 'number_integer': api/json_sax/number_integer.md
- 'number_integer_t': api/json_sax/number_integer_t.md
- 'number_unsigned': api/json_sax/number_unsigned.md
- 'number_unsigned_t': api/json_sax/number_unsigned_t.md
- 'parse_error': api/json_sax/parse_error.md
- 'start_array': api/json_sax/start_array.md
- 'start_object': api/json_sax/start_object.md
- 'string': api/json_sax/string.md
- 'string_t': api/json_sax/string_t.md
- 'operator<<(basic_json), operator<<(json_pointer)': api/operator_ltlt.md
- 'operator>>(basic_json)': api/operator_gtgt.md
- 'operator""_json': api/operator_literal_json.md
- 'operator""_json_pointer': api/operator_literal_json_pointer.md
- 'ordered_json': api/ordered_json.md
- ordered_map:
- 'Overview': api/ordered_map/index.md
- '(Constructor)': api/ordered_map/ordered_map.md
- '(Destructor)': api/ordered_map/~ordered_map.md
- 'operator=': api/ordered_map/operator=.md
- 'at': api/ordered_map/at.md
- 'Container': api/ordered_map/Container.md
- 'count': api/ordered_map/count.md
- 'emplace': api/ordered_map/emplace.md
- 'erase': api/ordered_map/erase.md
- 'find': api/ordered_map/find.md
- 'insert': api/ordered_map/insert.md
- 'key_compare': api/ordered_map/key_compare.md
- 'operator[]': api/ordered_map/operator[].md
- 'ordered_map': api/ordered_map.md
- macros:
- 'Overview': api/macros/index.md
- 'JSON_ASSERT': api/macros/json_assert.md
@@ -22,9 +22,7 @@ template<typename BinaryType>
class byte_container_with_subtype : public BinaryType
{
public:
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/container_type/
using container_type = BinaryType;
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype_type/
using subtype_type = std::uint64_t;
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
@@ -56,14 +54,12 @@ class byte_container_with_subtype : public BinaryType
, m_has_subtype(true)
{}
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/operator_eq/
bool operator==(const byte_container_with_subtype& rhs) const
{
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
}
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/operator_ne/
bool operator!=(const byte_container_with_subtype& rhs) const
{
return !(rhs == *this);
@@ -31,21 +31,15 @@ input.
template<typename BasicJsonType>
struct json_sax
{
/// @sa https://json.nlohmann.me/api/json_sax/number_integer_t/
using number_integer_t = typename BasicJsonType::number_integer_t;
/// @sa https://json.nlohmann.me/api/json_sax/number_unsigned_t/
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
/// @sa https://json.nlohmann.me/api/json_sax/number_float_t/
using number_float_t = typename BasicJsonType::number_float_t;
/// @sa https://json.nlohmann.me/api/json_sax/string_t/
using string_t = typename BasicJsonType::string_t;
/// @sa https://json.nlohmann.me/api/json_sax/binary_t/
using binary_t = typename BasicJsonType::binary_t;
/*!
@brief a null value was read
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/null/
*/
virtual bool null() = 0;
@@ -53,7 +47,6 @@ struct json_sax
@brief a boolean value was read
@param[in] val boolean value
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/boolean/
*/
virtual bool boolean(bool val) = 0;
@@ -61,7 +54,6 @@ struct json_sax
@brief an integer number was read
@param[in] val integer value
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_integer/
*/
virtual bool number_integer(number_integer_t val) = 0;
@@ -69,7 +61,6 @@ struct json_sax
@brief an unsigned integer number was read
@param[in] val unsigned integer value
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_unsigned/
*/
virtual bool number_unsigned(number_unsigned_t val) = 0;
@@ -78,7 +69,6 @@ struct json_sax
@param[in] val floating-point value
@param[in] s raw token value
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_float/
*/
virtual bool number_float(number_float_t val, const string_t& s) = 0;
@@ -87,7 +77,6 @@ struct json_sax
@param[in] val string value
@return whether parsing should proceed
@note It is safe to move the passed string value.
@sa https://json.nlohmann.me/api/json_sax/string/
*/
virtual bool string(string_t& val) = 0;
@@ -96,7 +85,6 @@ struct json_sax
@param[in] val binary value
@return whether parsing should proceed
@note It is safe to move the passed binary value.
@sa https://json.nlohmann.me/api/json_sax/binary/
*/
virtual bool binary(binary_t& val) = 0;
@@ -105,7 +93,6 @@ struct json_sax
@param[in] elements number of object elements or -1 if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
@sa https://json.nlohmann.me/api/json_sax/start_object/
*/
virtual bool start_object(std::size_t elements) = 0;
@@ -114,14 +101,12 @@ struct json_sax
@param[in] val object key
@return whether parsing should proceed
@note It is safe to move the passed string.
@sa https://json.nlohmann.me/api/json_sax/key/
*/
virtual bool key(string_t& val) = 0;
/*!
@brief the end of an object was read
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/end_object/
*/
virtual bool end_object() = 0;
@@ -130,14 +115,12 @@ struct json_sax
@param[in] elements number of array elements or -1 if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
@sa https://json.nlohmann.me/api/json_sax/start_array/
*/
virtual bool start_array(std::size_t elements) = 0;
/*!
@brief the end of an array was read
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/end_array/
*/
virtual bool end_array() = 0;
@@ -147,23 +130,16 @@ struct json_sax
@param[in] last_token the last read token
@param[in] ex an exception object describing the error
@return whether parsing should proceed (must return false)
@sa https://json.nlohmann.me/api/json_sax/parse_error/
*/
virtual bool parse_error(std::size_t position,
const std::string& last_token,
const detail::exception& ex) = 0;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax() = default;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax(const json_sax&) = default;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax(json_sax&&) noexcept = default;
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
json_sax& operator=(const json_sax&) = default;
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
json_sax& operator=(json_sax&&) noexcept = default;
/// @sa https://json.nlohmann.me/api/json_sax/~json_sax/
virtual ~json_sax() = default;
};
-1
View File
@@ -55,7 +55,6 @@ class json_pointer
public:
// for backwards compatibility accept BasicJsonType
/// @sa https://json.nlohmann.me/api/json_pointer/string_t/
using string_t = typename string_t_helper<RefStringType>::type;
/// @brief create JSON pointer
+116 -12
View File
@@ -587,16 +587,59 @@ void templated_json_throw(ExceptionType exception)
#define NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(...) template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> __VA_ARGS__
// Tags used to dispatch the NLOHMANN_DEFINE_TYPE_*/NLOHMANN_DEFINE_DERIVED_TYPE_*
// macros below between a zero-member and a one-or-more-member implementation
// (issue #4041, e.g. NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type) with no further
// arguments). NLOHMANN_JSON_TYPE_TAG(...) expands to the token EMPTY when
// __VA_ARGS__ is a single token (Type alone) and to MEMBERS for two or more
// (Type, member...), naming the body the public macro dispatches to. It
// reuses the existing 64-slot NLOHMANN_JSON_GET_MACRO dispatch with one extra
// trailing sentinel token appended so its own trailing "..." is never left
// completely empty at the lowest supported argument count --
// invoking a variadic macro so that "..." matches nothing is only granted
// unconditionally by the standard since C++20, and pre-C++20 compilers may
// reject it under -pedantic regardless of what the macro body does.
//
// NLOHMANN_JSON_DERIVED_TYPE_TAG(...) answers the same question for the
// derived-type macros, whose fixed prefix is Type,BaseType. It drops the
// leading Type and defers to NLOHMANN_JSON_TYPE_TAG rather than shifting the
// slot table by one: NLOHMANN_JSON_GET_MACRO only resolves 64 positional
// arguments, so tagging Type,BaseType,member... directly would run out one
// slot early and cap the derived-type macros at 62 members instead of the 63
// that NLOHMANN_JSON_PASTE supports.
#define NLOHMANN_JSON_CAT_(x, y) x ## y
#define NLOHMANN_JSON_CAT(x, y) NLOHMANN_JSON_CAT_(x, y)
#define NLOHMANN_JSON_TYPE_TAG(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, EMPTY, \
NLOHMANN_JSON_TYPE_TAG_SENTINEL))
#define NLOHMANN_JSON_DERIVED_TYPE_TAG_(Type, ...) NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__)
#define NLOHMANN_JSON_DERIVED_TYPE_TAG(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_TAG_(__VA_ARGS__))
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType&, Type&) noexcept { })
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
@@ -607,10 +650,15 @@ void templated_json_throw(ExceptionType exception)
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
// identical to NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type)
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
@@ -621,9 +669,14 @@ void templated_json_throw(ExceptionType exception)
@since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); })
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
@@ -633,10 +686,17 @@ void templated_json_throw(ExceptionType exception)
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType&, Type&) noexcept { })
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
@@ -647,10 +707,15 @@ void templated_json_throw(ExceptionType exception)
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
// identical to NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type)
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
@@ -661,9 +726,14 @@ void templated_json_throw(ExceptionType exception)
@since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); })
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
@@ -673,10 +743,17 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); }) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); })
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
@@ -687,10 +764,15 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
// identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType)
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
@@ -701,9 +783,14 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type, BaseType) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); })
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
@@ -714,10 +801,17 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); }) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); })
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
@@ -728,10 +822,15 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
// identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType)
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
@@ -742,9 +841,14 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type, BaseType) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); })
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
+1 -41
View File
@@ -192,31 +192,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using serializer = ::nlohmann::detail::serializer<basic_json>;
public:
/// @brief the type of the JSON value
/// @sa https://json.nlohmann.me/api/basic_json/value_t/
using value_t = detail::value_t;
/// JSON Pointer, see @ref nlohmann::json_pointer
using json_pointer = ::nlohmann::json_pointer<StringType>;
template<typename T, typename SFINAE>
using json_serializer = JSONSerializer<T, SFINAE>;
/// how to treat decoding errors
/// @sa https://json.nlohmann.me/api/basic_json/error_handler_t/
using error_handler_t = detail::error_handler_t;
/// how to treat CBOR tags
/// @sa https://json.nlohmann.me/api/basic_json/cbor_tag_handler_t/
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData
/// @sa https://json.nlohmann.me/api/basic_json/bjdata_version_t/
using bjdata_version_t = detail::bjdata_version_t;
/// helper type for initializer lists of basic_json values
/// @sa https://json.nlohmann.me/api/basic_json/initializer_list_t/
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
/// @brief the type of the SAX interface used to parse and serialize the JSON value
/// @sa https://json.nlohmann.me/api/basic_json/input_format_t/
using input_format_t = detail::input_format_t;
/// SAX interface type, see @ref nlohmann::json_sax
/// @sa https://json.nlohmann.me/api/basic_json/json_sax_t/
using json_sax_t = json_sax<basic_json>;
////////////////
@@ -358,19 +349,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// the template arguments passed to class @ref basic_json.
/// @{
#if defined(JSON_HAS_CPP_14)
/// @brief default object key comparator type
/// The actual object key comparator type (@ref object_comparator_t) may be
/// different.
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
#if defined(JSON_HAS_CPP_14)
// use of transparent comparator avoids unnecessary repeated construction of temporaries
// in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
using default_object_comparator_t = std::less<>;
#else
/// @brief default object key comparator type
/// The actual object key comparator type (@ref object_comparator_t) may be
/// different.
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
using default_object_comparator_t = std::less<StringType>;
#endif
@@ -1195,7 +1182,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// other constructors and destructor //
///////////////////////////////////////
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template<typename JsonRef,
detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
@@ -1816,8 +1802,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
@since version 2.1.0
@sa https://json.nlohmann.me/api/basic_json/get/
*/
template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
#if defined(JSON_HAS_CPP_14)
@@ -1861,8 +1845,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@sa see @ref get_ptr() for explicit pointer-member access
@since version 1.0.0
@sa https://json.nlohmann.me/api/basic_json/get/
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
@@ -1888,7 +1870,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// specialization to allow calling get_to with a basic_json value
// see https://github.com/nlohmann/json/issues/2175
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
template<typename ValueType,
detail::enable_if_t <
detail::is_basic_json<ValueType>::value,
@@ -1899,7 +1880,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return v;
}
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
template <
typename T, std::size_t N,
typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
@@ -1963,7 +1943,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@since version 1.0.0
*/
/// @sa https://json.nlohmann.me/api/basic_json/operator_ValueType/
template < typename ValueType, typename std::enable_if <
detail::conjunction <
detail::negation<std::is_pointer<ValueType>>,
@@ -2245,14 +2224,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
// (they seemingly cannot be constrained to resolve the ambiguity)
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
template<typename T>
reference operator[](T* key)
{
return operator[](typename object_t::key_type(key));
}
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
template<typename T>
const_reference operator[](T* key) const
{
@@ -2462,7 +2439,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
}
/// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, class BasicJsonType, detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value
&& detail::is_getable<basic_json_t, ValueType>::value
@@ -2473,7 +2449,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return value(ptr.convert(), default_value);
}
/// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value
@@ -2843,7 +2818,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.contains(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/contains/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const
@@ -4126,7 +4100,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result;
}
/// @sa https://json.nlohmann.me/api/basic_json/parse/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
static basic_json parse(detail::span_input_adapter&& i,
@@ -4161,7 +4134,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @sa https://json.nlohmann.me/api/basic_json/accept/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
static bool accept(detail::span_input_adapter&& i,
@@ -4509,7 +4481,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
@@ -4521,7 +4492,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
static basic_json from_cbor(detail::span_input_adapter&& i,
@@ -4568,7 +4538,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
@@ -4579,7 +4548,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
static basic_json from_msgpack(detail::span_input_adapter&& i,
@@ -4625,7 +4593,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
@@ -4636,7 +4603,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
static basic_json from_ubjson(detail::span_input_adapter&& i,
@@ -4713,7 +4679,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
@@ -4724,7 +4689,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_bson(ptr, ptr + len, strict, allow_exceptions);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
static basic_json from_bson(detail::span_input_adapter&& i,
@@ -4754,7 +4718,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_unchecked(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
@@ -4769,7 +4732,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_unchecked(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
@@ -4784,7 +4746,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_checked(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/at/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
@@ -4799,7 +4760,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_checked(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/at/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
-35
View File
@@ -30,41 +30,30 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
{
using key_type = Key;
using mapped_type = T;
/// @sa https://json.nlohmann.me/api/ordered_map/Container/
using Container = std::vector<std::pair<const Key, T>, Allocator>;
using iterator = typename Container::iterator;
using const_iterator = typename Container::const_iterator;
using size_type = typename Container::size_type;
using value_type = typename Container::value_type;
#ifdef JSON_HAS_CPP_14
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
using key_compare = std::equal_to<>;
#else
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
using key_compare = std::equal_to<Key>;
#endif
// Explicit constructors instead of `using Container::Container`
// otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map() noexcept(noexcept(Container())) : Container{} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
template <class It>
ordered_map(It first, It last, const Allocator& alloc = Allocator())
: Container{first, last, alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
: Container{init, alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(const ordered_map&) = default;
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(ordered_map&&) noexcept(std::is_nothrow_move_constructible<Container>::value) = default;
/// @sa https://json.nlohmann.me/api/ordered_map/~ordered_map/
~ordered_map() = default;
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
ordered_map& operator=(const ordered_map& other)
{
if (this != &other)
@@ -75,14 +64,12 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return *this;
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value)
{
Container::operator=(std::move(static_cast<Container&>(other)));
return *this;
}
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -96,7 +83,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {std::prev(this->end()), true};
}
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
std::pair<iterator, bool> emplace(KeyType && key, T && t)
@@ -112,13 +98,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {std::prev(this->end()), true};
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
T& operator[](const key_type& key)
{
return emplace(key, T{}).first->second;
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & operator[](KeyType && key)
@@ -126,13 +110,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return emplace(std::forward<KeyType>(key), T{}).first->second;
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
const T& operator[](const key_type& key) const
{
return at(key);
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & operator[](KeyType && key) const
@@ -140,7 +122,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return at(std::forward<KeyType>(key));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
T& at(const key_type& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -154,7 +135,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -170,7 +150,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
const T& at(const key_type& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -184,7 +163,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -200,7 +178,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
size_type erase(const key_type& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -220,7 +197,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -242,13 +218,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
iterator erase(iterator pos)
{
return erase(pos, std::next(pos));
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
iterator erase(iterator first, iterator last)
{
if (first == last)
@@ -302,7 +276,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::begin() + offset;
}
/// @sa https://json.nlohmann.me/api/ordered_map/count/
size_type count(const key_type& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -315,7 +288,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}
/// @sa https://json.nlohmann.me/api/ordered_map/count/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -330,7 +302,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}
/// @sa https://json.nlohmann.me/api/ordered_map/find/
iterator find(const key_type& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -343,7 +314,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}
/// @sa https://json.nlohmann.me/api/ordered_map/find/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -358,7 +328,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}
/// @sa https://json.nlohmann.me/api/ordered_map/find/
const_iterator find(const key_type& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -371,7 +340,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}
/// @sa https://json.nlohmann.me/api/ordered_map/find/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const_iterator find(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -386,13 +354,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
std::pair<iterator, bool> insert( value_type&& value )
{
return emplace(value.first, std::move(value.second));
}
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
std::pair<iterator, bool> insert( const value_type& value )
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -410,7 +376,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
std::input_iterator_tag>::value>::type;
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
template<typename InputIt, typename = require_input_iter<InputIt>>
void insert(InputIt first, InputIt last)
{
+117 -117
View File
@@ -2962,16 +2962,59 @@ void templated_json_throw(ExceptionType exception)
#define NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(...) template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> __VA_ARGS__
// Tags used to dispatch the NLOHMANN_DEFINE_TYPE_*/NLOHMANN_DEFINE_DERIVED_TYPE_*
// macros below between a zero-member and a one-or-more-member implementation
// (issue #4041, e.g. NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type) with no further
// arguments). NLOHMANN_JSON_TYPE_TAG(...) expands to the token EMPTY when
// __VA_ARGS__ is a single token (Type alone) and to MEMBERS for two or more
// (Type, member...), naming the body the public macro dispatches to. It
// reuses the existing 64-slot NLOHMANN_JSON_GET_MACRO dispatch with one extra
// trailing sentinel token appended so its own trailing "..." is never left
// completely empty at the lowest supported argument count --
// invoking a variadic macro so that "..." matches nothing is only granted
// unconditionally by the standard since C++20, and pre-C++20 compilers may
// reject it under -pedantic regardless of what the macro body does.
//
// NLOHMANN_JSON_DERIVED_TYPE_TAG(...) answers the same question for the
// derived-type macros, whose fixed prefix is Type,BaseType. It drops the
// leading Type and defers to NLOHMANN_JSON_TYPE_TAG rather than shifting the
// slot table by one: NLOHMANN_JSON_GET_MACRO only resolves 64 positional
// arguments, so tagging Type,BaseType,member... directly would run out one
// slot early and cap the derived-type macros at 62 members instead of the 63
// that NLOHMANN_JSON_PASTE supports.
#define NLOHMANN_JSON_CAT_(x, y) x ## y
#define NLOHMANN_JSON_CAT(x, y) NLOHMANN_JSON_CAT_(x, y)
#define NLOHMANN_JSON_TYPE_TAG(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, MEMBERS, \
MEMBERS, MEMBERS, MEMBERS, EMPTY, \
NLOHMANN_JSON_TYPE_TAG_SENTINEL))
#define NLOHMANN_JSON_DERIVED_TYPE_TAG_(Type, ...) NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__)
#define NLOHMANN_JSON_DERIVED_TYPE_TAG(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DERIVED_TYPE_TAG_(__VA_ARGS__))
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType&, Type&) noexcept { })
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
@@ -2982,10 +3025,15 @@ void templated_json_throw(ExceptionType exception)
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
// identical to NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_EMPTY(Type)
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
@@ -2996,9 +3044,14 @@ void templated_json_throw(ExceptionType exception)
@since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); })
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
@@ -3008,10 +3061,17 @@ void templated_json_throw(ExceptionType exception)
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); }) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type is used as a declarator type, not in an expression */ \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType&, Type&) noexcept { })
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
@@ -3022,10 +3082,15 @@ void templated_json_throw(ExceptionType exception)
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
// identical to NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type) NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_EMPTY(Type)
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
@@ -3036,9 +3101,14 @@ void templated_json_throw(ExceptionType exception)
@since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type&) { nlohmann_json_j = BasicJsonType::object(); })
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_, NLOHMANN_JSON_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
@@ -3048,10 +3118,17 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); }) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); })
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
@@ -3062,10 +3139,15 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
// identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY: with no members there is nothing to default
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_EMPTY(Type, BaseType)
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
@@ -3076,9 +3158,14 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type, BaseType) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); })
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
@@ -3089,10 +3176,17 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); }) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) Type/BaseType are used as declarator types, not in expressions */ \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); })
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_NAME, __VA_ARGS__)) })
@@ -3103,10 +3197,15 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) })
// identical to NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY: with no members there is nothing to default
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_EMPTY(Type, BaseType) NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_EMPTY(Type, BaseType)
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) }) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT_WITH_NAME, __VA_ARGS__)) })
@@ -3117,9 +3216,14 @@ void templated_json_throw(ExceptionType exception)
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) })
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_EMPTY(Type, BaseType) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); })
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_CAT(NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_, NLOHMANN_JSON_DERIVED_TYPE_TAG(__VA_ARGS__))(__VA_ARGS__))
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(Type, BaseType, ...) \
NLOHMANN_JSON_BASIC_TYPE_TEMPLATE(void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_DOUBLE_PASTE(NLOHMANN_JSON_TO_WITH_NAME, __VA_ARGS__)) })
@@ -6732,9 +6836,7 @@ template<typename BinaryType>
class byte_container_with_subtype : public BinaryType
{
public:
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/container_type/
using container_type = BinaryType;
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype_type/
using subtype_type = std::uint64_t;
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
@@ -6766,14 +6868,12 @@ class byte_container_with_subtype : public BinaryType
, m_has_subtype(true)
{}
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/operator_eq/
bool operator==(const byte_container_with_subtype& rhs) const
{
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
}
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/operator_ne/
bool operator!=(const byte_container_with_subtype& rhs) const
{
return !(rhs == *this);
@@ -9436,21 +9536,15 @@ input.
template<typename BasicJsonType>
struct json_sax
{
/// @sa https://json.nlohmann.me/api/json_sax/number_integer_t/
using number_integer_t = typename BasicJsonType::number_integer_t;
/// @sa https://json.nlohmann.me/api/json_sax/number_unsigned_t/
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
/// @sa https://json.nlohmann.me/api/json_sax/number_float_t/
using number_float_t = typename BasicJsonType::number_float_t;
/// @sa https://json.nlohmann.me/api/json_sax/string_t/
using string_t = typename BasicJsonType::string_t;
/// @sa https://json.nlohmann.me/api/json_sax/binary_t/
using binary_t = typename BasicJsonType::binary_t;
/*!
@brief a null value was read
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/null/
*/
virtual bool null() = 0;
@@ -9458,7 +9552,6 @@ struct json_sax
@brief a boolean value was read
@param[in] val boolean value
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/boolean/
*/
virtual bool boolean(bool val) = 0;
@@ -9466,7 +9559,6 @@ struct json_sax
@brief an integer number was read
@param[in] val integer value
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_integer/
*/
virtual bool number_integer(number_integer_t val) = 0;
@@ -9474,7 +9566,6 @@ struct json_sax
@brief an unsigned integer number was read
@param[in] val unsigned integer value
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_unsigned/
*/
virtual bool number_unsigned(number_unsigned_t val) = 0;
@@ -9483,7 +9574,6 @@ struct json_sax
@param[in] val floating-point value
@param[in] s raw token value
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/number_float/
*/
virtual bool number_float(number_float_t val, const string_t& s) = 0;
@@ -9492,7 +9582,6 @@ struct json_sax
@param[in] val string value
@return whether parsing should proceed
@note It is safe to move the passed string value.
@sa https://json.nlohmann.me/api/json_sax/string/
*/
virtual bool string(string_t& val) = 0;
@@ -9501,7 +9590,6 @@ struct json_sax
@param[in] val binary value
@return whether parsing should proceed
@note It is safe to move the passed binary value.
@sa https://json.nlohmann.me/api/json_sax/binary/
*/
virtual bool binary(binary_t& val) = 0;
@@ -9510,7 +9598,6 @@ struct json_sax
@param[in] elements number of object elements or -1 if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
@sa https://json.nlohmann.me/api/json_sax/start_object/
*/
virtual bool start_object(std::size_t elements) = 0;
@@ -9519,14 +9606,12 @@ struct json_sax
@param[in] val object key
@return whether parsing should proceed
@note It is safe to move the passed string.
@sa https://json.nlohmann.me/api/json_sax/key/
*/
virtual bool key(string_t& val) = 0;
/*!
@brief the end of an object was read
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/end_object/
*/
virtual bool end_object() = 0;
@@ -9535,14 +9620,12 @@ struct json_sax
@param[in] elements number of array elements or -1 if unknown
@return whether parsing should proceed
@note binary formats may report the number of elements
@sa https://json.nlohmann.me/api/json_sax/start_array/
*/
virtual bool start_array(std::size_t elements) = 0;
/*!
@brief the end of an array was read
@return whether parsing should proceed
@sa https://json.nlohmann.me/api/json_sax/end_array/
*/
virtual bool end_array() = 0;
@@ -9552,23 +9635,16 @@ struct json_sax
@param[in] last_token the last read token
@param[in] ex an exception object describing the error
@return whether parsing should proceed (must return false)
@sa https://json.nlohmann.me/api/json_sax/parse_error/
*/
virtual bool parse_error(std::size_t position,
const std::string& last_token,
const detail::exception& ex) = 0;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax() = default;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax(const json_sax&) = default;
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
json_sax(json_sax&&) noexcept = default;
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
json_sax& operator=(const json_sax&) = default;
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
json_sax& operator=(json_sax&&) noexcept = default;
/// @sa https://json.nlohmann.me/api/json_sax/~json_sax/
virtual ~json_sax() = default;
};
@@ -15423,7 +15499,6 @@ class json_pointer
public:
// for backwards compatibility accept BasicJsonType
/// @sa https://json.nlohmann.me/api/json_pointer/string_t/
using string_t = typename string_t_helper<RefStringType>::type;
/// @brief create JSON pointer
@@ -20786,41 +20861,30 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
{
using key_type = Key;
using mapped_type = T;
/// @sa https://json.nlohmann.me/api/ordered_map/Container/
using Container = std::vector<std::pair<const Key, T>, Allocator>;
using iterator = typename Container::iterator;
using const_iterator = typename Container::const_iterator;
using size_type = typename Container::size_type;
using value_type = typename Container::value_type;
#ifdef JSON_HAS_CPP_14
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
using key_compare = std::equal_to<>;
#else
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
using key_compare = std::equal_to<Key>;
#endif
// Explicit constructors instead of `using Container::Container`
// otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map() noexcept(noexcept(Container())) : Container{} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
template <class It>
ordered_map(It first, It last, const Allocator& alloc = Allocator())
: Container{first, last, alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
: Container{init, alloc} {}
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(const ordered_map&) = default;
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
ordered_map(ordered_map&&) noexcept(std::is_nothrow_move_constructible<Container>::value) = default;
/// @sa https://json.nlohmann.me/api/ordered_map/~ordered_map/
~ordered_map() = default;
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
ordered_map& operator=(const ordered_map& other)
{
if (this != &other)
@@ -20831,14 +20895,12 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return *this;
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value)
{
Container::operator=(std::move(static_cast<Container&>(other)));
return *this;
}
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -20852,7 +20914,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {std::prev(this->end()), true};
}
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
std::pair<iterator, bool> emplace(KeyType && key, T && t)
@@ -20868,13 +20929,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return {std::prev(this->end()), true};
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
T& operator[](const key_type& key)
{
return emplace(key, T{}).first->second;
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & operator[](KeyType && key)
@@ -20882,13 +20941,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return emplace(std::forward<KeyType>(key), T{}).first->second;
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
const T& operator[](const key_type& key) const
{
return at(key);
}
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & operator[](KeyType && key) const
@@ -20896,7 +20953,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return at(std::forward<KeyType>(key));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
T& at(const key_type& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -20910,7 +20966,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -20926,7 +20981,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
const T& at(const key_type& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -20940,7 +20994,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}
/// @sa https://json.nlohmann.me/api/ordered_map/at/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -20956,7 +21009,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
JSON_THROW(std::out_of_range("key not found"));
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
size_type erase(const key_type& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -20976,7 +21028,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -20998,13 +21049,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
iterator erase(iterator pos)
{
return erase(pos, std::next(pos));
}
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
iterator erase(iterator first, iterator last)
{
if (first == last)
@@ -21058,7 +21107,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::begin() + offset;
}
/// @sa https://json.nlohmann.me/api/ordered_map/count/
size_type count(const key_type& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -21071,7 +21119,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}
/// @sa https://json.nlohmann.me/api/ordered_map/count/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -21086,7 +21133,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return 0;
}
/// @sa https://json.nlohmann.me/api/ordered_map/find/
iterator find(const key_type& key)
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -21099,7 +21145,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}
/// @sa https://json.nlohmann.me/api/ordered_map/find/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -21114,7 +21159,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}
/// @sa https://json.nlohmann.me/api/ordered_map/find/
const_iterator find(const key_type& key) const
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -21127,7 +21171,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}
/// @sa https://json.nlohmann.me/api/ordered_map/find/
template<class KeyType, detail::enable_if_t<
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
const_iterator find(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
@@ -21142,13 +21185,11 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
return Container::end();
}
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
std::pair<iterator, bool> insert( value_type&& value )
{
return emplace(value.first, std::move(value.second));
}
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
std::pair<iterator, bool> insert( const value_type& value )
{
for (auto it = this->begin(); it != this->end(); ++it)
@@ -21166,7 +21207,6 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
std::input_iterator_tag>::value>::type;
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
template<typename InputIt, typename = require_input_iter<InputIt>>
void insert(InputIt first, InputIt last)
{
@@ -21303,31 +21343,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
using serializer = ::nlohmann::detail::serializer<basic_json>;
public:
/// @brief the type of the JSON value
/// @sa https://json.nlohmann.me/api/basic_json/value_t/
using value_t = detail::value_t;
/// JSON Pointer, see @ref nlohmann::json_pointer
using json_pointer = ::nlohmann::json_pointer<StringType>;
template<typename T, typename SFINAE>
using json_serializer = JSONSerializer<T, SFINAE>;
/// how to treat decoding errors
/// @sa https://json.nlohmann.me/api/basic_json/error_handler_t/
using error_handler_t = detail::error_handler_t;
/// how to treat CBOR tags
/// @sa https://json.nlohmann.me/api/basic_json/cbor_tag_handler_t/
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
/// how to encode BJData
/// @sa https://json.nlohmann.me/api/basic_json/bjdata_version_t/
using bjdata_version_t = detail::bjdata_version_t;
/// helper type for initializer lists of basic_json values
/// @sa https://json.nlohmann.me/api/basic_json/initializer_list_t/
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
/// @brief the type of the SAX interface used to parse and serialize the JSON value
/// @sa https://json.nlohmann.me/api/basic_json/input_format_t/
using input_format_t = detail::input_format_t;
/// SAX interface type, see @ref nlohmann::json_sax
/// @sa https://json.nlohmann.me/api/basic_json/json_sax_t/
using json_sax_t = json_sax<basic_json>;
////////////////
@@ -21469,19 +21500,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// the template arguments passed to class @ref basic_json.
/// @{
#if defined(JSON_HAS_CPP_14)
/// @brief default object key comparator type
/// The actual object key comparator type (@ref object_comparator_t) may be
/// different.
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
#if defined(JSON_HAS_CPP_14)
// use of transparent comparator avoids unnecessary repeated construction of temporaries
// in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
using default_object_comparator_t = std::less<>;
#else
/// @brief default object key comparator type
/// The actual object key comparator type (@ref object_comparator_t) may be
/// different.
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
using default_object_comparator_t = std::less<StringType>;
#endif
@@ -22306,7 +22333,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// other constructors and destructor //
///////////////////////////////////////
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
template<typename JsonRef,
detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
@@ -22927,8 +22953,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
@since version 2.1.0
@sa https://json.nlohmann.me/api/basic_json/get/
*/
template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
#if defined(JSON_HAS_CPP_14)
@@ -22972,8 +22996,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@sa see @ref get_ptr() for explicit pointer-member access
@since version 1.0.0
@sa https://json.nlohmann.me/api/basic_json/get/
*/
template<typename PointerType, typename std::enable_if<
std::is_pointer<PointerType>::value, int>::type = 0>
@@ -22999,7 +23021,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// specialization to allow calling get_to with a basic_json value
// see https://github.com/nlohmann/json/issues/2175
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
template<typename ValueType,
detail::enable_if_t <
detail::is_basic_json<ValueType>::value,
@@ -23010,7 +23031,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return v;
}
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
template <
typename T, std::size_t N,
typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
@@ -23074,7 +23094,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@since version 1.0.0
*/
/// @sa https://json.nlohmann.me/api/basic_json/operator_ValueType/
template < typename ValueType, typename std::enable_if <
detail::conjunction <
detail::negation<std::is_pointer<ValueType>>,
@@ -23356,14 +23375,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
// (they seemingly cannot be constrained to resolve the ambiguity)
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
template<typename T>
reference operator[](T* key)
{
return operator[](typename object_t::key_type(key));
}
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
template<typename T>
const_reference operator[](T* key) const
{
@@ -23573,7 +23590,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
}
/// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, class BasicJsonType, detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value
&& detail::is_getable<basic_json_t, ValueType>::value
@@ -23584,7 +23600,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return value(ptr.convert(), default_value);
}
/// @sa https://json.nlohmann.me/api/basic_json/value/
template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
detail::enable_if_t <
detail::is_basic_json<BasicJsonType>::value
@@ -23954,7 +23969,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.contains(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/contains/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const
@@ -25237,7 +25251,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result;
}
/// @sa https://json.nlohmann.me/api/basic_json/parse/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
static basic_json parse(detail::span_input_adapter&& i,
@@ -25272,7 +25285,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @sa https://json.nlohmann.me/api/basic_json/accept/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
static bool accept(detail::span_input_adapter&& i,
@@ -25620,7 +25632,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
@@ -25632,7 +25643,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
static basic_json from_cbor(detail::span_input_adapter&& i,
@@ -25679,7 +25689,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
@@ -25690,7 +25699,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
static basic_json from_msgpack(detail::span_input_adapter&& i,
@@ -25736,7 +25744,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
@@ -25747,7 +25754,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
static basic_json from_ubjson(detail::span_input_adapter&& i,
@@ -25824,7 +25830,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
template<typename T>
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
@@ -25835,7 +25840,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return from_bson(ptr, ptr + len, strict, allow_exceptions);
}
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
JSON_HEDLEY_WARN_UNUSED_RESULT
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
static basic_json from_bson(detail::span_input_adapter&& i,
@@ -25865,7 +25869,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_unchecked(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
@@ -25880,7 +25883,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_unchecked(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
@@ -25895,7 +25897,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_checked(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/at/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
@@ -25910,7 +25911,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return ptr.get_checked(this);
}
/// @sa https://json.nlohmann.me/api/basic_json/at/
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+286
View File
@@ -778,6 +778,154 @@ class derived_person_only_serialize_private_3 : person_without_default_construct
NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(derived_person_only_serialize_private_3, person_without_default_constructor_3, "json_hair_color", hair_color)
};
// Zero-member types for issue #4041: NLOHMANN_DEFINE_TYPE_* and
// NLOHMANN_DEFINE_DERIVED_TYPE_* must compile and produce a valid (empty)
// JSON object when no member arguments are given.
class empty_intrusive
{
public:
bool operator==(const empty_intrusive& /*rhs*/) const
{
return true;
}
NLOHMANN_DEFINE_TYPE_INTRUSIVE(empty_intrusive)
};
class empty_intrusive_with_default
{
public:
bool operator==(const empty_intrusive_with_default& /*rhs*/) const
{
return true;
}
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(empty_intrusive_with_default)
};
class empty_intrusive_only_serialize
{
public:
NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(empty_intrusive_only_serialize)
};
class empty_non_intrusive
{
public:
bool operator==(const empty_non_intrusive& /*rhs*/) const
{
return true;
}
};
// NOLINTNEXTLINE(misc-use-internal-linkage)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(empty_non_intrusive)
class empty_non_intrusive_with_default
{
public:
bool operator==(const empty_non_intrusive_with_default& /*rhs*/) const
{
return true;
}
};
// NOLINTNEXTLINE(misc-use-internal-linkage)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(empty_non_intrusive_with_default)
class empty_non_intrusive_only_serialize {};
// NOLINTNEXTLINE(misc-use-internal-linkage)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(empty_non_intrusive_only_serialize)
class empty_derived_intrusive : public person_with_private_data
{
public:
empty_derived_intrusive() = default;
empty_derived_intrusive(std::string name_, int age_, json metadata_)
: person_with_private_data(std::move(name_), age_, std::move(metadata_))
{}
NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(empty_derived_intrusive, person_with_private_data)
};
class empty_derived_intrusive_with_default : public person_with_private_data
{
public:
empty_derived_intrusive_with_default() = default;
empty_derived_intrusive_with_default(std::string name_, int age_, json metadata_)
: person_with_private_data(std::move(name_), age_, std::move(metadata_))
{}
NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(empty_derived_intrusive_with_default, person_with_private_data)
};
class empty_derived_intrusive_only_serialize : public person_with_private_data
{
public:
empty_derived_intrusive_only_serialize() = default;
empty_derived_intrusive_only_serialize(std::string name_, int age_, json metadata_)
: person_with_private_data(std::move(name_), age_, std::move(metadata_))
{}
NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(empty_derived_intrusive_only_serialize, person_with_private_data)
};
class empty_derived_non_intrusive : public person_with_private_data
{
public:
empty_derived_non_intrusive() = default;
empty_derived_non_intrusive(std::string name_, int age_, json metadata_)
: person_with_private_data(std::move(name_), age_, std::move(metadata_))
{}
};
// NOLINTNEXTLINE(misc-use-internal-linkage)
NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(empty_derived_non_intrusive, person_with_private_data)
class empty_derived_non_intrusive_with_default : public person_with_private_data
{
public:
empty_derived_non_intrusive_with_default() = default;
empty_derived_non_intrusive_with_default(std::string name_, int age_, json metadata_)
: person_with_private_data(std::move(name_), age_, std::move(metadata_))
{}
};
// NOLINTNEXTLINE(misc-use-internal-linkage)
NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(empty_derived_non_intrusive_with_default, person_with_private_data)
class empty_derived_non_intrusive_only_serialize : public person_with_private_data
{
public:
empty_derived_non_intrusive_only_serialize() = default;
empty_derived_non_intrusive_only_serialize(std::string name_, int age_, json metadata_)
: person_with_private_data(std::move(name_), age_, std::move(metadata_))
{}
};
// NOLINTNEXTLINE(misc-use-internal-linkage)
NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(empty_derived_non_intrusive_only_serialize, person_with_private_data)
// Types at the documented maximum member count (63) for issue #4041's
// argument-count dispatch. The derived-type macros carry a two-token
// Type,BaseType prefix, so they reach two slots further into
// NLOHMANN_JSON_GET_MACRO than the non-derived ones and are the first to break
// if the tag dispatch runs out of positional slots.
class max_members
{
public:
int m1{}, m2{}, m3{}, m4{}, m5{}, m6{}, m7{}, m8{}, m9{}, m10{}, m11{}, m12{}, m13{}, m14{}, m15{}, m16{}, m17{}, m18{}, m19{}, m20{}, m21{}, m22{}, m23{}, m24{}, m25{}, m26{}, m27{}, m28{}, m29{}, m30{}, m31{}, m32{}, m33{}, m34{}, m35{}, m36{}, m37{}, m38{}, m39{}, m40{}, m41{}, m42{}, m43{}, m44{}, m45{}, m46{}, m47{}, m48{}, m49{}, m50{}, m51{}, m52{}, m53{}, m54{}, m55{}, m56{}, m57{}, m58{}, m59{}, m60{}, m61{}, m62{}, m63{};
NLOHMANN_DEFINE_TYPE_INTRUSIVE(max_members, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20, m21, m22, m23, m24, m25, m26, m27, m28, m29, m30, m31, m32, m33, m34, m35, m36, m37, m38, m39, m40, m41, m42, m43, m44, m45, m46, m47, m48, m49, m50, m51, m52, m53, m54, m55, m56, m57, m58, m59, m60, m61, m62, m63)
};
class max_members_base
{
public:
int base_value = 0;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(max_members_base, base_value)
};
class max_members_derived : public max_members_base
{
public:
int m1{}, m2{}, m3{}, m4{}, m5{}, m6{}, m7{}, m8{}, m9{}, m10{}, m11{}, m12{}, m13{}, m14{}, m15{}, m16{}, m17{}, m18{}, m19{}, m20{}, m21{}, m22{}, m23{}, m24{}, m25{}, m26{}, m27{}, m28{}, m29{}, m30{}, m31{}, m32{}, m33{}, m34{}, m35{}, m36{}, m37{}, m38{}, m39{}, m40{}, m41{}, m42{}, m43{}, m44{}, m45{}, m46{}, m47{}, m48{}, m49{}, m50{}, m51{}, m52{}, m53{}, m54{}, m55{}, m56{}, m57{}, m58{}, m59{}, m60{}, m61{}, m62{}, m63{};
NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(max_members_derived, max_members_base, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20, m21, m22, m23, m24, m25, m26, m27, m28, m29, m30, m31, m32, m33, m34, m35, m36, m37, m38, m39, m40, m41, m42, m43, m44, m45, m46, m47, m48, m49, m50, m51, m52, m53, m54, m55, m56, m57, m58, m59, m60, m61, m62, m63)
};
} // namespace persons
TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
@@ -1191,3 +1339,141 @@ TEST_CASE_TEMPLATE("Serialization of non-default-constructible classes via NLOHM
}
}
}
// Regression tests for issue #4041: NLOHMANN_DEFINE_TYPE_* and
// NLOHMANN_DEFINE_DERIVED_TYPE_* macros must compile and produce valid
// (empty, or base-only for the derived case) JSON objects when no member
// arguments are given, on every supported C++ standard.
TEST_CASE_TEMPLATE("Serialization/deserialization of zero-member types via NLOHMANN_DEFINE_TYPE_* (issue #4041)", Json, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
nlohmann::json, nlohmann::ordered_json)
{
constexpr bool is_ordered = std::is_same<Json, nlohmann::ordered_json>::value;
const char* const derived_dump = is_ordered
? R"({"age":1,"name":"Erik","metadata":null})"
: R"({"age":1,"metadata":null,"name":"Erik"})";
SECTION("NLOHMANN_DEFINE_TYPE_INTRUSIVE with zero members")
{
persons::empty_intrusive obj{};
Json j = obj;
CHECK(j.dump() == "{}");
CHECK(j.template get<persons::empty_intrusive>() == obj);
}
SECTION("NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT with zero members")
{
persons::empty_intrusive_with_default obj{};
Json j = obj;
CHECK(j.dump() == "{}");
CHECK(j.template get<persons::empty_intrusive_with_default>() == obj);
}
SECTION("NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE with zero members")
{
const persons::empty_intrusive_only_serialize obj{};
Json j = obj;
CHECK(j.dump() == "{}");
}
SECTION("NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE with zero members")
{
persons::empty_non_intrusive obj{};
Json j = obj;
CHECK(j.dump() == "{}");
CHECK(j.template get<persons::empty_non_intrusive>() == obj);
}
SECTION("NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT with zero members")
{
persons::empty_non_intrusive_with_default obj{};
Json j = obj;
CHECK(j.dump() == "{}");
CHECK(j.template get<persons::empty_non_intrusive_with_default>() == obj);
}
SECTION("NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE with zero members")
{
const persons::empty_non_intrusive_only_serialize obj{};
Json j = obj;
CHECK(j.dump() == "{}");
}
SECTION("NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE with zero own members")
{
persons::empty_derived_intrusive obj{"Erik", 1, nullptr};
Json j = obj;
CHECK(j.dump() == derived_dump);
CHECK(j.template get<persons::empty_derived_intrusive>() == obj);
}
SECTION("NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT with zero own members")
{
persons::empty_derived_intrusive_with_default obj{"Erik", 1, nullptr};
Json j = obj;
CHECK(j.dump() == derived_dump);
CHECK(j.template get<persons::empty_derived_intrusive_with_default>() == obj);
}
SECTION("NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE with zero own members")
{
const persons::empty_derived_intrusive_only_serialize obj{"Erik", 1, nullptr};
Json j = obj;
CHECK(j.dump() == derived_dump);
}
SECTION("NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE with zero own members")
{
persons::empty_derived_non_intrusive obj{"Erik", 1, nullptr};
Json j = obj;
CHECK(j.dump() == derived_dump);
CHECK(j.template get<persons::empty_derived_non_intrusive>() == obj);
}
SECTION("NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT with zero own members")
{
persons::empty_derived_non_intrusive_with_default obj{"Erik", 1, nullptr};
Json j = obj;
CHECK(j.dump() == derived_dump);
CHECK(j.template get<persons::empty_derived_non_intrusive_with_default>() == obj);
}
SECTION("NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE with zero own members")
{
const persons::empty_derived_non_intrusive_only_serialize obj{"Erik", 1, nullptr};
Json j = obj;
CHECK(j.dump() == derived_dump);
}
}
// Regression test for the argument-count dispatch added for issue #4041: the
// documented maximum of 63 members must keep working, including for the
// derived-type macros whose Type,BaseType prefix consumes two dispatch slots.
TEST_CASE_TEMPLATE("Serialization/deserialization of maximum-member-count types via NLOHMANN_DEFINE_TYPE_*", Json, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
nlohmann::json, nlohmann::ordered_json)
{
SECTION("NLOHMANN_DEFINE_TYPE_INTRUSIVE with 63 members")
{
persons::max_members obj{};
obj.m1 = 1;
obj.m63 = 63;
Json j = obj;
CHECK(j.size() == 63);
const auto obj2 = j.template get<persons::max_members>();
CHECK(obj2.m1 == 1);
CHECK(obj2.m63 == 63);
}
SECTION("NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE with 63 own members")
{
persons::max_members_derived obj{};
obj.base_value = 7;
obj.m1 = 1;
obj.m63 = 63;
Json j = obj;
CHECK(j.size() == 64);
const auto obj2 = j.template get<persons::max_members_derived>();
CHECK(obj2.base_value == 7);
CHECK(obj2.m1 == 1);
CHECK(obj2.m63 == 63);
}
}
-127
View File
@@ -1,127 +0,0 @@
# Public API Policy
This document defines what counts as the **public API** of nlohmann/json for the purposes of the
`tools/api_checker/` tooling, what stability is (and is not) guaranteed, and how API changes are
classified as breaking or feature additions. It exists so that "public API" is a checkable, mechanical
property of the source, not a matter of convention or memory — see
[Discussion #3691](https://github.com/nlohmann/json/discussions/3691) for the original motivation.
## What counts as public API
The public API surface is derived from C++ semantics (class templates, access specifiers, namespace
scoping) by `extract_api.py`, not from the presence of documentation. It consists of:
1. **Public members of six class templates**: `basic_json`, `adl_serializer`,
`byte_container_with_subtype`, `json_pointer`, `json_sax`, `ordered_map`. Specifically, the *primary
template definition* (`CLASS_TEMPLATE` cursor with `is_definition() == True`) — never an implicit
instantiation, which silently drops SFINAE-guarded overloads. Two tiers apply:
- **Callable tier** (methods, constructors, destructors, conversion operators, function templates):
every public callable requires its own `@sa` documentation link, without exception.
- **Type tier** (public type aliases): requires `@sa`, except for a fixed exemption list of
STL-container named-requirement aliases that mirror standard-library conventions rather than being
independently documented: `value_type`, `reference`, `const_reference`, `pointer`, `const_pointer`,
`iterator`, `const_iterator`, `reverse_iterator`, `const_reverse_iterator`, `difference_type`,
`size_type`, `allocator_type`, `key_type`, `mapped_type`. This list is defined once, in
`extract_api.py`'s `stl_exempt` set, and referenced here rather than duplicated.
2. **Free functions and operators in `nlohmann::`**, including `nlohmann::literals::json_literals`
(the `operator""_json` / `operator""_json_pointer` user-defined literals). Comparison and stream
operators on `basic_json` and `json_pointer`, `operator/`, `swap`.
3. **The six alias-exposed exception types**: `basic_json::exception`, `parse_error`,
`invalid_iterator`, `type_error`, `out_of_range`, `other_error`. These are public `TYPE_ALIAS_DECL`s
in `basic_json` (e.g. `using exception = detail::exception;`) whose underlying class is physically
defined in `nlohmann::detail::exceptions.hpp`. `extract_api.py` follows the alias to find the `@sa` on
the underlying `detail::` class definition, since that's where the existing documentation convention
places it. Each exception class is documented as *one page* — its individual members (`what()`, `id`)
are not separately tracked, matching the existing one-page-per-class convention.
4. **Macros**, governed separately by `docs/mkdocs/docs/api/macros/` (the curated list of ~30 pages is
authoritative) and cross-checked, advisory-only, by `check_macros.py`. See "Known limitations" below
for why macros can't be checked the same way as everything else.
## What's excluded
- **Everything in `nlohmann::detail::`**, with the one narrow exception above (exception-type aliases).
**No stability is guaranteed for any symbol in the `detail::` namespace — clients must not rely on it**,
regardless of whether a given `detail::` symbol happens to be reachable from user code today. This is
an explicit, deliberate policy, not an oversight.
- **Private and protected members**, even of the six tracked classes. `extract_api.py` walks non-public
members only to check they don't carry a stray `@sa` (a documentation leak) — never to add them to the
public surface.
- **The ABI inline-namespace segment** (`json_abi_v3_12_0`, `json_abi_diag_v3_12_0`, etc. — see
[docs/mkdocs/docs/features/namespace.md](../../docs/mkdocs/docs/features/namespace.md)). This is a
version-tag identity concern for `diff_api.py` (it must not make every release look like the entire API
was removed and re-added), not a scoping or visibility concern — the symbols inside it are public or
not independent of the tag.
## Breaking vs. feature classification
`diff_api.py` compares the `public_api` surface of two snapshots using an overload-disambiguating
identity — `(scope, identity_name, kind, signature)`, where `signature` is the declaration's own
source text (return type, name, parameter list, trailing qualifiers; comments and constructors'
member-initializer-lists stripped). This is the third identity scheme this tooling has used; the
first two were each found broken by testing against real release tags, not by inspection — see
`extract_api.py`'s `identity_key()` docstring for the full history (a naive params-only key silently
collided on overloads differing only by constness/SFINAE; libclang's USR fixed that but encoded the
*enclosing class template's own arity*, so a single backward-compatible template-parameter addition
made ~228 of 330 `basic_json` entries look "changed" between two real releases with zero actual
breaking changes among them).
- An identity present only in the **new** snapshot → **feature** (addition).
- An identity present only in the **old** snapshot → **breaking** (removal).
- The same `(scope, name)` with one identity removed and a different one added → reported as a
**changed overload**, classified breaking by default. No automatic overload-compatibility reasoning
is attempted — whether a signature change is source-compatible is a judgment call for a human
reviewer, not a sound problem for a CI tool to solve unsupervised.
## Stable per-release API history
`tools/api_checker/history/<tag>.json` holds one immutable, committed API-surface record per
released `v3.*` tag (captured via `snapshot_release.py`; see `tools/api_checker/README.md` and
`tools/api_checker/history/README.md`). `diff_api.py` reads from here automatically when a ref
matches a stored file, instead of live-extracting via `git archive` every time.
Every surface file (the live `api_surface.json` and every `history/*.json` record) carries a
`format_version` integer. **Bump it whenever a change to the schema, or to the identity-computing
algorithm (`get_signature_text()`/`get_identity_name()`/`identity_key()` in `extract_api.py`), could
alter the `signature` or `identity_name` text for otherwise-unchanged source.** `diff_api.py` refuses
by default to compare two surfaces with different `format_version` values — this is a direct,
mechanical safeguard against the exact class of bug that motivated the current identity scheme (see
above): a silent algorithm change corrupting every historical comparison with no way to detect it.
An explicit `--allow-format-mismatch` flag exists for a deliberate, informed comparison anyway.
Practical implication for anyone changing `extract_api.py`'s identity logic: bump
`SURFACE_FORMAT_VERSION`, and treat every already-committed `history/*.json` file as needing a
`--force` regeneration (reviewed, not blind) before it can be meaningfully compared against surfaces
produced by the new algorithm version.
## Stability guarantees
This tooling makes the project's existing commitments *mechanically checkable* — it does not introduce a
new promise:
- `ChangeLog.md` states the project "adheres to [Semantic Versioning](http://semver.org/)."
- [docs/mkdocs/docs/home/releases.md](../../docs/mkdocs/docs/home/releases.md) marks every 3.x release "All
changes are backward-compatible" (except 3.0.0 itself, a major bump with a migration guide).
- `tests/abi/` provides a complementary, narrower guarantee: ABI/link compatibility *within one ABI tag*.
That is a binary-compatibility concern; this tooling's concern is source/API-level — a symbol can be
ABI-stable and still be a source-breaking change (e.g. a removed overload), or vice versa.
## Known limitations
- **Macros have no C++ access-specifier concept**, so the public/private test that works for classes
doesn't transfer. Only a minority of documented macro `#define`s have an attached `@sa`-style comment
(the `#ifndef X / #define X value #endif` idiom has no natural comment-attachment point), and internal
helper macros live in the same files as public configuration macros, so no path-based exclusion works
either. `check_macros.py` therefore only checks one direction: that every documented macro still has a
matching `#define` somewhere under `include/nlohmann/` (catches stale/renamed doc pages). It does
**not** attempt to detect undocumented macros — no reliable signal exists for that direction with the
current codebase conventions, and this tool doesn't pretend otherwise.
- **`diff_api.py` does not track exception specifications or template-parameter-only changes.** A
function whose `noexcept` status changes, or whose template parameter list changes without altering
its externally-visible identity, will not be flagged.
- **Known API-hygiene gap, surfaced by this tooling rather than fixed**:
`basic_json::insert_iterator` (a `public` member per C++ access rules) is a helper used internally by
`insert()`; its own source comment calls it "Helper for insertion of an iterator." It has no
documentation page and is deliberately left undocumented rather than either speculatively documented or
silently exempted — this is exactly the class of undocumented-and-probably-shouldn't-be-public symbol
this tooling exists to surface, per the motivating discussion. A future PR may reclassify it as private
as a (breaking, ABI-relevant) cleanup; that is out of scope here.
-392
View File
@@ -1,392 +0,0 @@
# API Checker Tools
Tooling to extract, validate, and track changes to the public API surface of nlohmann/json.
## Overview
These tools use libclang AST parsing to programmatically derive "the public API" from C++ semantics
(class templates, access specifiers, namespace scoping) — independently of documentation status. The
extracted surface is the source of truth for what is considered "public API." On top of this, the tools
verify that every public entity carries a documentation link and detect API changes between releases.
A per-release historical record lives in [`history/`](history/README.md), one file per tagged `v3.*`
release, so past API changes can be derived without re-running libclang against old git refs.
See [POLICY.md](POLICY.md) for the full definition of what counts as public API, what's excluded, how
breaking vs. feature changes are classified, and known limitations.
## Installation
Install dependencies:
```bash
pip install -r tools/api_checker/requirements.txt
```
Requires:
- Python 3.7+
- libclang 18.1.1 (installed via pip)
- clang++ or clang (system package, for include-path discovery only — does not need to
version-match the pinned libclang wheel)
## Tools
### extract_api.py
Extract the public API surface by parsing C++ AST using libclang. Produces two different outputs for
two different consumers — see "Two outputs, one extraction pass" below for why.
**Usage:**
```bash
python3 tools/api_checker/extract_api.py \
--header include/nlohmann/json.hpp \
--include include \
--output api_snapshot.json \
--surface-output api_surface.json
```
**Options:**
- `--header PATH` — Header file to analyze (default: `include/nlohmann/json.hpp`)
- `--include PATH` — Include directory for parsing (default: `include`)
- `--output PATH` — Full snapshot output (location + doc status; default: `api_snapshot.json`)
- `--surface-output PATH` — Minimal surface output (identity only; omit to skip)
- `--extra-isystem PATH` — Extra `-isystem` include path (repeatable), in case system-include
discovery via `clang++ -E -v` ever fails on a given runner image
- `--self-test` — Run self-tests and exit (validates ABI-tag stripping)
**How it works:**
Parses the header file using libclang with `PARSE_DETAILED_PROCESSING_RECORD`, discovers system
includes via `clang++ -E -x c++ -v /dev/null`, and walks the AST:
1. For each of the 6 public class templates (`basic_json`, `adl_serializer`, `byte_container_with_subtype`,
`json_pointer`, `json_sax`, `ordered_map`): locate the `CLASS_TEMPLATE` cursor with `is_definition() == True`
— never a `CLASS_DECL` implicit instantiation, which silently drops SFINAE-guarded overloads
2. Extract public members: `CXX_METHOD`, `CONSTRUCTOR`, `DESTRUCTOR`, `CONVERSION_FUNCTION`, `FUNCTION_TEMPLATE`
(callable tier — strict `@sa` requirement), and `TYPE_ALIAS_DECL` (type tier — with STL-container exemptions)
3. Extract free functions in `nlohmann::` namespace (excluding `detail::` and `std::`)
4. Normalize away ABI inline-namespace (regex `::json_abi[a-z_]*_v\d+_\d+_\d+``::`)
5. Use overload-disambiguating identity keys built from raw source-text signature capture,
ABI-tag-stripped — see "Identity keys" below
**Two outputs, one extraction pass:**
- `--output` (full snapshot): each entry carries `location` (file:line) and documentation status
(`doc_url`, `has_sa`). Consumed by `check_docs.py`. **Not meant to be committed**`location` shifts
on any unrelated code edit and `doc_url` changes when doc pages move, so a diff of this file mixes real
API changes with pure noise.
- `--surface-output` (minimal surface): each entry has only `scope`, `kind`, `name`, `identity_name`,
`tier`, `signature`, and `pretty_signature` — nothing that can change without the API itself changing.
**This is the file that gets committed** (`tools/api_checker/api_surface.json` for the current
working tree, `tools/api_checker/history/<tag>.json` per released tag) and diffed by `diff_api.py`.
**Identity keys — three approaches were tried and rejected before arriving at the current one:**
1. `{scope, name, kind, params}` from `cursor.get_arguments()`: silently collided for any overload set
differentiated only by constness, ref-qualifiers, or SFINAE constraints rather than parameter types —
confirmed empirically: `basic_json`'s two zero-argument `get()` overloads (one `const`, one not) both
produced `params=[]` and silently overwrote each other. A full scan found **59 such silent overwrites
across 27 colliding names**.
2. libclang's USR: correctly disambiguates every overload (it encodes the full mangled signature) — but
*also* encodes the enclosing class template's own arity into every member's USR. Confirmed
empirically against real release tags: `basic_json` gaining one new defaulted template parameter
between v3.11.2 and v3.11.3 (a backward-compatible change) changed literally every member's USR,
which made `diff_api.py` report **228 of 330 entries as "changed"** for a release with zero real
breaking changes among them.
3. **Current approach**: raw source-text signature capture — `identity = (scope, identity_name, kind,
signature)`, where `signature` is the declaration's own text (return type, name, parameter list,
trailing cv/ref/noexcept qualifiers; comments and constructors' member-initializer-lists stripped;
stops before the function body) read directly via the cursor's byte-offset extent. This is what's
actually written in source — declared names like `ValueType`, never a resolved
`basic_json<T0,...,T10>` — so it's immune to the class-arity problem above. Verified by manually
cross-referencing every "added"/"removed"/"changed" entry in three real release-to-release diffs
(v3.11.2→v3.11.3, v3.11.3→v3.12.0, v3.12.0→HEAD) against the actual `git diff` of the source — every
one confirmed genuine. Full details, including several further edge-case fixes found the same way
(a libclang tokenizer gap, comment-stripping, constructor-name arity-poisoning), are in
`extract_api.py`'s `identity_key()`/`get_signature_text()` docstrings.
**Output (full snapshot, from `--output`):**
```json
{
"meta": {
"extracted_from": "include/nlohmann/json.hpp"
},
"public_api": {
"<opaque internal key, not meant to be read>": {
"scope": "nlohmann::basic_json",
"name": "parse",
"identity_name": "parse",
"kind": "CXX_METHOD",
"tier": "callable",
"signature": "static basic_json parse ( InputType && i , ... )",
"location": "include/nlohmann/json.hpp:4104",
"doc_url": "https://json.nlohmann.me/api/basic_json/parse/",
"has_sa": true,
"pretty_signature": "nlohmann::basic_json::parse"
}
},
"documented_non_public": []
}
```
`documented_non_public` lists entities **not** part of the public surface (private/protected members of
the six tracked classes) that surprisingly carry a real `@sa` URL — a genuine documentation leak. It does
not list public entries that merely lack `@sa`; that's `check_docs.py`'s job.
**Output (surface, from `--surface-output`):**
```json
{
"format_version": 1,
"meta": {
"extracted_from": "include/nlohmann/json.hpp"
},
"public_api": [
{
"scope": "nlohmann::basic_json",
"kind": "CXX_METHOD",
"name": "parse",
"identity_name": "parse",
"tier": "callable",
"signature": "static basic_json parse ( InputType && i , parser_callback_t cb = nullptr , const bool allow_exceptions = true , const bool ignore_comments = false )",
"pretty_signature": "nlohmann::basic_json::parse"
}
]
}
```
A flat, sorted list of self-describing records — `signature`/`identity_name` are the same values
`identity_key()` joins into one opaque internal string, exposed here as explicit fields so the file is
readable and diffable by inspection, not just by tooling. `identity_name` differs from `name` only for
constructors/destructors (a canonical `"(constructor)"`/`"(destructor)"` placeholder — see
`get_identity_name()`'s docstring for why `cursor.spelling` isn't used directly there).
`format_version` guards against a future change to this schema or to the identity-computing algorithm
silently corrupting a comparison against an older stored surface — bump it whenever such a change could
alter `signature`/`identity_name` text for otherwise-unchanged source (see `diff_api.py`).
### check_docs.py
Verify that all public API entries have valid documentation links and that no non-public entities
carry `@sa` comments.
**Usage:**
```bash
python3 tools/api_checker/check_docs.py --snapshot api_snapshot.json
```
**Options:**
- `--snapshot PATH` — Full API snapshot JSON file from `extract_api.py --output` (default: `api_snapshot.json`)
**How it works:**
Two-pass validation over the extracted snapshot:
1. For every `public_api` entry (callable tier, and type tier minus STL exemptions): flag if missing `@sa`,
flag if `@sa` URL doesn't resolve to an existing documentation file (following `docs/mkdocs/mkdocs.yml`'s
`redirect_maps` when a page has moved, and trying the class-overview conventions used by different
classes — flat `<class>.md`, nested `<class>/<class>.md`, nested `<class>/index.md`)
2. For every `documented_non_public` entry: flag as unexpected `@sa` on a non-public entity
**Output:**
Prints warnings for each issue, categorized by rule:
- `docs/missing_sa_comment` — public API without `@sa` documentation link
- `docs/missing_doc_file``@sa` URL points to non-existent `.md` file
- `docs/invalid_sa_url` — malformed `@sa` URL
- `docs/sa_on_non_public` — unexpected `@sa` on a non-public entity
Exits with status 0 if all checks pass, 1 if issues found.
### diff_api.py
Compare the public API surface between two refs and classify changes as feature (added) or
breaking (removed / changed overload).
**Usage:**
```bash
python3 tools/api_checker/diff_api.py --old v3.12.0 --new HEAD
python3 tools/api_checker/diff_api.py --old v3.11.2 --new v3.11.3 # both resolved from history/
python3 tools/api_checker/diff_api.py --old v3.12.0 --new HEAD --fail-on-breaking
python3 tools/api_checker/diff_api.py --old-file a.json --new-file b.json # compare two files directly
```
**Options:**
- `--old REF` / `--new REF` — Refs to compare (tags, branches, commits). `--new` defaults to `HEAD`.
Each is resolved by checking `tools/api_checker/history/<ref>.json` first (fast — no libclang or
git-archive needed), falling back to live extraction if no matching file exists there.
- `--old-file PATH` / `--new-file PATH` — Load an arbitrary surface JSON file directly, bypassing
both git and `tools/api_checker/history/`. Mutually exclusive with `--old`/`--new` respectively.
- `--no-history` — Force live extraction even when a matching `tools/api_checker/history/<ref>.json`
exists. Useful to check that a stored record is still faithful to a fresh run of the current tool.
- `--allow-format-mismatch` — Proceed even if the two surfaces have different `format_version`
(otherwise `diff_api.py` refuses — see below).
- `--header PATH` / `--include PATH` — For live extraction only (default:
`include/nlohmann/json.hpp` / `include`)
- `--fail-on-breaking` — Exit with status 1 if breaking changes are detected
**How it works:**
For a ref not found in `tools/api_checker/history/`, checks out the **full `include/` tree** at
that ref into a temp directory via `git archive` (a single-file checkout of `json.hpp` is not
enough — it `#include`s dozens of other headers that must exist at the same ref), then runs the
*current* `extract_api.py --surface-output` against it. Diffs the two surfaces by identity
(`scope`, `identity_name`, `kind`, `signature`):
- Identity only in the new surface → **feature**.
- Identity only in the old surface → **breaking** (removed).
- Same `(scope, name)` with a removed identity and an added identity → grouped as a **changed
overload**, breaking by default. No automatic overload-compatibility reasoning is attempted — a
human judges whether the change is actually source-compatible.
Before diffing, the two surfaces' `format_version` fields are compared; a mismatch aborts with an
error (override with `--allow-format-mismatch`) rather than silently producing an unsound diff — see
`extract_api.py`'s `SURFACE_FORMAT_VERSION` docstring for the incident that motivated this guard.
ABI-tag stripping is inherited automatically since both extractions go through the same
`extract_api.py`.
**Use case:** Run before cutting a release to verify the changelog correctly categorizes changes as
breaking vs. features.
### snapshot_release.py
Capture an immutable, per-release API surface record into `tools/api_checker/history/<tag>.json`.
This is what makes `diff_api.py` fast for released tags — see "How it works" above.
**Usage:**
```bash
python3 tools/api_checker/snapshot_release.py --ref v3.12.0
python3 tools/api_checker/snapshot_release.py --ref v3.11.0 --ref v3.12.0 # repeatable
python3 tools/api_checker/snapshot_release.py --all-tags # every v3.* tag
python3 tools/api_checker/snapshot_release.py --ref v3.12.0 --force # overwrite an existing record
```
**Options:**
- `--ref REF` — Git tag to snapshot; repeatable
- `--all-tags` — Snapshot every `v3.*` tag (existing files are skipped unless `--force`)
- `--output-dir PATH` — Where to write (default: `tools/api_checker/history/`)
- `--force` — Overwrite an existing history file. History files are immutable by convention —
only pass this for a deliberate, reviewed regeneration; review the resulting diff before
committing
- `--header PATH` / `--include PATH` — Same as `diff_api.py`'s live-extraction options
**How it works:** Reuses `diff_api.py`'s `extract_surface_for_ref()` for the git-archive-and-extract
work, then adds `generated_at`/`generator` provenance and writes the result to
`tools/api_checker/history/<ref>.json`. Given multiple refs (or `--all-tags`), does **not** abort
the batch on one failing ref — collects failures and prints a summary at the end, so one
unparseable old tag doesn't block backfilling the releases that do work. See
`tools/api_checker/history/README.md` for the currently-known gaps (pre-restructuring tags that
predate the `include/nlohmann/` layout entirely).
**Not CI-automated** — this is a manual step in the release checklist (see below), by design.
### check_macros.py
Advisory-only cross-check between documented macros and their `#define`/reference sites. **Never blocks
CI**, regardless of findings.
**Usage:**
```bash
python3 tools/api_checker/check_macros.py
```
**Options:**
- `--macros-dir PATH` — Directory of macro doc pages (default: `docs/mkdocs/docs/api/macros`)
- `--include-dir PATH` — Directory to search for `#define`/reference sites (default: `include/nlohmann`)
**How it works:**
For each `.md` page under `docs/mkdocs/docs/api/macros/` (excluding `index.md`), extracts the macro
name(s) from the H1 heading — handling both plain single-macro headings and multi-line HTML headings
that list a family of related macros (e.g. the `NLOHMANN_DEFINE_DERIVED_TYPE_*` family) — then checks
whether each name is defined **or referenced** (`#define`, `#ifdef`, `#ifndef`, `defined(...)`) anywhere
under `include/nlohmann/`. Referenced-but-not-defined is deliberately accepted: macros like
`JSON_NOEXCEPTION` or `JSON_THROW_USER` are user-supplied overrides that the library only checks for,
never defines itself.
Only checks the documented-macro-still-exists direction (catches stale/renamed doc pages). Does **not**
check the converse (undocumented macros) — see [POLICY.md](POLICY.md)'s "Known limitations" for why.
## Continuous Integration
A GitHub Actions workflow (`.github/workflows/check_api_docs.yml`) runs on every pull request:
1. Installs `clang` (system package, for include discovery) and Python dependencies
2. Runs `extract_api.py`, regenerating both the ephemeral full snapshot and the tracked
`tools/api_checker/api_surface.json`
3. Runs `check_docs.py`**Phase 1: advisory** (`continue-on-error: true`), surfacing the doc backlog
without failing the job while it's burned down. Will flip to blocking once the backlog is cleared.
4. Runs `check_macros.py` — always advisory
5. Diffs `tools/api_checker/api_surface.json` against the regenerated copy — **blocking from the start**
(unlike the doc-backlog check, this is purely mechanical regeneration with no backlog to phase in,
matching the precedent set by `check_amalgamation.yml`). Uploads a patch artifact if it differs, so
contributors can `git apply` it instead of installing libclang locally.
## Workflow: Adding New Public API
1. Add the new public method/function to the header
2. Add a `/// @sa https://json.nlohmann.me/api/<class>/<member>/` comment above it
3. Create the corresponding documentation page at `docs/mkdocs/docs/api/<class>/<member>.md` and a
`mkdocs.yml` nav entry
4. Regenerate and commit the tracked surface file:
```bash
python3 tools/api_checker/extract_api.py --surface-output tools/api_checker/api_surface.json
git add tools/api_checker/api_surface.json
```
5. Push your PR — CI verifies both the doc link and that the surface file is up to date
## Workflow: Release Checklist
Before cutting a release:
```bash
# Diff current API against the previous release
python3 tools/api_checker/diff_api.py --old v3.12.0 --new HEAD
# Review the output to verify the changelog correctly categorizes breaking vs. feature changes
```
After tagging the release:
```bash
# Capture and commit the new tag's API surface, so future diffs against it hit the fast,
# stored-file path instead of live-extracting every time. A manual step, not CI-automated.
python3 tools/api_checker/snapshot_release.py --ref v3.13.0
git add tools/api_checker/history/v3.13.0.json
```
## Troubleshooting
**libclang not found:**
```
Error: Could not locate libclang library
```
→ Ensure libclang is installed: `python3 -m pip install libclang==18.1.1`
**Parse errors in header:**
```
Parse errors encountered:
... list of diagnostics ...
```
→ Check that system includes can be discovered. Run `clang++ -E -x c++ -v /dev/null` and verify
the output includes a section titled `#include <...> search starts here:` with system paths.
If include discovery fails, use `--extra-isystem PATH` to provide additional paths.
**No API entries extracted (or very few):**
- Check that the header file exists and is valid C++: `ls -la include/nlohmann/json.hpp`
- Verify that no parse errors occur above
- Confirm that you're targeting a `CLASS_TEMPLATE` definition, not an implicit instantiation
(the tool logs `Found N public API entries` — a zero or very small count suggests the wrong cursor kind)
**Doc link returns 404:**
- Verify the file exists at the expected path: `docs/mkdocs/docs/api/<class>/<member>.md`
- Check for URL encoding issues (e.g., `operator[]``operator%5B%5D`)
- Verify the URL structure in the `@sa` comment: should be `https://json.nlohmann.me/api/<path>/`
- Check `docs/mkdocs/mkdocs.yml`'s `redirect_maps` if the page has moved
**`tools/api_checker/api_surface.json` is out of date in CI:**
→ Regenerate and commit it: `python3 tools/api_checker/extract_api.py --surface-output tools/api_checker/api_surface.json`
**`diff_api.py` refuses with "format_version mismatch":**
→ One side is a stored surface (live `api_surface.json` or a `tools/api_checker/history/*.json`
file) captured with an older/newer version of `extract_api.py`'s identity-computing algorithm than
the other side. Comparing them directly could produce an unsound diff (this is exactly the failure
mode that motivated adding the check — see `extract_api.py`'s `SURFACE_FORMAT_VERSION` docstring).
Either regenerate the older side with the current tool, or pass `--allow-format-mismatch` if you
understand the risk and want to proceed anyway.
## Contributing
Report bugs or suggest improvements to [Discussion #3691](https://github.com/nlohmann/json/discussions/3691).
Read [POLICY.md](POLICY.md) first for the definition of public API this tooling enforces.
File diff suppressed because it is too large Load Diff
-182
View File
@@ -1,182 +0,0 @@
#!/usr/bin/env python3
"""Verify that public API entries have documentation links.
Consumes an API snapshot from extract_api.py and checks:
1. Every public callable/type-tier entry has an @sa comment (with exceptions)
2. Every @sa URL resolves to an existing documentation file
3. No @sa comments appear on non-public entities
"""
import argparse
import json
import os
import re
import subprocess
import sys
from urllib.parse import unquote
warnings = 0
# STL-container-named-requirement aliases that are exempt from @sa requirement
STL_EXEMPT = {'value_type', 'reference', 'const_reference', 'pointer', 'const_pointer',
'iterator', 'const_iterator', 'reverse_iterator', 'const_reverse_iterator',
'difference_type', 'size_type', 'allocator_type', 'key_type', 'mapped_type'}
def get_repo_root():
"""Find the repository root via git, so this script works regardless of invoking CWD."""
try:
result = subprocess.run(
['git', 'rev-parse', '--show-toplevel'],
capture_output=True, text=True, check=True, timeout=10
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
return os.getcwd()
REPO_ROOT = get_repo_root()
MKDOCS_YML = os.path.join(REPO_ROOT, 'docs', 'mkdocs', 'mkdocs.yml')
def load_redirect_map() -> dict:
"""Parse the redirect_maps block of docs/mkdocs/mkdocs.yml: {old_relative_path: new_relative_path}.
mkdocs' redirect plugin lets a doc page move without breaking existing @sa URLs -- e.g.
'api/basic_json/operator_ltlt.md' redirects to the real file at 'api/operator_ltlt.md'.
Without consulting this map, url_to_docfile() would flag every redirected URL as a broken link.
"""
if not os.path.exists(MKDOCS_YML):
return {}
with open(MKDOCS_YML) as f:
content = f.read()
redirect_map = {}
for m in re.finditer(r"^\s*'([^']+\.md)':\s*(\S+\.md)\s*$", content, re.MULTILINE):
redirect_map[m.group(1)] = m.group(2)
return redirect_map
REDIRECT_MAP = load_redirect_map()
def report(rule: str, location: str, description: str):
"""Report a documentation issue."""
global warnings
warnings += 1
print(f'{warnings:3}. {location}: {description} [{rule}]')
def url_to_docfile(url: str) -> str | None:
"""Convert @sa URL to the documentation file it resolves to, following mkdocs redirects."""
if not url:
return None
# Extract path after /api/
match = re.search(r'/api/(.+?)/?$', url)
if not match:
return None
# URL-decode each path component
path_parts = [unquote(part) for part in match.group(1).split('/')]
relative_path = 'api/' + '/'.join(path_parts) + '.md'
docs_root = os.path.join(REPO_ROOT, 'docs', 'mkdocs', 'docs')
direct_path = os.path.join(docs_root, relative_path)
# Class-overview URLs (a single path segment after /api/, e.g. ".../api/json_pointer/")
# use inconsistent on-disk conventions across classes: ordered_map.md is a flat file,
# byte_container_with_subtype/byte_container_with_subtype.md nests under a same-named
# file, json_pointer/index.md nests under index.md. Try all observed conventions.
candidates = [direct_path]
if len(path_parts) == 1:
stem = path_parts[0]
candidates.append(os.path.join(docs_root, 'api', stem, 'index.md'))
candidates.append(os.path.join(docs_root, 'api', stem, stem + '.md'))
redirected = REDIRECT_MAP.get(relative_path)
if redirected:
candidates.append(os.path.join(docs_root, redirected))
for candidate in candidates:
if os.path.exists(candidate):
return candidate
# Nothing resolved -- return the direct path so the caller reports a meaningful "missing" location.
return direct_path
def check_docs(snapshot_path: str):
"""Check documentation for all API entries."""
if not os.path.exists(snapshot_path):
print(f"Error: Snapshot file not found: {snapshot_path}")
sys.exit(1)
with open(snapshot_path, 'r') as f:
data = json.load(f)
api = data.get('public_api', {})
documented_non_public = data.get('documented_non_public', [])
print(f"Checking documentation for {len(api)} API entries...")
print(120 * "-")
# Check public API entries
for _identity_key, entry in sorted(api.items()):
location = entry.get('location', 'unknown')
name = entry.get('name', '?')
tier = entry.get('tier', '?')
doc_url = entry.get('doc_url')
has_sa = entry.get('has_sa', False)
# Skip type_exempt tier entries
if tier == 'type_exempt':
continue
# Check for missing @sa
if not has_sa:
report('docs/missing_sa_comment', location,
f'public API "{name}" (tier: {tier}) has no @sa documentation link')
continue
# Verify URL resolves to an existing file
doc_file = url_to_docfile(doc_url)
if not doc_file:
report('docs/invalid_sa_url', location,
f'public API "{name}" has invalid @sa URL: {doc_url}')
continue
if not os.path.exists(doc_file):
display_path = os.path.relpath(doc_file, REPO_ROOT)
report('docs/missing_doc_file', location,
f'public API "{name}" @sa URL points to non-existent file: {display_path}')
# Check documented_non_public entries
for entry in documented_non_public:
location = entry.get('location', 'unknown')
reason = entry.get('reason', '?')
report('docs/sa_on_non_public', location,
f'@sa comment found on non-public entity: {reason}')
print(120 * "-")
if warnings > 0:
print(f"\nFound {warnings} documentation issues")
return False
else:
print("\nAll public API entries are properly documented ✓")
return True
def main():
parser = argparse.ArgumentParser(description='Check documentation for public API')
parser.add_argument('--snapshot', default='api_snapshot.json',
help='Path to API snapshot JSON file')
args = parser.parse_args()
if not check_docs(args.snapshot):
sys.exit(1)
if __name__ == '__main__':
main()
-131
View File
@@ -1,131 +0,0 @@
#!/usr/bin/env python3
"""Advisory-only cross-check between documented macros and their #define sites.
Macros have no C++ access-specifier concept, so the AST-based public/private test that
extract_api.py uses for classes doesn't transfer -- see tools/api_checker/POLICY.md's "Known
limitations" section. This script only checks one direction: that every macro documented under
docs/mkdocs/docs/api/macros/ still has a matching #define somewhere under include/nlohmann/,
catching stale or renamed doc pages. It does NOT check the converse (undocumented macros) --
no reliable signal exists for that direction given this codebase's conventions.
Never blocks CI -- always exits 0, even when it reports findings.
"""
import argparse
import glob
import os
import re
import sys
MACRO_TOKEN_RE = re.compile(r'\b([A-Z][A-Z0-9_]*)\b')
warnings = 0
def report(location: str, description: str):
global warnings
warnings += 1
print(f'{warnings:3}. {location}: {description}')
def extract_macro_names(md_path: str) -> list:
"""Extract macro name(s) from a doc page's H1 heading.
Most pages use a single-line markdown heading ('# JSON_ASSERT'). A few document a family of
related macros under one page using a multi-line HTML heading listing comma-separated names
(e.g. NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, ..._WITH_DEFAULT, ..._ONLY_SERIALIZE). Handle
both: collect the heading text (markdown '# ...' line, or everything between <h1> and </h1>,
which may span multiple lines), then split on commas.
"""
with open(md_path) as f:
content = f.read()
heading_text = None
html_match = re.search(r'<h1>(.*?)</h1>', content, re.DOTALL)
if html_match:
heading_text = html_match.group(1)
else:
for line in content.splitlines():
if line.startswith('# '):
heading_text = line[2:]
break
if not heading_text:
return []
names = []
for part in heading_text.split(','):
match = MACRO_TOKEN_RE.search(part)
if match:
names.append(match.group(1))
return names
def macro_is_referenced(macro_name: str, include_dir: str) -> bool:
"""Check whether macro_name is defined OR referenced (#ifdef/#ifndef/defined()) anywhere
under include_dir.
Some documented macros (e.g. JSON_NOEXCEPTION, JSON_THROW_USER) are user-supplied overrides:
the library only checks whether they're defined, it never #defines them itself. Requiring a
literal #define would make this advisory check permanently noisy for that whole category, so
presence of any reference is treated as "this macro still exists in the codebase."
"""
pattern = re.compile(
r'#\s*define\s+' + re.escape(macro_name) + r'\b'
r'|#\s*ifn?def\s+' + re.escape(macro_name) + r'\b'
r'|defined\s*\(\s*' + re.escape(macro_name) + r'\s*\)'
r'|defined\s+' + re.escape(macro_name) + r'\b'
)
for root, _dirs, files in os.walk(include_dir):
for fname in files:
if not fname.endswith('.hpp'):
continue
path = os.path.join(root, fname)
with open(path, encoding='utf-8', errors='ignore') as f:
if pattern.search(f.read()):
return True
return False
def check_macros(macros_dir: str, include_dir: str) -> bool:
md_files = sorted(glob.glob(os.path.join(macros_dir, '*.md')))
md_files = [f for f in md_files if os.path.basename(f) != 'index.md']
print(f"Checking {len(md_files)} documented macros against #define sites in {include_dir}...")
print(120 * "-")
for md_path in md_files:
macro_names = extract_macro_names(md_path)
if not macro_names:
report(md_path, "could not extract macro name(s) from H1 heading")
continue
for macro_name in macro_names:
if not macro_is_referenced(macro_name, include_dir):
report(md_path, f'documented macro "{macro_name}" is not defined or referenced under {include_dir}')
print(120 * "-")
if warnings > 0:
print(f"\nFound {warnings} stale/renamed macro doc pages (advisory only, not blocking)")
else:
print("\nAll documented macros have a matching #define ✓")
return warnings == 0
def main():
parser = argparse.ArgumentParser(description='Advisory cross-check of documented macros vs. #define sites')
parser.add_argument('--macros-dir', default='docs/mkdocs/docs/api/macros',
help='Directory containing macro documentation pages')
parser.add_argument('--include-dir', default='include/nlohmann',
help='Directory to search for #define sites')
args = parser.parse_args()
check_macros(args.macros_dir, args.include_dir)
# Advisory only -- never fail CI regardless of findings.
sys.exit(0)
if __name__ == '__main__':
main()
-295
View File
@@ -1,295 +0,0 @@
#!/usr/bin/env python3
"""Diff the public API surface between two refs to flag breaking vs. feature changes.
A "ref" for --old/--new is resolved in this order:
1. A stored, committed historical record at tools/api_checker/history/<ref>.json, if one exists
and --no-history wasn't passed (fast path -- no libclang/git-archive needed).
2. Live extraction: check the ref out via `git archive` into a temp dir and run extract_api.py
against it (needed for HEAD, branches, or any tag not yet backfilled into history/).
--old-file/--new-file bypass both and load an arbitrary surface JSON file directly.
Uses extract_api.py's --surface-output (identity-only: scope, kind, name, identity_name, tier,
signature, pretty_signature -- no location, no doc_url) for both sides, so the diff reflects only
genuine API changes, never unrelated code motion or documentation-site restructuring. Identity is
(scope, identity_name, kind, signature) -- see extract_api.py's identity_key()/get_signature_text()
docstrings for the full history of why this is what it is: a naive {scope,name,kind,params} key
silently collided on overloads differing only by constness/SFINAE; switching to libclang's USR
fixed that but encoded the *enclosing class template's own arity* into every member's identity, so
a single backward-compatible template-parameter addition (confirmed via real release tags
v3.11.2->v3.11.3) made ~228 of 330 entries look "changed" for a release with no real breaking
changes. The current raw-source-text-signature approach was arrived at, and each of several further
refinements verified, by testing against real historical releases -- not by inspecting code alone.
"""
import argparse
import json
import os
import subprocess
import sys
import tempfile
from collections import defaultdict
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
HISTORY_DIR = os.path.join(SCRIPT_DIR, 'history')
# The format_version this build of diff_api.py understands. Kept in sync with
# extract_api.py's SURFACE_FORMAT_VERSION by convention (both bump together whenever the
# identity-computing algorithm changes) -- imported directly so there's exactly one source of
# truth, not two constants that can drift apart.
sys.path.insert(0, SCRIPT_DIR)
from extract_api import SURFACE_FORMAT_VERSION # noqa: E402
def resolve_commit_sha(ref: str) -> str | None:
"""Resolve a ref to its full commit sha, or None if that fails."""
try:
result = subprocess.run(
['git', 'rev-parse', ref],
capture_output=True, text=True, check=True, timeout=10
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
return None
def extract_surface_for_ref(ref: str, header: str = 'include/nlohmann/json.hpp',
include: str = 'include') -> dict:
"""Check out the full include/ tree at `ref` into a temp dir and extract its API surface.
A single-file checkout of json.hpp is not enough: json.hpp #includes dozens of other
headers under nlohmann/detail/ that must exist at the same ref for parsing to succeed.
Returns the full loaded surface JSON (format_version, meta, public_api list), with `ref` and
the resolved commit sha recorded in meta -- callers that only care about the diff can ignore
these; snapshot_release.py uses them as a historical record's provenance.
"""
with tempfile.TemporaryDirectory(prefix='api_checker_') as tmpdir:
archive = subprocess.run(
['git', 'archive', ref, '--', 'include'],
capture_output=True, check=True
)
subprocess.run(['tar', '-x', '-C', tmpdir], input=archive.stdout, check=True)
ref_include = os.path.join(tmpdir, include)
ref_header = os.path.join(tmpdir, header)
if not os.path.exists(ref_header):
print(f"Error: {header} does not exist at ref {ref}")
sys.exit(1)
surface_output = os.path.join(tmpdir, 'surface.json')
result = subprocess.run(
[sys.executable, os.path.join(SCRIPT_DIR, 'extract_api.py'),
'--header', ref_header,
'--include', ref_include,
'--output', os.path.join(tmpdir, 'snapshot.json'),
'--surface-output', surface_output],
capture_output=True, text=True
)
if result.returncode != 0:
print(f"Error extracting API at ref {ref}:")
print(result.stdout)
print(result.stderr)
sys.exit(1)
with open(surface_output) as f:
surface = json.load(f)
# extract_api.py resolved 'extracted_from' relative to *its own* invocation cwd/repo-root
# detection, which inside this temp checkout is meaningless (a path like
# "../../../../var/folders/.../tmp.XXXX/include/nlohmann/json.hpp") -- overwrite with the
# stable, repo-relative header path actually passed in, so a stored history file's metadata
# doesn't embed a throwaway temp directory name.
surface.setdefault('meta', {})['extracted_from'] = header
surface['meta']['ref'] = ref
commit_sha = resolve_commit_sha(ref)
if commit_sha:
surface['meta']['commit'] = commit_sha
return surface
def load_surface_file(path: str) -> dict:
"""Load a surface JSON file from disk (a stored history/ record or an explicit --old-file/--new-file)."""
with open(path) as f:
return json.load(f)
def resolve_surface(ref: str | None, explicit_file: str | None, header: str, include: str,
no_history: bool) -> tuple:
"""Resolve one side of a diff. Returns (surface_dict, description_string_for_display)."""
if explicit_file:
return load_surface_file(explicit_file), f"file:{explicit_file}"
history_path = os.path.join(HISTORY_DIR, f"{ref}.json")
if not no_history and os.path.exists(history_path):
return load_surface_file(history_path), f"history:{ref}"
return extract_surface_for_ref(ref, header, include), f"live:{ref}"
def build_identity_dict(public_api: list) -> dict:
"""Group a surface's public_api list by identity for diffing.
Identity is (scope, identity_name, kind, signature) -- the same four components
extract_api.py's identity_key() joins into one opaque string internally, exposed here as
explicit fields on each record (see extract_api.py's write_surface()) so this reconstruction
is a plain, visible tuple lookup rather than decoding anything.
"""
return {
(entry['scope'], entry['identity_name'], entry['kind'], entry['signature']): entry
for entry in public_api
}
def diff_surfaces(old_api: dict, new_api: dict) -> dict:
"""Compare two API surfaces by identity key, grouping same-(scope,name) changes."""
old_keys = set(old_api.keys())
new_keys = set(new_api.keys())
added_keys = new_keys - old_keys
removed_keys = old_keys - new_keys
# Group removed/added entries by (scope, name) to detect "changed overload" pairs,
# rather than reporting a same-named removal+addition as two unrelated changes.
removed_by_name = defaultdict(list)
for key in removed_keys:
entry = old_api[key]
removed_by_name[(entry['scope'], entry['name'])].append(key)
added_by_name = defaultdict(list)
for key in added_keys:
entry = new_api[key]
added_by_name[(entry['scope'], entry['name'])].append(key)
changed = []
pure_added = []
pure_removed = []
for name, removed_list in removed_by_name.items():
if name in added_by_name:
added_list = added_by_name.pop(name)
for key in removed_list:
changed.append({'scope': name[0], 'name': name[1],
'old': old_api[key]['pretty_signature'],
'new': new_api[added_list[0]]['pretty_signature']})
else:
for key in removed_list:
pure_removed.append(old_api[key])
for name, added_list in added_by_name.items():
for key in added_list:
pure_added.append(new_api[key])
return {
'added': sorted(pure_added, key=lambda e: e['pretty_signature']),
'removed': sorted(pure_removed, key=lambda e: e['pretty_signature']),
'changed': sorted(changed, key=lambda e: (e['scope'], e['name'])),
}
def print_diff(diff: dict, old_desc: str, new_desc: str) -> bool:
"""Print a human-readable diff report. Returns True if breaking changes were found."""
print(120 * "-")
print(f"API Diff: {old_desc} -> {new_desc}")
print(120 * "-")
if diff['added']:
print(f"\nAdded ({len(diff['added'])} entries) -- feature:")
for entry in diff['added']:
print(f" + {entry['pretty_signature']}")
if diff['removed']:
print(f"\nRemoved ({len(diff['removed'])} entries) -- BREAKING:")
for entry in diff['removed']:
print(f" - {entry['pretty_signature']}")
if diff['changed']:
print(f"\nChanged overloads ({len(diff['changed'])} entries) -- BREAKING by default:")
for entry in diff['changed']:
print(f" ~ {entry['old']} -> {entry['new']}")
if not any([diff['added'], diff['removed'], diff['changed']]):
print("\nNo API changes detected")
print(120 * "-")
breaking = bool(diff['removed'] or diff['changed'])
if breaking:
print("\nWARNING: Potential BREAKING CHANGES detected:")
print(f" - {len(diff['removed'])} removed entries")
print(f" - {len(diff['changed'])} changed overloads")
if diff['added']:
print(f"\nNew features: {len(diff['added'])} added entries")
return breaking
def main():
parser = argparse.ArgumentParser(description='Diff public API surface between two refs')
parser.add_argument('--old', help='Old git ref (tag/commit) -- checks tools/api_checker/history/ first, '
'then falls back to live extraction')
parser.add_argument('--new', help='New git ref (default: HEAD unless --new-file is given)')
parser.add_argument('--old-file', help='Path to a stored surface JSON file for the "old" side '
'(bypasses git and tools/api_checker/history/ entirely)')
parser.add_argument('--new-file', help='Path to a stored surface JSON file for the "new" side '
'(bypasses git and tools/api_checker/history/ entirely)')
parser.add_argument('--no-history', action='store_true',
help='Force live extraction even if a matching tools/api_checker/history/<ref>.json '
'exists -- useful to check a stored record is still faithful to a fresh run')
parser.add_argument('--allow-format-mismatch', action='store_true',
help='Proceed even if the two surfaces have different format_version (results may '
'be unsound -- the identity algorithm may differ between versions)')
parser.add_argument('--header', default='include/nlohmann/json.hpp',
help='Header file to analyze (relative to repo root, live-extraction only)')
parser.add_argument('--include', default='include',
help='Include directory (relative to repo root, live-extraction only)')
parser.add_argument('--fail-on-breaking', action='store_true',
help='Exit with status 1 if breaking changes are detected')
args = parser.parse_args()
if args.old and args.old_file:
parser.error('--old and --old-file are mutually exclusive')
if not args.old and not args.old_file:
parser.error('one of --old or --old-file is required')
if args.new and args.new_file:
parser.error('--new and --new-file are mutually exclusive')
new_ref = args.new or ('HEAD' if not args.new_file else None)
print(f"Resolving old surface ({args.old_file or args.old})...")
old_surface, old_desc = resolve_surface(args.old, args.old_file, args.header, args.include, args.no_history)
print(f"Resolving new surface ({args.new_file or new_ref})...")
new_surface, new_desc = resolve_surface(new_ref, args.new_file, args.header, args.include, args.no_history)
old_fmt = old_surface.get('format_version')
new_fmt = new_surface.get('format_version')
# Check both surfaces against SURFACE_FORMAT_VERSION (what *this build* of diff_api.py
# understands), not just against each other: two surfaces on the same format_version could
# still both be on a version this build predates and doesn't correctly interpret.
mismatched = old_fmt != SURFACE_FORMAT_VERSION or new_fmt != SURFACE_FORMAT_VERSION
if mismatched and not args.allow_format_mismatch:
print(f"Error: format_version mismatch. This build of diff_api.py understands "
f"format_version {SURFACE_FORMAT_VERSION!r}, but {old_desc} is {old_fmt!r} and "
f"{new_desc} is {new_fmt!r}.")
print("The identity/signature algorithm may differ between these two surfaces, making a diff unsound.")
print("Re-run with --allow-format-mismatch to proceed anyway.")
sys.exit(1)
elif mismatched:
print(f"WARNING: proceeding with mismatched format_version (this build understands "
f"{SURFACE_FORMAT_VERSION!r}; {old_desc}={old_fmt!r}, {new_desc}={new_fmt!r}) as requested.")
old_api = build_identity_dict(old_surface['public_api'])
new_api = build_identity_dict(new_surface['public_api'])
print(f"\nComparing {len(old_api)} old entries with {len(new_api)} new entries...")
diff = diff_surfaces(old_api, new_api)
breaking = print_diff(diff, old_desc, new_desc)
if args.fail_on_breaking and breaking:
sys.exit(1)
if __name__ == '__main__':
main()
-668
View File
@@ -1,668 +0,0 @@
#!/usr/bin/env python3
"""Extract the public API surface of nlohmann/json using libclang AST.
This tool derives the public API from C++ semantics (class templates, access specifiers,
namespace scoping) independently of documentation status. The extracted surface is the
source of truth for what is considered "public API" doc-checking and API diffing are
downstream consumers of this snapshot.
Strategy:
1. Parse include/nlohmann/json.hpp with libclang (with proper system includes)
2. Walk the primary class-template definitions of the 6 known public classes
3. Extract callable members (methods, constructors, destructors, conversion ops) and type aliases
4. Extract free functions/operators in nlohmann:: (excluding detail::)
5. Handle alias-exposed exception types by following the alias to the detail:: definition
6. Normalize away the ABI inline-namespace (json_abi_v3_12_0, json_abi_diag_v3_12_0, etc.)
7. Emit a snapshot with an overload-disambiguating identity key and documentation status
Output includes both public_api (all tracked public entities) and documented_non_public
(entities with @sa comments that are NOT in the public surface used for validation).
"""
import argparse
import json
import os
import re
import subprocess
import sys
try:
from clang import cindex
except ImportError:
print("Error: libclang not installed. Run: pip install -r tools/api_checker/requirements.txt")
sys.exit(1)
ABI_TAG_PATTERN = re.compile(r'::json(?:_abi)?[a-z_]*_v\d+_\d+_\d+(?=::|$)')
# Bump whenever a change to this schema, or to the identity-computing algorithm
# (get_signature_text()/get_identity_name()/identity_key()), could alter the 'signature' or
# 'identity_name' text for otherwise-unchanged source. diff_api.py refuses by default to compare
# two surface files with different SURFACE_FORMAT_VERSION -- see diff_api.py and
# tools/api_checker/POLICY.md for why: an earlier, unversioned identity scheme (based on
# libclang's USR) silently corrupted every historical comparison whenever the *unrelated*
# enclosing class template gained a new template parameter, and nothing caught it because there
# was no version to check.
SURFACE_FORMAT_VERSION = 3
def strip_abi_tag(text: str) -> str:
"""Remove ABI inline-namespace from a qualified name or type string."""
return ABI_TAG_PATTERN.sub('', text)
_FILE_CONTENT_CACHE = {}
def _read_file_cached(path: str) -> str:
if path not in _FILE_CONTENT_CACHE:
try:
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
_FILE_CONTENT_CACHE[path] = f.read()
except OSError:
_FILE_CONTENT_CACHE[path] = ''
return _FILE_CONTENT_CACHE[path]
def get_signature_text(cursor) -> str:
"""The normalized source text of a declaration's own signature -- template header, return
type, name, full parameter list, and trailing cv/ref/noexcept qualifiers -- stopping before
the function body ('{') or at the terminating ';'/'= default;'/'= 0;'. Never includes the
body, so implementation-only changes don't affect identity.
Reads raw source text via cursor.extent's byte offsets rather than cursor.get_tokens():
the latter was found to silently return zero tokens whenever a cursor's extent starts
exactly at an unexpanded macro invocation (confirmed empirically for basic_json's four
binary() overloads and iterator_wrapper(), both preceded by JSON_HEDLEY_WARN_UNUSED_RESULT
-- a known class of libclang tokenizer edge case, not specific to this codebase). Raw text
also avoids cursor.get_arguments()/cursor.type.spelling, which were separately found
empty/unreliable for some FUNCTION_TEMPLATE cursors with complex trailing
noexcept(noexcept(...))/decltype(...) clauses (adl_serializer::from_json). Source text is
what's actually written (declared names like "ValueType", never a resolved
"basic_json<T0,...,T10>"), so it's immune to the enclosing class's template arity too --
see identity_key()'s docstring for why that matters.
Comments are stripped while scanning. Without this, a purely cosmetic NOLINT annotation
added mid-signature (e.g. `KeyType&& key) // NOLINT(...)` before the body's '{') would
change the captured text -- confirmed empirically via a real nlohmann/json release
(v3.11.2 -> v3.11.3 added such NOLINT comments to several ordered_map methods and to
basic_json's swap(), with no other change, and diff_api.py reported all of them as
"changed overloads").
"""
start = cursor.extent.start
end = cursor.extent.end
if not start.file:
return ''
content = _read_file_cached(start.file.name)
if not content:
return ''
raw = content[start.offset:end.offset]
paren_depth = 0
angle_depth = 0
seen_param_list_close = False
out = []
i = 0
n = len(raw)
while i < n:
ch = raw[i]
if ch == '/' and i + 1 < n and raw[i + 1] == '/':
i = raw.find('\n', i)
if i == -1:
break
continue
if ch == '/' and i + 1 < n and raw[i + 1] == '*':
end_comment = raw.find('*/', i + 2)
i = n if end_comment == -1 else end_comment + 2
continue
if ch == '{' and paren_depth == 0 and angle_depth == 0:
break
if ch == ';' and paren_depth == 0 and angle_depth == 0:
out.append(ch)
break
# A bare ':' after the parameter list has closed starts a constructor's
# member-initializer-list ("basic_json(...) : m_data(v) { ... }") -- that's
# implementation (which members get initialized how), not public signature, so stop
# before it. Guarded by seen_param_list_close so this doesn't misfire on a ':' that's
# actually part of a preceding "::" scope-resolution token in the return type/params.
if (ch == ':' and seen_param_list_close and paren_depth == 0 and angle_depth == 0
and (i == 0 or raw[i - 1] != ':') and (i + 1 >= n or raw[i + 1] != ':')):
break
if ch == '(':
paren_depth += 1
elif ch == ')':
paren_depth = max(0, paren_depth - 1)
if paren_depth == 0:
seen_param_list_close = True
elif ch == '<':
angle_depth += 1
elif ch == '>' and angle_depth > 0:
angle_depth -= 1
out.append(ch)
i += 1
sig = re.sub(r'\s+', ' ', ''.join(out)).strip()
return strip_abi_tag(sig)
def get_identity_name(cursor, scope: str) -> str:
"""The name component of a cursor's identity -- usually cursor.spelling, but not for
CONSTRUCTOR/DESTRUCTOR cursors, FUNCTION_TEMPLATE cursors that are themselves templated
constructors (e.g. `template<typename CompatibleType> basic_json(CompatibleType&& val)`,
which libclang represents as FUNCTION_TEMPLATE, not CONSTRUCTOR), or CONVERSION_FUNCTION
cursors.
libclang's cursor.spelling for a constructor-like cursor renders the *enclosing class's full
template argument list* (e.g. "basic_json<ObjectType, ..., CustomBaseClass>"), not just
"basic_json". Using it as-is for identity would reintroduce class-arity sensitivity (see
identity_key()'s docstring for why that's a problem) just for constructors specifically.
Detected by checking whether cursor.spelling equals or starts with "<ClassName><" -- the
class's own bare name, the last segment of scope. A canonical placeholder
("(constructor)"/"(destructor)") is substituted for identity purposes; callers still use
cursor.spelling as-is for human-readable display fields (name/pretty_signature), where the
full templated name is informative rather than noisy.
For CONVERSION_FUNCTION cursors, cursor.spelling renders libclang's internally-resolved
return type rather than what's literally written -- confirmed empirically for
`json_pointer::operator string_t()` (return type declared via `using string_t = typename
string_t_helper<RefStringType>::type`), which spelled as
"operator nlohmann::json_pointer::string_t_helper<type-parameter-0-0>::type" locally but
"operator typename string_t_helper<type-parameter-0-0>::type" in CI, purely a difference in
how each libclang build canonicalizes the same dependent type -- both machines pinned the
identical libclang==18.1.1 wheel, and the discrepancy persisted even under a full C++20
parse with real <ranges> support, ruling out JSON_HAS_RANGES as the cause. Extracted from
raw source text instead (the same "read what's actually written" fix get_signature_text()
already applies to signatures generally), via a regex over the cursor's own source extent:
immune to libclang's dependent-type resolution differences, and incidentally more readable
than "operator type-parameter-1-0" (basic_json's own conversion operator's spelling, which
was already an unrelated but analogous artifact worth avoiding here too).
"""
class_bare_name = scope.rsplit('::', 1)[-1]
is_constructor_like = (
cursor.kind == cindex.CursorKind.CONSTRUCTOR
or (cursor.kind == cindex.CursorKind.FUNCTION_TEMPLATE
and (cursor.spelling == class_bare_name or cursor.spelling.startswith(class_bare_name + '<')))
)
if is_constructor_like:
return '(constructor)'
if cursor.kind == cindex.CursorKind.DESTRUCTOR:
return '(destructor)'
if cursor.kind == cindex.CursorKind.CONVERSION_FUNCTION or cursor.spelling.startswith('operator '):
# The `cursor.spelling.startswith('operator ')` half of this condition also catches
# templated conversion operators, which libclang represents as FUNCTION_TEMPLATE rather
# than CONVERSION_FUNCTION -- e.g. basic_json's `operator ValueType()`, whose
# cursor.spelling is the equally libclang-internal "operator type-parameter-1-0".
match = re.search(r'\boperator\s+(.+?)\s*\(\s*\)', get_signature_text(cursor))
if match:
return f'operator {match.group(1)}'
return cursor.spelling
def identity_key(cursor, scope: str) -> str:
"""A stable, overload-disambiguating key, used internally during extraction to prevent two
distinct entries from silently colliding in the in-memory api_dict. Two prior approaches were
tried and found broken:
1. {scope, name, kind, params} from cursor.get_arguments() alone: silently collided for
overload sets differentiated only by constness, ref-qualifiers, or SFINAE constraints
rather than parameter types -- e.g. basic_json's two zero-argument get() overloads (one
const, one not) both produced params=[] and overwrote each other in the output dict. A
full scan found 59 such silent overwrites across 27 colliding names. Extending this
approach with is_const/is_static/ref-qualifier flags fixed most of these but not all --
adl_serializer::from_json's two overloads and basic_json's two erase(iterator) overloads
still collided, because get_arguments() returns [] for them (see get_signature_text()'s
docstring) and their template-parameter lists happen to read identically too.
2. libclang's USR: correctly disambiguates every overload (it encodes the full mangled
signature) but also encodes the *enclosing class template's own arity* into every
member's USR. Confirmed empirically: basic_json gaining a defaulted CustomBaseClass
template parameter between v3.11.2 and v3.11.3 (a backward-compatible change) changed
literally every basic_json member's USR, which made diff_api.py report ~228/330 entries
as "changed" for a release with zero real breaking changes among them. Reconstructing
"the member's own identity" by string-surgery on USR text was rejected: clang's USR
grammar is undocumented and not a stable contract for this kind of manipulation.
This key avoids both failure modes: scope is passed in (derived from get_qualified_name(),
never from USR), and get_signature_text() reads the declaration as literally written in
source, which never encodes the enclosing class's resolved template arguments and reliably
disambiguates every case found so far (SFINAE-only overloads, overloads get_arguments()
can't see, and simple parameter-type differences alike).
Known limitation: because the signature text includes parameter *names*, not just types, a
pure parameter rename (no type/constraint change) would look like a "changed" entry to
diff_api.py. Accepted as a much smaller and rarer source of noise than either bug above --
see POLICY.md.
NOTE: this opaque joined string is only used as a dict key during extraction. The persisted
--surface-output format (see main()) stores scope/identity_name/kind/signature as separate,
explicit fields instead -- a human or `git diff` should never need to decode this string.
"""
return '\x1f'.join([scope, get_identity_name(cursor, scope), cursor.kind.name, get_signature_text(cursor)])
def get_repo_root():
"""Find the repository root via git, so location paths are deterministic regardless of invoking CWD."""
try:
result = subprocess.run(
['git', 'rev-parse', '--show-toplevel'],
capture_output=True, text=True, check=True, timeout=10
)
return result.stdout.strip()
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
return None
REPO_ROOT = get_repo_root()
def canonicalize_path(file_path: str) -> str:
"""Make a file path deterministic: absolute, then relative to the repo root."""
if not file_path:
return 'unknown'
abs_path = os.path.abspath(file_path)
if REPO_ROOT:
return os.path.relpath(abs_path, REPO_ROOT)
return abs_path
def cursor_location(cursor) -> str:
"""Build a canonical 'path:line' string for a cursor, or 'unknown' if it has no file."""
if cursor.location and cursor.location.file:
return f"{canonicalize_path(cursor.location.file.name)}:{cursor.location.line}"
return "unknown"
def get_system_includes(compiler='clang++'):
"""Discover system include paths by parsing clang++ -E -x c++ -v /dev/null output."""
try:
result = subprocess.run(
[compiler, '-E', '-x', 'c++', '-v', '/dev/null'],
capture_output=True, text=True, check=True, timeout=10
)
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired) as e:
print(f"Warning: Failed to discover system includes via '{compiler}': {e}")
return []
stderr = result.stderr
in_block = False
paths = []
for line in stderr.splitlines():
if line.strip() == '#include <...> search starts here:':
in_block = True
continue
if line.strip() == 'End of search list.':
in_block = False
continue
if in_block:
# Strip trailing annotations like "(framework directory)"
path = line.strip().split()[0] if line.strip() else ''
if path:
paths.append(path)
return [f'-isystem{p}' for p in paths]
def setup_libclang():
"""Locate and set up libclang library."""
possible_paths = [
'/opt/homebrew/opt/llvm/lib/libclang.dylib', # macOS
'/usr/local/opt/llvm/lib/libclang.dylib', # macOS alt
'/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib',
'/usr/lib/libclang.so', # Linux
'/usr/lib/x86_64-linux-gnu/libclang.so', # Linux
]
for path in possible_paths:
if os.path.exists(path):
try:
cindex.conf.set_library_file(path)
return True
except Exception:
continue
try:
filename = cindex.conf.get_filename()
if filename:
cindex.conf.set_library_file(filename)
return True
except Exception:
pass
return False
def extract_sa_url(raw_comment: str) -> str | None:
"""Extract @sa URL from a Doxygen comment."""
if not raw_comment:
return None
match = re.search(r'@sa\s+(https://[^\s]+)', raw_comment)
return match.group(1) if match else None
def get_qualified_name(cursor) -> str:
"""Get fully qualified name by walking semantic_parent chain."""
parts = []
c = cursor
while c and c.kind != cindex.CursorKind.TRANSLATION_UNIT:
if c.kind == cindex.CursorKind.NAMESPACE:
# Skip ABI inline-namespace cursors
if not ABI_TAG_PATTERN.search(f'::{c.spelling}'):
parts.append(c.spelling)
elif c.spelling:
parts.append(c.spelling)
c = c.semantic_parent
return '::'.join(reversed(parts)) if parts else ''
def walk_class_template(cursor, public_classes: set, api_dict: dict, documented_non_public: list):
"""Walk a class template's members: extract public callable/type-tier entities, and flag
any non-public member that surprisingly carries a real @sa URL (a genuine documentation leak)."""
if cursor.kind not in (cindex.CursorKind.CLASS_TEMPLATE, cindex.CursorKind.CLASS_DECL, cindex.CursorKind.STRUCT_DECL):
return
if cursor.spelling not in public_classes or not cursor.is_definition():
return
scope = strip_abi_tag(get_qualified_name(cursor))
location = cursor_location(cursor)
# Exemption list: STL-container-named-requirement aliases that don't require @sa
stl_exempt = {'value_type', 'reference', 'const_reference', 'pointer', 'const_pointer',
'iterator', 'const_iterator', 'reverse_iterator', 'const_reverse_iterator',
'difference_type', 'size_type', 'allocator_type', 'key_type', 'mapped_type'}
for child in cursor.get_children():
is_public = not hasattr(child, 'access_specifier') or str(child.access_specifier) == 'AccessSpecifier.PUBLIC'
if not is_public:
# documented_non_public tracks entities NOT part of the public surface that
# nonetheless carry a real @sa URL -- a genuine documentation leak, not "any
# public member missing @sa" (that's what check_docs.py's missing-@sa check is for).
leaked_url = extract_sa_url(child.raw_comment)
if leaked_url:
documented_non_public.append({
'location': cursor_location(child),
'raw_comment_excerpt': (child.raw_comment or '')[:100],
'reason': f'{child.kind.name} "{child.spelling}" is non-public but has @sa: {leaked_url}'
})
continue
# Callable tier: methods, constructors, destructors, conversion ops, function templates
if child.kind in (cindex.CursorKind.CXX_METHOD, cindex.CursorKind.CONSTRUCTOR,
cindex.CursorKind.DESTRUCTOR, cindex.CursorKind.CONVERSION_FUNCTION,
cindex.CursorKind.FUNCTION_TEMPLATE):
key = identity_key(child, scope)
doc_url = extract_sa_url(child.raw_comment)
api_dict[key] = {
'scope': scope,
'name': child.spelling,
'identity_name': get_identity_name(child, scope),
'kind': child.kind.name,
'tier': 'callable',
'signature': get_signature_text(child),
'location': cursor_location(child),
'doc_url': doc_url,
'has_sa': doc_url is not None,
'pretty_signature': f"{scope}::{child.spelling}",
}
# Type tier: TYPE_ALIAS_DECL
elif child.kind == cindex.CursorKind.TYPE_ALIAS_DECL:
name = child.spelling
tier = 'type_exempt' if name in stl_exempt else 'type'
# Try to follow the alias to find @sa on the underlying declaration
doc_url = extract_sa_url(child.raw_comment)
underlying_location = None
if not doc_url and hasattr(child, 'underlying_typedef_type'):
try:
underlying_decl = child.underlying_typedef_type.get_declaration()
if underlying_decl and underlying_decl.raw_comment:
doc_url = extract_sa_url(underlying_decl.raw_comment)
if underlying_decl.location.file:
underlying_location = cursor_location(underlying_decl)
except Exception:
pass
key = identity_key(child, scope)
api_dict[key] = {
'scope': scope,
'name': name,
'identity_name': get_identity_name(child, scope),
'kind': 'TYPE_ALIAS_DECL',
'tier': tier,
'signature': get_signature_text(child),
'location': location,
'doc_url': doc_url,
'has_sa': doc_url is not None,
'pretty_signature': f"{scope}::{name}",
}
if underlying_location:
api_dict[key]['resolved_via_alias_to'] = underlying_location
def walk_ast(cursor, public_classes: set, api_dict: dict, documented_non_public: list):
"""Recursively walk AST."""
# Check if this is one of the six public class templates
if cursor.kind in (cindex.CursorKind.CLASS_TEMPLATE, cindex.CursorKind.CLASS_DECL, cindex.CursorKind.STRUCT_DECL):
if cursor.spelling in public_classes:
walk_class_template(cursor, public_classes, api_dict, documented_non_public)
# Extract free functions (not nested in a class)
elif cursor.kind == cindex.CursorKind.FUNCTION_DECL:
# Check it's in nlohmann namespace, not detail/std
parent = cursor.semantic_parent
if parent and parent.kind == cindex.CursorKind.NAMESPACE:
ns_name = parent.spelling
if ns_name == 'nlohmann' or (parent.semantic_parent and parent.semantic_parent.kind == cindex.CursorKind.NAMESPACE and
parent.semantic_parent.spelling == 'nlohmann'):
# This is a free function in nlohmann (or a subnamespace like json_literals)
if 'detail' not in ns_name and 'std' not in ns_name:
# get_qualified_name(parent), not get_qualified_name(cursor) -- the latter
# would include the function's own name as the last segment (cursor is the
# function itself), producing a bogus scope like "nlohmann::operator==" and
# a doubled pretty_signature "nlohmann::operator==::operator==".
scope = strip_abi_tag(get_qualified_name(parent))
key = identity_key(cursor, scope)
doc_url = extract_sa_url(cursor.raw_comment)
api_dict[key] = {
'scope': scope,
'name': cursor.spelling,
'identity_name': get_identity_name(cursor, scope),
'kind': 'FUNCTION_DECL',
'tier': 'callable',
'signature': get_signature_text(cursor),
'location': cursor_location(cursor),
'doc_url': doc_url,
'has_sa': doc_url is not None,
'pretty_signature': f"{scope}::{cursor.spelling}",
}
# Recurse into container types
for child in cursor.get_children():
walk_ast(child, public_classes, api_dict, documented_non_public)
def write_surface(api_dict: dict, output_path: str, extracted_from: str, extra_meta: dict | None = None):
"""Write the minimal, location/doc-independent API surface: a format_version-tagged, sorted
list of self-describing records (scope, kind, name, identity_name, tier, signature,
pretty_signature) -- no opaque joined key, no location, no doc_url.
This is the file meant to be committed and diffed release-to-release (see
tools/api_checker/README.md and POLICY.md): 'signature'/'identity_name' are the same values
identity_key() joins into one opaque string for internal use during extraction, but exposed
here as separate fields so the file is self-describing -- a human or `git diff` can see
exactly what changed without decoding anything (see identity_key()'s docstring).
extra_meta lets callers (e.g. snapshot_release.py, writing a per-release historical record)
add richer, immutable provenance (ref/commit/generated_at) beyond what a live, repeatedly-
regenerated snapshot should carry -- see SURFACE_FORMAT_VERSION's docstring for why the live
file deliberately omits a timestamp.
"""
records = [
{
'scope': entry['scope'],
'kind': entry['kind'],
'name': entry['name'],
'identity_name': entry['identity_name'],
'tier': entry['tier'],
'signature': entry['signature'],
'pretty_signature': entry['pretty_signature'],
}
for entry in api_dict.values()
]
records.sort(key=lambda r: (r['scope'], r['name'], r['kind'], r['signature']))
meta = {'extracted_from': extracted_from}
if extra_meta:
meta.update(extra_meta)
output = {
'format_version': SURFACE_FORMAT_VERSION,
'meta': meta,
'public_api': records,
}
with open(output_path, 'w') as f:
json.dump(output, f, indent=2, sort_keys=True)
f.write('\n')
def run_self_test():
"""Test ABI-tag stripping."""
tests = [
('nlohmann::json_abi_v3_12_0::basic_json::parse', 'nlohmann::basic_json::parse'),
('nlohmann::json_abi_diag_v3_12_0::basic_json::dump', 'nlohmann::basic_json::dump'),
('nlohmann::json_abi_diag_ldvcmp_v3_11_2::basic_json::get', 'nlohmann::basic_json::get'),
# The ABI tag as the *last* segment (no following '::') -- the scope of a free function
# whose direct parent is the ABI-tagged namespace itself, e.g. nlohmann::operator==.
# A regression: an earlier version of this pattern required a trailing '::' via a
# lookahead, so it silently failed to strip exactly this case, and diff_api.py reported
# every free comparison operator as removed-and-readded on every ABI-tag version bump.
('nlohmann::json_abi_v3_11_3', 'nlohmann'),
# v3.11.0/v3.11.1 used "json_vMAJOR_MINOR_PATCH" (no "_abi" segment) before the tag was
# renamed to "json_abi_v..." in v3.11.2. A regression: an earlier version of this pattern
# hard-required the literal "_abi" segment, so it silently failed to strip this older
# form, making diff_api.py report ~100% of the API as removed-and-readded across
# v3.10.5->v3.11.0->v3.11.1->v3.11.2 (confirmed against the real backfilled history).
('nlohmann::json_v3_11_0::basic_json::parse', 'nlohmann::basic_json::parse'),
]
for input_str, expected in tests:
result = strip_abi_tag(input_str)
if result != expected:
print(f"FAIL: strip_abi_tag('{input_str}') = '{result}', expected '{expected}'")
return False
print("Self-test passed: ABI-tag stripping works correctly")
return True
def main():
parser = argparse.ArgumentParser(description='Extract public API surface from nlohmann/json')
parser.add_argument('--header', default='include/nlohmann/json.hpp',
help='Path to header file to analyze')
parser.add_argument('--include', default='include',
help='Include path for parsing')
parser.add_argument('--output', default='api_snapshot.json',
help='Output file for the full API snapshot (includes source location and '
'documentation status -- for check_docs.py; not meant to be committed, '
'since location/doc-link churn would make every regeneration look like '
'an API change)')
parser.add_argument('--surface-output', default=None,
help='Output file for the minimal API surface (identity only: scope, name, '
'kind, tier, pretty_signature -- no location or doc_url). This is the '
'file meant to be committed and diffed release-to-release, since it is '
'unaffected by unrelated code motion or documentation-site restructuring.')
parser.add_argument('--self-test', action='store_true',
help='Run self-tests and exit')
parser.add_argument('--extra-isystem', action='append', default=[],
help='Extra -isystem include path (repeatable)')
args = parser.parse_args()
if args.self_test:
sys.exit(0 if run_self_test() else 1)
if not setup_libclang():
print("Error: Could not locate libclang library")
sys.exit(1)
if not os.path.exists(args.header):
print(f"Error: Header file not found: {args.header}")
sys.exit(1)
print(f"Extracting API from {args.header}...")
# Discover system includes
sys_includes = get_system_includes()
if not sys_includes:
print("Warning: Could not discover system includes via clang++")
# Build parse arguments
parse_args = [
f'-I{args.include}',
'-std=c++17',
'-x', 'c++',
'-fparse-all-comments',
# JSON_HAS_RANGES auto-detects via the standard library's __cpp_lib_ranges feature-test
# macro, which -- unlike compiler-level feature macros such as
# __cpp_impl_three_way_comparison -- isn't reliably gated to C++20 mode by every stdlib:
# confirmed empirically that it's undefined under -std=c++17 with macOS's libc++, but
# defined under the same flag with the Ubuntu stdlib used in CI, producing a different
# extracted signature for parse()/accept()/from_*() depending only on which machine ran
# the extraction. Pinning to 1 was tried first and rejected: under a real -std=c++17
# build, <ranges>'s std::ranges namespace isn't necessarily populated even when the
# feature-test macro leaks through, and JSON_HAS_RANGES=1 code paths reference
# std::ranges directly, which fails to parse ("no member named 'ranges' in namespace
# 'std'") on macOS's libc++ despite the successful C++20 compile above. 0 is the value
# that's guaranteed to parse on every stdlib under -std=c++17, so it's the deterministic,
# safe choice, even though it means the SentinelType-taking overloads aren't captured.
'-DJSON_HAS_RANGES=0',
] + sys_includes + [f'-isystem{path}' for path in args.extra_isystem]
# Parse
index = cindex.Index.create()
tu = index.parse(args.header, parse_args,
options=cindex.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD)
# Check for errors
errors = [d for d in tu.diagnostics if d.severity >= cindex.Diagnostic.Error]
if errors:
print("Parse errors encountered:")
for diag in errors[:10]:
print(f" {diag}")
if len(errors) > 10:
print(f" ... and {len(errors) - 10} more")
sys.exit(1)
# Extract API
public_classes = {'basic_json', 'adl_serializer', 'byte_container_with_subtype',
'json_pointer', 'json_sax', 'ordered_map'}
api_dict = {}
documented_non_public = []
walk_ast(tu.cursor, public_classes, api_dict, documented_non_public)
# Output. Deliberately no timestamp here: this file is meant to be committed and diffed
# (see tools/api_checker/README.md), so its content must be a pure function of the source
# tree -- a generated-at timestamp would make every regeneration look like a diff.
output = {
'meta': {
'extracted_from': canonicalize_path(os.path.abspath(args.header)),
},
'public_api': api_dict,
'documented_non_public': documented_non_public,
}
with open(args.output, 'w') as f:
json.dump(output, f, indent=2, sort_keys=True)
f.write('\n')
print(f"Found {len(api_dict)} public API entries")
if documented_non_public:
print(f"Found {len(documented_non_public)} entities with @sa outside the public surface")
print(f"Wrote API snapshot to {args.output}")
if args.surface_output:
write_surface(api_dict, args.surface_output, canonicalize_path(os.path.abspath(args.header)))
print(f"Wrote API surface (location/doc-independent) to {args.surface_output}")
if __name__ == '__main__':
main()

Some files were not shown because too many files have changed in this diff Show More