mirror of
https://github.com/nlohmann/json.git
synced 2026-08-28 20:07:32 +00:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a27065bd12 | ||
|
|
3940f4b730 | ||
|
|
0663907b68 | ||
|
|
f23b3c63a2 |
@@ -0,0 +1,81 @@
|
||||
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
|
||||
@@ -3,6 +3,7 @@
|
||||
*.gcno
|
||||
*.gcda
|
||||
.DS_Store
|
||||
__pycache__/
|
||||
|
||||
/.idea
|
||||
/cmake-build-*
|
||||
@@ -43,5 +44,9 @@ 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
|
||||
|
||||
@@ -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.
|
||||
: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.
|
||||
|
||||
: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,7 +420,9 @@ 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.
|
||||
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.
|
||||
5. Since version 1.0.0.
|
||||
6. Since version 1.0.0.
|
||||
7. Since version 1.0.0.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,32 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,31 @@
|
||||
# <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,3 +51,5 @@ 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,3 +85,8 @@ 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)).
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,45 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,45 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,28 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,30 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,33 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,30 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,30 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,30 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,29 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,30 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,22 @@
|
||||
# <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,8 +57,7 @@ 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, which may also be empty
|
||||
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
|
||||
|
||||
## Default definition
|
||||
|
||||
@@ -128,20 +127,6 @@ 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,8 +33,7 @@ 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, which may also be empty
|
||||
: name of the member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
|
||||
|
||||
## Default definition
|
||||
|
||||
@@ -59,20 +58,6 @@ 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,8 +33,7 @@ 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, which may also be empty
|
||||
: name of the (public) member variable to serialize/deserialize; up to 63 members can be given as a comma-separated list
|
||||
|
||||
## Default definition
|
||||
|
||||
@@ -60,18 +59,6 @@ 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
|
||||
|
||||
@@ -8,7 +8,7 @@ This type preserves the insertion order of object keys.
|
||||
|
||||
## Iterator invalidation
|
||||
|
||||
The type is based on [`ordered_map`](ordered_map.md) which in turn uses a `std::vector` to store object elements.
|
||||
The type is based on [`ordered_map`](ordered_map/index.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.md)
|
||||
- [ordered_map](ordered_map/index.md)
|
||||
- [Object Order](../features/object_order.md)
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# <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).
|
||||
@@ -0,0 +1,61 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,53 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,58 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,75 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,56 @@
|
||||
# <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.
|
||||
@@ -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** - base container type (`#!cpp std::vector<std::pair<const Key, T>, Allocator>`)
|
||||
- [**Container**](Container.md) - base container type (`#!cpp std::vector<std::pair<const Key, T>, Allocator>`)
|
||||
- **iterator**
|
||||
- **const_iterator**
|
||||
- **size_type**
|
||||
- **value_type**
|
||||
- **key_compare** - key comparison function
|
||||
- [**key_compare**](key_compare.md) - key comparison function
|
||||
```cpp
|
||||
std::equal_to<Key> // until C++14
|
||||
|
||||
@@ -46,15 +46,16 @@ std::equal_to<> // since C++14
|
||||
|
||||
## Member functions
|
||||
|
||||
- (constructor)
|
||||
- (destructor)
|
||||
- **emplace**
|
||||
- **operator\[\]**
|
||||
- **at**
|
||||
- **erase**
|
||||
- **count**
|
||||
- **find**
|
||||
- **insert**
|
||||
- [(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)
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -74,9 +75,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.
|
||||
@@ -0,0 +1,63 @@
|
||||
# <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).
|
||||
@@ -0,0 +1,34 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,32 @@
|
||||
# <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).
|
||||
@@ -0,0 +1,62 @@
|
||||
# <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.
|
||||
@@ -0,0 +1,66 @@
|
||||
# <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).
|
||||
@@ -0,0 +1,17 @@
|
||||
# <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).
|
||||
@@ -0,0 +1,21 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
draft2 size: 4
|
||||
draft3 size: 6
|
||||
@@ -0,0 +1,11 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,15 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
c1 == c2: true
|
||||
c1 == c3: false
|
||||
@@ -0,0 +1,15 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
c1 != c2: false
|
||||
c1 != c3: true
|
||||
@@ -0,0 +1,10 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,13 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
["a",1,2.0,false]
|
||||
@@ -0,0 +1,10 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,10 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,10 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,10 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,10 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,10 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
true
|
||||
@@ -0,0 +1,12 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Container is std::vector<std::pair<const Key, T>>: true
|
||||
@@ -0,0 +1,26 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
m.at("one") = 1
|
||||
m.at("two") = 22
|
||||
exception: key not found
|
||||
@@ -0,0 +1,12 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
m.count("one") = 1
|
||||
m.count("two") = 0
|
||||
@@ -0,0 +1,15 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
inserted: true, value: eins
|
||||
inserted: false, value: eins
|
||||
@@ -0,0 +1,24 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
removed by key: 1
|
||||
remaining: three
|
||||
@@ -0,0 +1,19 @@
|
||||
#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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
found: one = 1
|
||||
"two" not found
|
||||
@@ -0,0 +1,21 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
inserted: true
|
||||
one:1 two:2 three:3
|
||||
@@ -0,0 +1,12 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
compare("a", "a") = true
|
||||
compare("a", "b") = false
|
||||
@@ -0,0 +1,14 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
m["one"] = 1
|
||||
m["one"] = 1
|
||||
@@ -215,24 +215,6 @@ 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
@@ -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.md)
|
||||
- [`ordered_json`](../api/ordered_json.md) via [`ordered_map`](../api/ordered_map/index.md)
|
||||
|
||||
## Value storage
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
## v3.12.0 (2025-04-11)
|
||||
|
||||
|
||||
+30
-1
@@ -52,6 +52,7 @@ 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
|
||||
@@ -117,6 +118,7 @@ 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
|
||||
@@ -154,6 +156,7 @@ nav:
|
||||
- 'get_to': api/basic_json/get_to.md
|
||||
- 'std::formatter<basic_json>': api/basic_json/std_formatter.md
|
||||
- 'std::hash<basic_json>': 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
|
||||
@@ -172,6 +175,7 @@ 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
|
||||
@@ -228,9 +232,13 @@ 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
|
||||
@@ -256,25 +264,46 @@ 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': api/ordered_map.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
|
||||
- macros:
|
||||
- 'Overview': api/macros/index.md
|
||||
- 'JSON_ASSERT': api/macros/json_assert.md
|
||||
|
||||
@@ -22,7 +22,9 @@ 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/
|
||||
@@ -54,12 +56,14 @@ 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,15 +31,21 @@ 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;
|
||||
|
||||
@@ -47,6 +53,7 @@ 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;
|
||||
|
||||
@@ -54,6 +61,7 @@ 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;
|
||||
|
||||
@@ -61,6 +69,7 @@ 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;
|
||||
|
||||
@@ -69,6 +78,7 @@ 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;
|
||||
|
||||
@@ -77,6 +87,7 @@ 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;
|
||||
|
||||
@@ -85,6 +96,7 @@ 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;
|
||||
|
||||
@@ -93,6 +105,7 @@ 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;
|
||||
|
||||
@@ -101,12 +114,14 @@ 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;
|
||||
|
||||
@@ -115,12 +130,14 @@ 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;
|
||||
|
||||
@@ -130,16 +147,23 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ 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
|
||||
|
||||
@@ -587,59 +587,16 @@ 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_JSON_DEFINE_TYPE_INTRUSIVE_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(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__)) })
|
||||
@@ -650,15 +607,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.11.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(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__)) })
|
||||
@@ -669,14 +621,9 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.11.3
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(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__)) })
|
||||
|
||||
@@ -686,17 +633,10 @@ 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_JSON_DEFINE_TYPE_NON_INTRUSIVE_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(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__)) })
|
||||
@@ -707,15 +647,10 @@ 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_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(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__)) })
|
||||
@@ -726,14 +661,9 @@ 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_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(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__)) })
|
||||
|
||||
@@ -743,17 +673,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(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__)) })
|
||||
@@ -764,15 +687,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(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__)) })
|
||||
@@ -783,14 +701,9 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(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__)) })
|
||||
|
||||
@@ -801,17 +714,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(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__)) })
|
||||
@@ -822,15 +728,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(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__)) })
|
||||
@@ -841,14 +742,9 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(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__)) })
|
||||
|
||||
|
||||
@@ -192,22 +192,31 @@ 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>;
|
||||
|
||||
////////////////
|
||||
@@ -349,15 +358,19 @@ 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
|
||||
|
||||
@@ -1182,6 +1195,7 @@ 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 >
|
||||
@@ -1802,6 +1816,8 @@ 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)
|
||||
@@ -1845,6 +1861,8 @@ 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>
|
||||
@@ -1870,6 +1888,7 @@ 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,
|
||||
@@ -1880,6 +1899,7 @@ 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)
|
||||
@@ -1943,6 +1963,7 @@ 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>>,
|
||||
@@ -2224,12 +2245,14 @@ 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
|
||||
{
|
||||
@@ -2439,6 +2462,7 @@ 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
|
||||
@@ -2449,6 +2473,7 @@ 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
|
||||
@@ -2818,6 +2843,7 @@ 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
|
||||
@@ -4100,6 +4126,7 @@ 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,
|
||||
@@ -4134,6 +4161,7 @@ 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,
|
||||
@@ -4481,6 +4509,7 @@ 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))
|
||||
@@ -4492,6 +4521,7 @@ 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,
|
||||
@@ -4538,6 +4568,7 @@ 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))
|
||||
@@ -4548,6 +4579,7 @@ 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,
|
||||
@@ -4593,6 +4625,7 @@ 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))
|
||||
@@ -4603,6 +4636,7 @@ 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,
|
||||
@@ -4679,6 +4713,7 @@ 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))
|
||||
@@ -4689,6 +4724,7 @@ 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,
|
||||
@@ -4718,6 +4754,7 @@ 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)
|
||||
@@ -4732,6 +4769,7 @@ 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
|
||||
@@ -4746,6 +4784,7 @@ 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)
|
||||
@@ -4760,6 +4799,7 @@ 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
|
||||
|
||||
@@ -30,30 +30,41 @@ 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)
|
||||
@@ -64,12 +75,14 @@ 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)
|
||||
@@ -83,6 +96,7 @@ 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)
|
||||
@@ -98,11 +112,13 @@ 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)
|
||||
@@ -110,11 +126,13 @@ 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
|
||||
@@ -122,6 +140,7 @@ 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)
|
||||
@@ -135,6 +154,7 @@ 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)
|
||||
@@ -150,6 +170,7 @@ 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)
|
||||
@@ -163,6 +184,7 @@ 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)
|
||||
@@ -178,6 +200,7 @@ 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)
|
||||
@@ -197,6 +220,7 @@ 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)
|
||||
@@ -218,11 +242,13 @@ 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)
|
||||
@@ -276,6 +302,7 @@ 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)
|
||||
@@ -288,6 +315,7 @@ 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)
|
||||
@@ -302,6 +330,7 @@ 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)
|
||||
@@ -314,6 +343,7 @@ 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)
|
||||
@@ -328,6 +358,7 @@ 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)
|
||||
@@ -340,6 +371,7 @@ 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)
|
||||
@@ -354,11 +386,13 @@ 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)
|
||||
@@ -376,6 +410,7 @@ 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
@@ -2962,59 +2962,16 @@ 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_JSON_DEFINE_TYPE_INTRUSIVE_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(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__)) })
|
||||
@@ -3025,15 +2982,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.11.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(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__)) })
|
||||
@@ -3044,14 +2996,9 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.11.3
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(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__)) })
|
||||
|
||||
@@ -3061,17 +3008,10 @@ 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_JSON_DEFINE_TYPE_NON_INTRUSIVE_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(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__)) })
|
||||
@@ -3082,15 +3022,10 @@ 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_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(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__)) })
|
||||
@@ -3101,14 +3036,9 @@ 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_JSON_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, ...) \
|
||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(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__)) })
|
||||
|
||||
@@ -3118,17 +3048,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(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__)) })
|
||||
@@ -3139,15 +3062,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(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__)) })
|
||||
@@ -3158,14 +3076,9 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(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__)) })
|
||||
|
||||
@@ -3176,17 +3089,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(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__)) })
|
||||
@@ -3197,15 +3103,10 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(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__)) })
|
||||
@@ -3216,14 +3117,9 @@ void templated_json_throw(ExceptionType exception)
|
||||
@since version 3.12.0
|
||||
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
|
||||
*/
|
||||
#define NLOHMANN_JSON_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_MEMBERS(Type, BaseType, ...) \
|
||||
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(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__)) })
|
||||
|
||||
@@ -6836,7 +6732,9 @@ 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/
|
||||
@@ -6868,12 +6766,14 @@ 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);
|
||||
@@ -9536,15 +9436,21 @@ 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;
|
||||
|
||||
@@ -9552,6 +9458,7 @@ 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;
|
||||
|
||||
@@ -9559,6 +9466,7 @@ 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;
|
||||
|
||||
@@ -9566,6 +9474,7 @@ 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;
|
||||
|
||||
@@ -9574,6 +9483,7 @@ 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;
|
||||
|
||||
@@ -9582,6 +9492,7 @@ 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;
|
||||
|
||||
@@ -9590,6 +9501,7 @@ 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;
|
||||
|
||||
@@ -9598,6 +9510,7 @@ 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;
|
||||
|
||||
@@ -9606,12 +9519,14 @@ 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;
|
||||
|
||||
@@ -9620,12 +9535,14 @@ 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;
|
||||
|
||||
@@ -9635,16 +9552,23 @@ 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;
|
||||
};
|
||||
|
||||
@@ -15499,6 +15423,7 @@ 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
|
||||
@@ -20861,30 +20786,41 @@ 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)
|
||||
@@ -20895,12 +20831,14 @@ 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)
|
||||
@@ -20914,6 +20852,7 @@ 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)
|
||||
@@ -20929,11 +20868,13 @@ 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)
|
||||
@@ -20941,11 +20882,13 @@ 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
|
||||
@@ -20953,6 +20896,7 @@ 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)
|
||||
@@ -20966,6 +20910,7 @@ 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)
|
||||
@@ -20981,6 +20926,7 @@ 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)
|
||||
@@ -20994,6 +20940,7 @@ 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)
|
||||
@@ -21009,6 +20956,7 @@ 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)
|
||||
@@ -21028,6 +20976,7 @@ 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)
|
||||
@@ -21049,11 +20998,13 @@ 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)
|
||||
@@ -21107,6 +21058,7 @@ 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)
|
||||
@@ -21119,6 +21071,7 @@ 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)
|
||||
@@ -21133,6 +21086,7 @@ 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)
|
||||
@@ -21145,6 +21099,7 @@ 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)
|
||||
@@ -21159,6 +21114,7 @@ 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)
|
||||
@@ -21171,6 +21127,7 @@ 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)
|
||||
@@ -21185,11 +21142,13 @@ 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)
|
||||
@@ -21207,6 +21166,7 @@ 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)
|
||||
{
|
||||
@@ -21343,22 +21303,31 @@ 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>;
|
||||
|
||||
////////////////
|
||||
@@ -21500,15 +21469,19 @@ 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
|
||||
|
||||
@@ -22333,6 +22306,7 @@ 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 >
|
||||
@@ -22953,6 +22927,8 @@ 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)
|
||||
@@ -22996,6 +22972,8 @@ 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>
|
||||
@@ -23021,6 +22999,7 @@ 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,
|
||||
@@ -23031,6 +23010,7 @@ 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)
|
||||
@@ -23094,6 +23074,7 @@ 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>>,
|
||||
@@ -23375,12 +23356,14 @@ 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
|
||||
{
|
||||
@@ -23590,6 +23573,7 @@ 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
|
||||
@@ -23600,6 +23584,7 @@ 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
|
||||
@@ -23969,6 +23954,7 @@ 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
|
||||
@@ -25251,6 +25237,7 @@ 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,
|
||||
@@ -25285,6 +25272,7 @@ 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,
|
||||
@@ -25632,6 +25620,7 @@ 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))
|
||||
@@ -25643,6 +25632,7 @@ 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,
|
||||
@@ -25689,6 +25679,7 @@ 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))
|
||||
@@ -25699,6 +25690,7 @@ 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,
|
||||
@@ -25744,6 +25736,7 @@ 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))
|
||||
@@ -25754,6 +25747,7 @@ 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,
|
||||
@@ -25830,6 +25824,7 @@ 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))
|
||||
@@ -25840,6 +25835,7 @@ 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,
|
||||
@@ -25869,6 +25865,7 @@ 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)
|
||||
@@ -25883,6 +25880,7 @@ 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
|
||||
@@ -25897,6 +25895,7 @@ 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)
|
||||
@@ -25911,6 +25910,7 @@ 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
|
||||
|
||||
@@ -778,154 +778,6 @@ 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)
|
||||
@@ -1339,141 +1191,3 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,392 @@
|
||||
# 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
Executable
+182
@@ -0,0 +1,182 @@
|
||||
#!/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()
|
||||
Executable
+131
@@ -0,0 +1,131 @@
|
||||
#!/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()
|
||||
Executable
+295
@@ -0,0 +1,295 @@
|
||||
#!/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()
|
||||
Executable
+668
@@ -0,0 +1,668 @@
|
||||
#!/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
Reference in New Issue
Block a user