mirror of
https://github.com/nlohmann/json.git
synced 2026-10-04 21:50:33 +00:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe4e78e867 | ||
|
|
9196e92520 | ||
|
|
7d22d865dd | ||
|
|
4c73319d3b | ||
|
|
da5097abcd | ||
|
|
2319f6e6f9 | ||
|
|
9a0d1c0c47 | ||
|
|
1bbb5d400a | ||
|
|
08d18d5a82 | ||
|
|
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
|
*.gcno
|
||||||
*.gcda
|
*.gcda
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
/.idea
|
/.idea
|
||||||
/cmake-build-*
|
/cmake-build-*
|
||||||
@@ -45,5 +46,9 @@ venv
|
|||||||
|
|
||||||
nlohmann_json.spdx
|
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
|
# Bazel-related
|
||||||
MODULE.bazel.lock
|
MODULE.bazel.lock
|
||||||
|
|||||||
@@ -104,7 +104,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).
|
: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.
|
: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.
|
||||||
|
|
||||||
@@ -1393,7 +1393,7 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
|
|||||||
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
||||||
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||||
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
||||||
- The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
- The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||||
|
|
||||||
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::to_json', 'Fu
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype', 'Class', 'api/byte_container_with_subtype/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype', 'Class', 'api/byte_container_with_subtype/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::byte_container_with_subtype', 'Constructor', 'api/byte_container_with_subtype/byte_container_with_subtype/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::byte_container_with_subtype', 'Constructor', 'api/byte_container_with_subtype/byte_container_with_subtype/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::clear_subtype', 'Method', 'api/byte_container_with_subtype/clear_subtype/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::clear_subtype', 'Method', 'api/byte_container_with_subtype/clear_subtype/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::container_type', 'Type', 'api/byte_container_with_subtype/container_type/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::has_subtype', 'Method', 'api/byte_container_with_subtype/has_subtype/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::has_subtype', 'Method', 'api/byte_container_with_subtype/has_subtype/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator!=', 'Operator', 'api/byte_container_with_subtype/operator_ne/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator!=', 'Operator', 'api/byte_container_with_subtype/operator_ne/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator==', 'Operator', 'api/byte_container_with_subtype/operator_eq/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator==', 'Operator', 'api/byte_container_with_subtype/operator_eq/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::set_subtype', 'Method', 'api/byte_container_with_subtype/set_subtype/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::set_subtype', 'Method', 'api/byte_container_with_subtype/set_subtype/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::subtype', 'Method', 'api/byte_container_with_subtype/subtype/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::subtype', 'Method', 'api/byte_container_with_subtype/subtype/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::subtype_type', 'Type', 'api/byte_container_with_subtype/subtype_type/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json', 'Class', 'api/basic_json/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json', 'Class', 'api/basic_json/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('format_as', 'Function', 'api/basic_json/format_as/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('format_as', 'Function', 'api/basic_json/format_as/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
|
||||||
@@ -26,6 +28,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Con
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::begin', 'Method', 'api/basic_json/begin/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::begin', 'Method', 'api/basic_json/begin/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary', 'Function', 'api/basic_json/binary/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary', 'Function', 'api/basic_json/binary/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary_t', 'Type', 'api/basic_json/binary_t/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary_t', 'Type', 'api/basic_json/binary_t/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::bjdata_version_t', 'Enum', 'api/basic_json/bjdata_version_t/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::boolean_t', 'Type', 'api/basic_json/boolean_t/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::boolean_t', 'Type', 'api/basic_json/boolean_t/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbegin', 'Method', 'api/basic_json/cbegin/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbegin', 'Method', 'api/basic_json/cbegin/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbor_tag_handler_t', 'Enum', 'api/basic_json/cbor_tag_handler_t/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbor_tag_handler_t', 'Enum', 'api/basic_json/cbor_tag_handler_t/index.html');
|
||||||
@@ -61,6 +64,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_binary', 'Met
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ptr', 'Method', 'api/basic_json/get_ptr/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ptr', 'Method', 'api/basic_json/get_ptr/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ref', 'Method', 'api/basic_json/get_ref/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ref', 'Method', 'api/basic_json/get_ref/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_to', 'Method', 'api/basic_json/get_to/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_to', 'Method', 'api/basic_json/get_to/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::initializer_list_t', 'Type', 'api/basic_json/initializer_list_t/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::input_format_t', 'Enum', 'api/basic_json/input_format_t/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::input_format_t', 'Enum', 'api/basic_json/input_format_t/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::insert', 'Method', 'api/basic_json/insert/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::insert', 'Method', 'api/basic_json/insert/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::invalid_iterator', 'Class', 'api/basic_json/invalid_iterator/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::invalid_iterator', 'Class', 'api/basic_json/invalid_iterator/index.html');
|
||||||
@@ -79,6 +83,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_string', 'Meth
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_structured', 'Method', 'api/basic_json/is_structured/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_structured', 'Method', 'api/basic_json/is_structured/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::items', 'Method', 'api/basic_json/items/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::items', 'Method', 'api/basic_json/items/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_base_class_t', 'Type', 'api/basic_json/json_base_class_t/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_base_class_t', 'Type', 'api/basic_json/json_base_class_t/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_sax_t', 'Type', 'api/basic_json/json_sax_t/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_serializer', 'Class', 'api/basic_json/json_serializer/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_serializer', 'Class', 'api/basic_json/json_serializer/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::max_size', 'Method', 'api/basic_json/max_size/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::max_size', 'Method', 'api/basic_json/max_size/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::merge_patch', 'Method', 'api/basic_json/merge_patch/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::merge_patch', 'Method', 'api/basic_json/merge_patch/index.html');
|
||||||
@@ -153,24 +158,44 @@ INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::string_t', 'Typ
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::to_string', 'Method', 'api/json_pointer/to_string/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::to_string', 'Method', 'api/json_pointer/to_string/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax', 'Class', 'api/json_sax/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax', 'Class', 'api/json_sax/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::binary', 'Method', 'api/json_sax/binary/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::binary', 'Method', 'api/json_sax/binary/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::binary_t', 'Type', 'api/json_sax/binary_t/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::boolean', 'Method', 'api/json_sax/boolean/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::boolean', 'Method', 'api/json_sax/boolean/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_array', 'Method', 'api/json_sax/end_array/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_array', 'Method', 'api/json_sax/end_array/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_object', 'Method', 'api/json_sax/end_object/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_object', 'Method', 'api/json_sax/end_object/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::json_sax', 'Constructor', 'api/json_sax/json_sax/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::key', 'Method', 'api/json_sax/key/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::key', 'Method', 'api/json_sax/key/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::null', 'Method', 'api/json_sax/null/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::null', 'Method', 'api/json_sax/null/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_float', 'Method', 'api/json_sax/number_float/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_float', 'Method', 'api/json_sax/number_float/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_float_t', 'Type', 'api/json_sax/number_float_t/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_integer', 'Method', 'api/json_sax/number_integer/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_integer', 'Method', 'api/json_sax/number_integer/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_integer_t', 'Type', 'api/json_sax/number_integer_t/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_unsigned', 'Method', 'api/json_sax/number_unsigned/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_unsigned', 'Method', 'api/json_sax/number_unsigned/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_unsigned_t', 'Type', 'api/json_sax/number_unsigned_t/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::operator=', 'Operator', 'api/json_sax/operator=/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::parse_error', 'Method', 'api/json_sax/parse_error/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::parse_error', 'Method', 'api/json_sax/parse_error/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_array', 'Method', 'api/json_sax/start_array/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_array', 'Method', 'api/json_sax/start_array/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_object', 'Method', 'api/json_sax/start_object/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_object', 'Method', 'api/json_sax/start_object/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::string', 'Method', 'api/json_sax/string/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::string', 'Method', 'api/json_sax/string/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::string_t', 'Type', 'api/json_sax/string_t/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::~json_sax', 'Method', 'api/json_sax/~json_sax/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json', 'Literal', 'api/operator_literal_json/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json', 'Literal', 'api/operator_literal_json/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json_pointer', 'Literal', 'api/operator_literal_json_pointer/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json_pointer', 'Literal', 'api/operator_literal_json_pointer/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator<<', 'Operator', 'api/operator_ltlt/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('operator<<', 'Operator', 'api/operator_ltlt/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator>>', 'Operator', 'api/operator_gtgt/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('operator>>', 'Operator', 'api/operator_gtgt/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json', 'Class', 'api/ordered_json/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json', 'Class', 'api/ordered_json/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map', 'Class', 'api/ordered_map/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map', 'Class', 'api/ordered_map/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::Container', 'Type', 'api/ordered_map/Container/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::at', 'Method', 'api/ordered_map/at/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::count', 'Method', 'api/ordered_map/count/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::emplace', 'Method', 'api/ordered_map/emplace/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::erase', 'Method', 'api/ordered_map/erase/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::find', 'Method', 'api/ordered_map/find/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::insert', 'Method', 'api/ordered_map/insert/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::key_compare', 'Type', 'api/ordered_map/key_compare/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::operator=', 'Operator', 'api/ordered_map/operator=/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::operator[]', 'Operator', 'api/ordered_map/operator[]/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::ordered_map', 'Constructor', 'api/ordered_map/ordered_map/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map::~ordered_map', 'Method', 'api/ordered_map/~ordered_map/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('std::formatter<basic_json>', 'Class', 'api/basic_json/std_formatter/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('std::formatter<basic_json>', 'Class', 'api/basic_json/std_formatter/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('std::hash<basic_json>', 'Class', 'api/basic_json/std_hash/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('std::hash<basic_json>', 'Class', 'api/basic_json/std_hash/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('std::swap<basic_json>', 'Function', 'api/basic_json/std_swap/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('std::swap<basic_json>', 'Function', 'api/basic_json/std_swap/index.html');
|
||||||
|
|||||||
@@ -475,8 +475,10 @@ basic_json(basic_json&& other) noexcept;
|
|||||||
1. Since version 1.0.0.
|
1. Since version 1.0.0.
|
||||||
2. Since version 1.0.0.
|
2. Since version 1.0.0.
|
||||||
3. Since version 2.1.0.
|
3. Since version 2.1.0.
|
||||||
4. Since version 3.2.0. Explicit for different string types if `JSON_USE_IMPLICIT_CONVERSIONS` is `0` since
|
4. Since version 3.2.0. Also initializes the position reported by
|
||||||
version 3.13.0.
|
[`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. Explicit
|
||||||
|
for different string types if `JSON_USE_IMPLICIT_CONVERSIONS` is `0` since version 3.13.0.
|
||||||
5. Since version 1.0.0.
|
5. Since version 1.0.0.
|
||||||
6. Since version 1.0.0.
|
6. Since version 1.0.0.
|
||||||
7. Since version 1.0.0. Fixed in version 3.13.0 to also check the iterator range for binary values; before, a range
|
7. Since version 1.0.0. Fixed in version 3.13.0 to also check the iterator range for binary values; before, a range
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -23,10 +23,9 @@ type to use.
|
|||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
`NumberFloatType`
|
`NumberFloatType`
|
||||||
: the type to store floating-point numbers. The parser converts `#!cpp float`, `#!cpp double`, and a
|
: the type to store floating-point numbers. Parsing and serialization are implemented in terms of
|
||||||
`#!cpp long double` that is IEEE 754 binary64 itself and other `#!cpp long double` formats with
|
`#!cpp std::strtof`/`#!cpp std::strtod`/`#!cpp std::strtold` and `#!cpp std::snprintf`, so the type must be
|
||||||
`#!cpp std::from_chars` or `#!cpp std::strtold`, and serialization falls back to `#!cpp std::snprintf`, so the
|
`#!cpp float`, `#!cpp double`, or `#!cpp long double`. The
|
||||||
type must be `#!cpp float`, `#!cpp double`, or `#!cpp long double`. The
|
|
||||||
[binary formats](../../features/binary_formats/index.md) additionally require `#!cpp float` or `#!cpp double`,
|
[binary formats](../../features/binary_formats/index.md) additionally require `#!cpp float` or `#!cpp double`,
|
||||||
because they have no encoding for `#!cpp long double`. See
|
because they have no encoding for `#!cpp long double`. See
|
||||||
[Template Parameter Requirements](../../features/types/template_parameters.md#numberfloattype).
|
[Template Parameter Requirements](../../features/types/template_parameters.md#numberfloattype).
|
||||||
|
|||||||
@@ -51,3 +51,5 @@ Linear.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 1.0.0.
|
- 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.
|
||||||
|
|||||||
@@ -92,3 +92,8 @@ Linear in the size of the JSON value.
|
|||||||
- Since version 1.0.0.
|
- Since version 1.0.0.
|
||||||
- Macros `JSON_EXPLICIT`/[`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) added
|
- Macros `JSON_EXPLICIT`/[`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md) added
|
||||||
in version 3.9.0.
|
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,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.
|
||||||
@@ -8,16 +8,16 @@ This type preserves the insertion order of object keys.
|
|||||||
|
|
||||||
## Iterator invalidation
|
## 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
|
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
|
[`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.
|
reference after the insertion point will point to the same index, which is now a different value.
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
[`ordered_map`](ordered_map.md) has no lookup index: every key-based object operation is a linear scan, so building or
|
[`ordered_map`](ordered_map/index.md) has no lookup index: every key-based object operation is a linear scan, so building or
|
||||||
parsing an object of `n` keys costs O(n²) rather than O(n log n). See
|
parsing an object of `n` keys costs O(n²) rather than O(n log n). See
|
||||||
[`ordered_map` complexity](ordered_map.md#complexity) for the per-operation table and for measured numbers.
|
[`ordered_map` complexity](ordered_map/index.md#complexity) for the per-operation table and for measured numbers.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ parsing an object of `n` keys costs O(n²) rather than O(n log n). See
|
|||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [ordered_map](ordered_map.md)
|
- [ordered_map](ordered_map/index.md)
|
||||||
- [Object Order](../features/object_order.md)
|
- [Object Order](../features/object_order.md)
|
||||||
|
|
||||||
## Version history
|
## 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>;
|
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>`).
|
(`nlohmann::basic_json<ordered_map>`).
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
@@ -30,19 +30,19 @@ case all iterators (including the `end()` iterator) and all references to the el
|
|||||||
|
|
||||||
When the storage grows, the keys are copied and the mapped values are moved to the new storage. A plain `std::vector`
|
When the storage grows, the keys are copied and the mapped values are moved to the new storage. A plain `std::vector`
|
||||||
would copy the whole elements instead, because their `#!cpp const` keys make them not nothrow move constructible; for
|
would copy the whole elements instead, because their `#!cpp const` keys make them not nothrow move constructible; for
|
||||||
[`ordered_json`](ordered_json.md), this would be a deep copy of every nested value. The values are only copied if
|
[`ordered_json`](../ordered_json.md), this would be a deep copy of every nested value. The values are only copied if
|
||||||
`T` is not default constructible or not nothrow move assignable.
|
`T` is not default constructible or not nothrow move assignable.
|
||||||
|
|
||||||
## Member types
|
## Member types
|
||||||
|
|
||||||
- **key_type** - key type (`Key`)
|
- **key_type** - key type (`Key`)
|
||||||
- **mapped_type** - mapped type (`T`)
|
- **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**
|
- **iterator**
|
||||||
- **const_iterator**
|
- **const_iterator**
|
||||||
- **size_type**
|
- **size_type**
|
||||||
- **value_type**
|
- **value_type**
|
||||||
- **key_compare** - key comparison function
|
- [**key_compare**](key_compare.md) - key comparison function
|
||||||
```cpp
|
```cpp
|
||||||
std::equal_to<Key> // until C++14
|
std::equal_to<Key> // until C++14
|
||||||
|
|
||||||
@@ -51,15 +51,16 @@ std::equal_to<> // since C++14
|
|||||||
|
|
||||||
## Member functions
|
## Member functions
|
||||||
|
|
||||||
- (constructor)
|
- [(constructor)](ordered_map.md)
|
||||||
- (destructor)
|
- [(destructor)](~ordered_map.md)
|
||||||
- **emplace**
|
- [**operator=**](operator=.md)
|
||||||
- **operator\[\]**
|
- [**emplace**](emplace.md)
|
||||||
- **at**
|
- [**operator\[\]**](operator[].md)
|
||||||
- **erase**
|
- [**at**](at.md)
|
||||||
- **count**
|
- [**erase**](erase.md)
|
||||||
- **find**
|
- [**count**](count.md)
|
||||||
- **insert**
|
- [**find**](find.md)
|
||||||
|
- [**insert**](insert.md)
|
||||||
|
|
||||||
## Exception safety
|
## Exception safety
|
||||||
|
|
||||||
@@ -89,7 +90,7 @@ This differs from `#!cpp std::map`, where the same operations are O(log n).
|
|||||||
!!! warning "Quadratic cost of building large objects"
|
!!! warning "Quadratic cost of building large objects"
|
||||||
|
|
||||||
Because every insertion scans all elements inserted so far, building an object of `n` distinct keys costs
|
Because every insertion scans all elements inserted so far, building an object of `n` distinct keys costs
|
||||||
**O(n²)** in total. This applies to filling an [`ordered_json`](ordered_json.md) object key by key as well as to
|
**O(n²)** in total. This applies to filling an [`ordered_json`](../ordered_json.md) object key by key as well as to
|
||||||
parsing one, since the parser inserts each key as it is read.
|
parsing one, since the parser inserts each key as it is read.
|
||||||
|
|
||||||
The cost is negligible for the object sizes typically found in configuration files or API payloads, but it grows
|
The cost is negligible for the object sizes typically found in configuration files or API payloads, but it grows
|
||||||
@@ -106,7 +107,7 @@ This differs from `#!cpp std::map`, where the same operations are O(log n).
|
|||||||
If key order matters for objects of that size, consider a container with a lookup index, such as
|
If key order matters for objects of that size, consider a container with a lookup index, such as
|
||||||
[`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map)
|
[`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map)
|
||||||
([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)), as the object type -- see
|
([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)), as the object type -- see
|
||||||
[object order](../features/object_order.md).
|
[object order](../../features/object_order.md).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@@ -126,10 +127,10 @@ This differs from `#!cpp std::map`, where the same operations are O(log n).
|
|||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [ordered_json](ordered_json.md)
|
- [ordered_json](../ordered_json.md)
|
||||||
|
|
||||||
## Version history
|
## 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.
|
- Added **key_compare** member in version 3.11.0.
|
||||||
- Changed in version 3.13.0: growing the storage moves the mapped values instead of copying them.
|
- Changed in version 3.13.0: growing the storage moves the mapped values instead of copying them.
|
||||||
@@ -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,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
|
||||||
@@ -51,16 +51,16 @@ If you do want to preserve the **insertion order**, you can use the type [`nlohm
|
|||||||
--8<-- "examples/ordered_json.output"
|
--8<-- "examples/ordered_json.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) also preserves the insertion order and, unlike [`ordered_map`](../api/ordered_map.md), keeps a lookup index, so it does not have the quadratic cost described below. It is used through a small adapter ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
|
Alternatively, [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) also preserves the insertion order and, unlike [`ordered_map`](../api/ordered_map/index.md), keeps a lookup index, so it does not have the quadratic cost described below. It is used through a small adapter ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
|
||||||
|
|
||||||
If the order does not matter and you only want faster lookup, `boost::unordered_flat_map`, `absl::flat_hash_map`, `absl::node_hash_map`, and several other hash maps work through an adapter that restores the template argument order `basic_json` expects; see [Template Parameter Requirements](types/template_parameters.md#objecttype). Note these are *unordered*, not insertion-ordered.
|
If the order does not matter and you only want faster lookup, `boost::unordered_flat_map`, `absl::flat_hash_map`, `absl::node_hash_map`, and several other hash maps work through an adapter that restores the template argument order `basic_json` expects; see [Template Parameter Requirements](types/template_parameters.md#objecttype). Note these are *unordered*, not insertion-ordered.
|
||||||
|
|
||||||
[`tsl::ordered_map`](https://github.com/Tessil/ordered-map) cannot be used: its iterators expose the mapped value as `const`, while `basic_json` needs to modify it in place.
|
[`tsl::ordered_map`](https://github.com/Tessil/ordered-map) cannot be used: its iterators expose the mapped value as `const`, while `basic_json` needs to modify it in place.
|
||||||
|
|
||||||
The [`ordered_map`](../api/ordered_map.md) behind `nlohmann::ordered_json` is deliberately minimal and has no lookup
|
The [`ordered_map`](../api/ordered_map/index.md) behind `nlohmann::ordered_json` is deliberately minimal and has no lookup
|
||||||
index, so every key access is a linear scan and building an object of `n` keys costs O(n²). This is unnoticeable at
|
index, so every key access is a linear scan and building an object of `n` keys costs O(n²). This is unnoticeable at
|
||||||
typical object sizes but becomes significant for objects with many thousands of keys; see
|
typical object sizes but becomes significant for objects with many thousands of keys; see
|
||||||
[`ordered_map` complexity](../api/ordered_map.md#complexity). The alternatives above keep a lookup index and do not
|
[`ordered_map` complexity](../api/ordered_map/index.md#complexity). The alternatives above keep a lookup index and do not
|
||||||
have this cost.
|
have this cost.
|
||||||
|
|
||||||
### Notes on parsing
|
### Notes on parsing
|
||||||
|
|||||||
@@ -82,13 +82,12 @@ flowchart TD
|
|||||||
|
|
||||||
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
|
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
|
||||||
- The number types can be changed, see [Template number types](#template-number-types).
|
- The number types can be changed, see [Template number types](#template-number-types).
|
||||||
- The library converts integers and floating-point numbers itself, independent of the locale. Floating-point
|
- Integers are converted by the library's own digit parser. Floating-point numbers are converted with
|
||||||
numbers are correctly rounded (to nearest, ties to even). Only a `#!c long double` that is not IEEE 754 binary64
|
[`std::from_chars`](https://en.cppreference.com/w/cpp/utility/from_chars) if the library is compiled with C++17
|
||||||
(e.g., the 80-bit x87 format) is converted with `#!cpp std::from_chars` where available, or else with
|
and the standard library supports it, then with an exact fast path for `#!c double` values with few significant
|
||||||
[`std::strtold`](https://en.cppreference.com/w/cpp/string/byte/strtof). For that call, the library temporarily
|
digits, and otherwise with the locale-aware
|
||||||
replaces the `.` with the decimal point of the current locale (which may be longer than one byte, e.g., in
|
[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) (`std::strtof`/`std::strtold` for the
|
||||||
`fa_IR.UTF-8`), so the result does not depend on the locale either. Changing the locale in another thread during
|
other floating-point types). Before version 3.13.0, the conversion was realized by
|
||||||
parsing is undefined behavior of the C library, though. Before version 3.13.0, the conversion was realized by
|
|
||||||
[`std::strtoull`](https://en.cppreference.com/w/cpp/string/byte/strtoul),
|
[`std::strtoull`](https://en.cppreference.com/w/cpp/string/byte/strtoul),
|
||||||
[`std::strtoll`](https://en.cppreference.com/w/cpp/string/byte/strtol), and `std::strtod`, respectively.
|
[`std::strtoll`](https://en.cppreference.com/w/cpp/string/byte/strtol), and `std::strtod`, respectively.
|
||||||
|
|
||||||
@@ -101,10 +100,10 @@ flowchart TD
|
|||||||
### Number limits
|
### Number limits
|
||||||
|
|
||||||
- Any 64-bit signed or unsigned integer can be stored without loss of precision.
|
- Any 64-bit signed or unsigned integer can be stored without loss of precision.
|
||||||
- Numbers exceeding the limits of `#!c double` (i.e., numbers whose rounded value is not satisfying
|
- Numbers exceeding the limits of `#!c double` (i.e., numbers that after conversion via
|
||||||
|
[`std::strtod`](https://en.cppreference.com/w/cpp/string/byte/strtof) are not satisfying
|
||||||
[`std::isfinite`](https://en.cppreference.com/w/cpp/numeric/math/isfinite) such as `#!c 1E400`) will throw exception
|
[`std::isfinite`](https://en.cppreference.com/w/cpp/numeric/math/isfinite) such as `#!c 1E400`) will throw exception
|
||||||
[`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing. Numbers too
|
[`json.exception.out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406) during parsing.
|
||||||
small for `#!c double` (such as `#!c 1E-400`) become zero, with the sign of the number.
|
|
||||||
- Floating-point numbers are rounded to the next number representable as `double`. For instance
|
- Floating-point numbers are rounded to the next number representable as `double`. For instance
|
||||||
`#!c 3.141592653589793238462643383279` is stored as [`0x400921fb54442d18`](https://float.exposed/0x400921fb54442d18).
|
`#!c 3.141592653589793238462643383279` is stored as [`0x400921fb54442d18`](https://float.exposed/0x400921fb54442d18).
|
||||||
This is the same behavior as the code `#!c double x = 3.141592653589793238462643383279;`.
|
This is the same behavior as the code `#!c double x = 3.141592653589793238462643383279;`.
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ Requirements are split into two groups:
|
|||||||
diagnosed with dedicated error messages, and violating most of them results in a compiler error somewhere inside
|
diagnosed with dedicated error messages, and violating most of them results in a compiler error somewhere inside
|
||||||
the library. Four violations are not caught at compile time at all:
|
the library. Four violations are not caught at compile time at all:
|
||||||
|
|
||||||
- A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and silently misparses numbers
|
- A [`StringType`](#stringtype) whose `data()` is not null-terminated compiles and can silently misparse
|
||||||
stored as a `#!cpp long double` that is not IEEE 754 binary64 (e.g., the 80-bit x87 format), because the lexer
|
floating-point numbers, because the lexer may hand the buffer to `#!cpp std::strtod`, which reads up to the
|
||||||
hands the buffer to `#!cpp std::strtold`.
|
terminating null character.
|
||||||
- A stateful [`AllocatorType`](#allocatortype) compiles and silently ignores its state: allocation, deallocation,
|
- A stateful [`AllocatorType`](#allocatortype) compiles and silently ignores its state: allocation, deallocation,
|
||||||
and [`get_allocator()`](../../api/basic_json/get_allocator.md) each use a different default-constructed instance.
|
and [`get_allocator()`](../../api/basic_json/get_allocator.md) each use a different default-constructed instance.
|
||||||
- The two [cross-specialization conversions](#cross-specialization-conversions) below. These abort on an assertion
|
- The two [cross-specialization conversions](#cross-specialization-conversions) below. These abort on an assertion
|
||||||
@@ -38,7 +38,7 @@ Requirements are split into two groups:
|
|||||||
|
|
||||||
| Template parameter | Default | Notable substitutes |
|
| Template parameter | Default | Notable substitutes |
|
||||||
|-------------------------------------------------------------------|-----------------------------------|-----------------------------------------------------------------------|
|
|-------------------------------------------------------------------|-----------------------------------|-----------------------------------------------------------------------|
|
||||||
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map.md), Abseil hash maps |
|
| [`ObjectType`](#objecttype) | `std::map` | [`nlohmann::ordered_map`](../../api/ordered_map/index.md), Abseil hash maps |
|
||||||
| [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` |
|
| [`ArrayType`](#arraytype) | `std::vector` | `#!cpp std::deque` |
|
||||||
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
|
| [`StringType`](#stringtype) | `std::string` | `std::string`-like types over `char` |
|
||||||
| [`BooleanType`](#booleantype) | `bool` | none worth using |
|
| [`BooleanType`](#booleantype) | `bool` | none worth using |
|
||||||
@@ -231,7 +231,7 @@ The library does not sort or de-duplicate keys itself; the behavior described in
|
|||||||
| Container | Notes |
|
| Container | Notes |
|
||||||
|----------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
|
|----------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
|
||||||
| `#!cpp std::map` (default) | |
|
| `#!cpp std::map` (default) | |
|
||||||
| [`nlohmann::ordered_map`](../../api/ordered_map.md) | used by [`ordered_json`](../../api/ordered_json.md); keeps insertion order |
|
| [`nlohmann::ordered_map`](../../api/ordered_map/index.md) | used by [`ordered_json`](../../api/ordered_json.md); keeps insertion order |
|
||||||
| [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) | keeps insertion order; adapter puts `fifo_map_compare` in the comparator slot |
|
| [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) | keeps insertion order; adapter puts `fifo_map_compare` in the comparator slot |
|
||||||
| `boost::container::map`, `boost::container::flat_map` | no adapter needed |
|
| `boost::container::map`, `boost::container::flat_map` | no adapter needed |
|
||||||
| `#!cpp std::unordered_map` | through the adapter above; not with libstdc++ 9, see the note |
|
| `#!cpp std::unordered_map` | through the adapter above; not with libstdc++ 9, see the note |
|
||||||
@@ -537,10 +537,9 @@ therefore silently changes parse results rather than raising an error. See
|
|||||||
|
|
||||||
`NumberFloatType` must be one of `#!cpp float`, `#!cpp double`, or `#!cpp long double`:
|
`NumberFloatType` must be one of `#!cpp float`, `#!cpp double`, or `#!cpp long double`:
|
||||||
|
|
||||||
- The [parser](../parsing/index.md) converts number literals to `#!cpp float`, `#!cpp double`, and a
|
- The [parser](../parsing/index.md) converts number literals with `#!cpp std::from_chars` or, as a fallback, with
|
||||||
`#!cpp long double` that is IEEE 754 binary64 itself; other `#!cpp long double` formats are converted with
|
`#!cpp std::strtof`, `#!cpp std::strtod`, or `#!cpp std::strtold`; the library provides overloads for exactly these
|
||||||
`#!cpp std::from_chars` where available, or with `#!cpp std::strtold`. The library provides overloads for exactly
|
three types.
|
||||||
these three types.
|
|
||||||
- [`dump`](../../api/basic_json/dump.md) falls back to `#!cpp std::snprintf` with the `%g` and `%Lg` conversion
|
- [`dump`](../../api/basic_json/dump.md) falls back to `#!cpp std::snprintf` with the `%g` and `%Lg` conversion
|
||||||
specifiers, for which the library likewise provides only `#!cpp double` and `#!cpp long double` overloads
|
specifiers, for which the library likewise provides only `#!cpp double` and `#!cpp long double` overloads
|
||||||
(`#!cpp float` is promoted to `#!cpp double`).
|
(`#!cpp float` is promoted to `#!cpp double`).
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -50,7 +50,7 @@ The public headers are in [`include/nlohmann`](https://github.com/nlohmann/json/
|
|||||||
- [`adl_serializer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/adl_serializer.hpp), [`byte_container_with_subtype.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/byte_container_with_subtype.hpp), and [`ordered_map.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/ordered_map.hpp) define
|
- [`adl_serializer.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/adl_serializer.hpp), [`byte_container_with_subtype.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/byte_container_with_subtype.hpp), and [`ordered_map.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/ordered_map.hpp) define
|
||||||
[`adl_serializer`](../api/adl_serializer/index.md),
|
[`adl_serializer`](../api/adl_serializer/index.md),
|
||||||
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md), and
|
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md), and
|
||||||
[`ordered_map`](../api/ordered_map.md).
|
[`ordered_map`](../api/ordered_map/index.md).
|
||||||
|
|
||||||
Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths
|
Everything else lives in [`detail/`](https://github.com/nlohmann/json/tree/develop/include/nlohmann/detail) and namespace `nlohmann::detail`, which is not part of the public API. Paths
|
||||||
below are relative to `include/nlohmann`.
|
below are relative to `include/nlohmann`.
|
||||||
@@ -97,7 +97,7 @@ is generated from these files with `make amalgamate` and must not be edited by h
|
|||||||
The library provides two specializations:
|
The library provides two specializations:
|
||||||
|
|
||||||
- [`json`](../api/json.md) uses all default template arguments.
|
- [`json`](../api/json.md) uses all default template arguments.
|
||||||
- [`ordered_json`](../api/ordered_json.md) uses [`ordered_map`](../api/ordered_map.md) as `ObjectType` to keep the
|
- [`ordered_json`](../api/ordered_json.md) uses [`ordered_map`](../api/ordered_map/index.md) as `ObjectType` to keep the
|
||||||
insertion order of object keys.
|
insertion order of object keys.
|
||||||
|
|
||||||
The requirements on the template arguments are listed in
|
The requirements on the template arguments are listed in
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ The class contains a slightly modified version of the Grisu2 algorithm from Flor
|
|||||||
|
|
||||||
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||||
|
|
||||||
The class contains an adapted version of the Eisel-Lemire algorithm, its table of powers of five, and its digit comparison for long numbers from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
This page summarizes the notable changes of every release and links to the relevant documentation.
|
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
|
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).
|
||||||
|
|
||||||
!!! info "Unreleased changes"
|
!!! info "Unreleased changes"
|
||||||
|
|
||||||
|
|||||||
+28
-1
@@ -52,6 +52,7 @@ nav:
|
|||||||
- "FAQ": home/faq.md
|
- "FAQ": home/faq.md
|
||||||
- home/exceptions.md
|
- home/exceptions.md
|
||||||
- home/releases.md
|
- home/releases.md
|
||||||
|
- home/api_changes.md
|
||||||
- home/design_goals.md
|
- home/design_goals.md
|
||||||
- home/architecture.md
|
- home/architecture.md
|
||||||
- home/customers.md
|
- home/customers.md
|
||||||
@@ -123,6 +124,7 @@ nav:
|
|||||||
- 'begin': api/basic_json/begin.md
|
- 'begin': api/basic_json/begin.md
|
||||||
- 'binary': api/basic_json/binary.md
|
- 'binary': api/basic_json/binary.md
|
||||||
- 'binary_t': api/basic_json/binary_t.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
|
- 'boolean_t': api/basic_json/boolean_t.md
|
||||||
- 'cbegin': api/basic_json/cbegin.md
|
- 'cbegin': api/basic_json/cbegin.md
|
||||||
- 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md
|
- 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md
|
||||||
@@ -161,6 +163,7 @@ nav:
|
|||||||
- 'get_to': api/basic_json/get_to.md
|
- 'get_to': api/basic_json/get_to.md
|
||||||
- 'std::formatter<basic_json>': api/basic_json/std_formatter.md
|
- 'std::formatter<basic_json>': api/basic_json/std_formatter.md
|
||||||
- 'std::hash<basic_json>': api/basic_json/std_hash.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
|
- 'input_format_t': api/basic_json/input_format_t.md
|
||||||
- 'insert': api/basic_json/insert.md
|
- 'insert': api/basic_json/insert.md
|
||||||
- 'invalid_iterator': api/basic_json/invalid_iterator.md
|
- 'invalid_iterator': api/basic_json/invalid_iterator.md
|
||||||
@@ -179,6 +182,7 @@ nav:
|
|||||||
- 'is_structured': api/basic_json/is_structured.md
|
- 'is_structured': api/basic_json/is_structured.md
|
||||||
- 'items': api/basic_json/items.md
|
- 'items': api/basic_json/items.md
|
||||||
- 'json_base_class_t': api/basic_json/json_base_class_t.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
|
- 'json_serializer': api/basic_json/json_serializer.md
|
||||||
- 'max_size': api/basic_json/max_size.md
|
- 'max_size': api/basic_json/max_size.md
|
||||||
- 'meta': api/basic_json/meta.md
|
- 'meta': api/basic_json/meta.md
|
||||||
@@ -236,11 +240,13 @@ nav:
|
|||||||
- 'Overview': api/byte_container_with_subtype/index.md
|
- 'Overview': api/byte_container_with_subtype/index.md
|
||||||
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
|
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
|
||||||
- 'clear_subtype': api/byte_container_with_subtype/clear_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
|
- '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_eq.md
|
||||||
- 'operator!=': api/byte_container_with_subtype/operator_ne.md
|
- 'operator!=': api/byte_container_with_subtype/operator_ne.md
|
||||||
- 'set_subtype': api/byte_container_with_subtype/set_subtype.md
|
- 'set_subtype': api/byte_container_with_subtype/set_subtype.md
|
||||||
- 'subtype': api/byte_container_with_subtype/subtype.md
|
- 'subtype': api/byte_container_with_subtype/subtype.md
|
||||||
|
- 'subtype_type': api/byte_container_with_subtype/subtype_type.md
|
||||||
- adl_serializer:
|
- adl_serializer:
|
||||||
- 'Overview': api/adl_serializer/index.md
|
- 'Overview': api/adl_serializer/index.md
|
||||||
- 'from_json': api/adl_serializer/from_json.md
|
- 'from_json': api/adl_serializer/from_json.md
|
||||||
@@ -267,25 +273,46 @@ nav:
|
|||||||
- 'to_string': api/json_pointer/to_string.md
|
- 'to_string': api/json_pointer/to_string.md
|
||||||
- json_sax:
|
- json_sax:
|
||||||
- 'Overview': api/json_sax/index.md
|
- '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': api/json_sax/binary.md
|
||||||
|
- 'binary_t': api/json_sax/binary_t.md
|
||||||
- 'boolean': api/json_sax/boolean.md
|
- 'boolean': api/json_sax/boolean.md
|
||||||
- 'end_array': api/json_sax/end_array.md
|
- 'end_array': api/json_sax/end_array.md
|
||||||
- 'end_object': api/json_sax/end_object.md
|
- 'end_object': api/json_sax/end_object.md
|
||||||
- 'key': api/json_sax/key.md
|
- 'key': api/json_sax/key.md
|
||||||
- 'null': api/json_sax/null.md
|
- 'null': api/json_sax/null.md
|
||||||
- 'number_float': api/json_sax/number_float.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': 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': api/json_sax/number_unsigned.md
|
||||||
|
- 'number_unsigned_t': api/json_sax/number_unsigned_t.md
|
||||||
- 'parse_error': api/json_sax/parse_error.md
|
- 'parse_error': api/json_sax/parse_error.md
|
||||||
- 'start_array': api/json_sax/start_array.md
|
- 'start_array': api/json_sax/start_array.md
|
||||||
- 'start_object': api/json_sax/start_object.md
|
- 'start_object': api/json_sax/start_object.md
|
||||||
- 'string': api/json_sax/string.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), operator<<(json_pointer)': api/operator_ltlt.md
|
||||||
- 'operator>>(basic_json)': api/operator_gtgt.md
|
- 'operator>>(basic_json)': api/operator_gtgt.md
|
||||||
- 'operator""_json': api/operator_literal_json.md
|
- 'operator""_json': api/operator_literal_json.md
|
||||||
- 'operator""_json_pointer': api/operator_literal_json_pointer.md
|
- 'operator""_json_pointer': api/operator_literal_json_pointer.md
|
||||||
- 'ordered_json': api/ordered_json.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:
|
- macros:
|
||||||
- 'Overview': api/macros/index.md
|
- 'Overview': api/macros/index.md
|
||||||
- 'JSON_ASSERT': api/macros/json_assert.md
|
- 'JSON_ASSERT': api/macros/json_assert.md
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ template<typename BinaryType>
|
|||||||
class byte_container_with_subtype : public BinaryType
|
class byte_container_with_subtype : public BinaryType
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/container_type/
|
||||||
using container_type = BinaryType;
|
using container_type = BinaryType;
|
||||||
|
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype_type/
|
||||||
using subtype_type = std::uint64_t;
|
using subtype_type = std::uint64_t;
|
||||||
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
/// @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)
|
, 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
|
bool operator==(const byte_container_with_subtype& rhs) const
|
||||||
{
|
{
|
||||||
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
|
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);
|
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
|
bool operator!=(const byte_container_with_subtype& rhs) const
|
||||||
{
|
{
|
||||||
return !(rhs == *this);
|
return !(rhs == *this);
|
||||||
|
|||||||
@@ -34,15 +34,21 @@ input.
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
struct json_sax
|
struct json_sax
|
||||||
{
|
{
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_sax/number_integer_t/
|
||||||
using number_integer_t = typename BasicJsonType::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;
|
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;
|
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;
|
using string_t = typename BasicJsonType::string_t;
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_sax/binary_t/
|
||||||
using binary_t = typename BasicJsonType::binary_t;
|
using binary_t = typename BasicJsonType::binary_t;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief a null value was read
|
@brief a null value was read
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
|
@sa https://json.nlohmann.me/api/json_sax/null/
|
||||||
*/
|
*/
|
||||||
virtual bool null() = 0;
|
virtual bool null() = 0;
|
||||||
|
|
||||||
@@ -50,6 +56,7 @@ struct json_sax
|
|||||||
@brief a boolean value was read
|
@brief a boolean value was read
|
||||||
@param[in] val boolean value
|
@param[in] val boolean value
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
|
@sa https://json.nlohmann.me/api/json_sax/boolean/
|
||||||
*/
|
*/
|
||||||
virtual bool boolean(bool val) = 0;
|
virtual bool boolean(bool val) = 0;
|
||||||
|
|
||||||
@@ -57,6 +64,7 @@ struct json_sax
|
|||||||
@brief an integer number was read
|
@brief an integer number was read
|
||||||
@param[in] val integer value
|
@param[in] val integer value
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
|
@sa https://json.nlohmann.me/api/json_sax/number_integer/
|
||||||
*/
|
*/
|
||||||
virtual bool number_integer(number_integer_t val) = 0;
|
virtual bool number_integer(number_integer_t val) = 0;
|
||||||
|
|
||||||
@@ -64,6 +72,7 @@ struct json_sax
|
|||||||
@brief an unsigned integer number was read
|
@brief an unsigned integer number was read
|
||||||
@param[in] val unsigned integer value
|
@param[in] val unsigned integer value
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
|
@sa https://json.nlohmann.me/api/json_sax/number_unsigned/
|
||||||
*/
|
*/
|
||||||
virtual bool number_unsigned(number_unsigned_t val) = 0;
|
virtual bool number_unsigned(number_unsigned_t val) = 0;
|
||||||
|
|
||||||
@@ -72,6 +81,7 @@ struct json_sax
|
|||||||
@param[in] val floating-point value
|
@param[in] val floating-point value
|
||||||
@param[in] s raw token value
|
@param[in] s raw token value
|
||||||
@return whether parsing should proceed
|
@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;
|
virtual bool number_float(number_float_t val, const string_t& s) = 0;
|
||||||
|
|
||||||
@@ -80,6 +90,7 @@ struct json_sax
|
|||||||
@param[in] val string value
|
@param[in] val string value
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
@note It is safe to move the passed string value.
|
@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;
|
virtual bool string(string_t& val) = 0;
|
||||||
|
|
||||||
@@ -88,6 +99,7 @@ struct json_sax
|
|||||||
@param[in] val binary value
|
@param[in] val binary value
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
@note It is safe to move the passed binary value.
|
@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;
|
virtual bool binary(binary_t& val) = 0;
|
||||||
|
|
||||||
@@ -96,6 +108,7 @@ struct json_sax
|
|||||||
@param[in] elements number of object elements or -1 if unknown
|
@param[in] elements number of object elements or -1 if unknown
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
@note binary formats may report the number of elements
|
@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;
|
virtual bool start_object(std::size_t elements) = 0;
|
||||||
|
|
||||||
@@ -104,12 +117,14 @@ struct json_sax
|
|||||||
@param[in] val object key
|
@param[in] val object key
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
@note It is safe to move the passed string.
|
@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;
|
virtual bool key(string_t& val) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief the end of an object was read
|
@brief the end of an object was read
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
|
@sa https://json.nlohmann.me/api/json_sax/end_object/
|
||||||
*/
|
*/
|
||||||
virtual bool end_object() = 0;
|
virtual bool end_object() = 0;
|
||||||
|
|
||||||
@@ -118,12 +133,14 @@ struct json_sax
|
|||||||
@param[in] elements number of array elements or -1 if unknown
|
@param[in] elements number of array elements or -1 if unknown
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
@note binary formats may report the number of elements
|
@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;
|
virtual bool start_array(std::size_t elements) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief the end of an array was read
|
@brief the end of an array was read
|
||||||
@return whether parsing should proceed
|
@return whether parsing should proceed
|
||||||
|
@sa https://json.nlohmann.me/api/json_sax/end_array/
|
||||||
*/
|
*/
|
||||||
virtual bool end_array() = 0;
|
virtual bool end_array() = 0;
|
||||||
|
|
||||||
@@ -133,16 +150,23 @@ struct json_sax
|
|||||||
@param[in] last_token the last read token
|
@param[in] last_token the last read token
|
||||||
@param[in] ex an exception object describing the error
|
@param[in] ex an exception object describing the error
|
||||||
@return whether parsing should proceed (must return false)
|
@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,
|
virtual bool parse_error(std::size_t position,
|
||||||
const std::string& last_token,
|
const std::string& last_token,
|
||||||
const detail::exception& ex) = 0;
|
const detail::exception& ex) = 0;
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
|
||||||
json_sax() = default;
|
json_sax() = default;
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
|
||||||
json_sax(const json_sax&) = default;
|
json_sax(const json_sax&) = default;
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_sax/json_sax/
|
||||||
json_sax(json_sax&&) noexcept = default;
|
json_sax(json_sax&&) noexcept = default;
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
|
||||||
json_sax& operator=(const json_sax&) = default;
|
json_sax& operator=(const json_sax&) = default;
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_sax/operator=/
|
||||||
json_sax& operator=(json_sax&&) noexcept = default;
|
json_sax& operator=(json_sax&&) noexcept = default;
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_sax/~json_sax/
|
||||||
virtual ~json_sax() = default;
|
virtual ~json_sax() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1044,11 +1044,9 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
token_type::parse_error otherwise
|
token_type::parse_error otherwise
|
||||||
|
|
||||||
@note The scanner is independent of the current locale: token_buffer
|
@note The scanner is independent of the current locale: token_buffer
|
||||||
always holds `.`. The conversion of float and double does not use
|
always holds `.`. Only the std::strtod fallback of convert_number()
|
||||||
the locale either. Only the std::strtold fallback of
|
depends on the locale, and it looks up the decimal point right
|
||||||
convert_number() for long double formats other than binary64
|
before converting (see detail::convert_float_locale_aware()).
|
||||||
depends on it, and it looks up the decimal point right before
|
|
||||||
converting (see detail::convert_float_locale_aware()).
|
|
||||||
*/
|
*/
|
||||||
token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
|
token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
|
||||||
{
|
{
|
||||||
@@ -1061,7 +1059,7 @@ class lexer : public lexer_base<BasicJsonType>
|
|||||||
|
|
||||||
// offset just past the last mantissa byte in token_buffer (i.e. the
|
// offset just past the last mantissa byte in token_buffer (i.e. the
|
||||||
// index of 'e'/'E', or the whole token when there is no exponent).
|
// index of 'e'/'E', or the whole token when there is no exponent).
|
||||||
// convert_number() uses it to split the token; npos means
|
// convert_number() uses it to count significant digits; npos means
|
||||||
// "not seen an exponent yet" and is resolved at scan_number_done
|
// "not seen an exponent yet" and is resolved at scan_number_done
|
||||||
std::size_t mantissa_end = std::string::npos;
|
std::size_t mantissa_end = std::string::npos;
|
||||||
|
|
||||||
@@ -1391,8 +1389,8 @@ scan_number_done:
|
|||||||
@param[in] mantissa_end offset just past the last mantissa byte in
|
@param[in] mantissa_end offset just past the last mantissa byte in
|
||||||
token_buffer (the index of 'e'/'E', or
|
token_buffer (the index of 'e'/'E', or
|
||||||
token_buffer.size() when there is no exponent);
|
token_buffer.size() when there is no exponent);
|
||||||
with decimal_point_position, it locates the parts
|
used to skip Clinger's fast path when it cannot
|
||||||
of a float token without scanning it again
|
possibly succeed - see detail::mantissa_fits_clinger()
|
||||||
*/
|
*/
|
||||||
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
token_type convert_number(token_type number_type, std::size_t mantissa_end)
|
||||||
{
|
{
|
||||||
@@ -1446,11 +1444,10 @@ scan_number_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this code is reached if we parse a floating-point number or if an
|
// this code is reached if we parse a floating-point number or if an
|
||||||
// integer conversion above overflowed. float and double (and long
|
// integer conversion above overflowed. Prefer std::from_chars
|
||||||
// double where it is binary64) are converted by the library itself,
|
// (Eisel-Lemire, locale-independent, correctly rounded) when available;
|
||||||
// correctly rounded and independent of the locale; other long double
|
// otherwise the exact Clinger fast path (double only); otherwise the
|
||||||
// formats use std::from_chars when available, otherwise the
|
// locale-aware strtof/strtod/strtold.
|
||||||
// locale-aware strtold.
|
|
||||||
if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float))
|
if (convert_float_fast(num_begin, num_end, decimal_point_position, mantissa_end, value_float))
|
||||||
{
|
{
|
||||||
return token_type::value_float;
|
return token_type::value_float;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,7 @@ class json_pointer
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// for backwards compatibility accept BasicJsonType
|
// for backwards compatibility accept BasicJsonType
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_pointer/string_t/
|
||||||
using string_t = typename string_t_helper<RefStringType>::type;
|
using string_t = typename string_t_helper<RefStringType>::type;
|
||||||
|
|
||||||
/// @brief create JSON pointer
|
/// @brief create JSON pointer
|
||||||
|
|||||||
@@ -205,25 +205,34 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
using serializer = ::nlohmann::detail::serializer<basic_json>;
|
using serializer = ::nlohmann::detail::serializer<basic_json>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// @brief the type of the JSON value
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/value_t/
|
||||||
using value_t = detail::value_t;
|
using value_t = detail::value_t;
|
||||||
/// JSON Pointer, see @ref nlohmann::json_pointer
|
/// JSON Pointer, see @ref nlohmann::json_pointer
|
||||||
using json_pointer = ::nlohmann::json_pointer<StringType>;
|
using json_pointer = ::nlohmann::json_pointer<StringType>;
|
||||||
template<typename T, typename SFINAE>
|
template<typename T, typename SFINAE>
|
||||||
using json_serializer = JSONSerializer<T, SFINAE>;
|
using json_serializer = JSONSerializer<T, SFINAE>;
|
||||||
/// how to treat decoding errors
|
/// how to treat decoding errors
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/error_handler_t/
|
||||||
using error_handler_t = detail::error_handler_t;
|
using error_handler_t = detail::error_handler_t;
|
||||||
/// how to treat CBOR tags
|
/// 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;
|
using cbor_tag_handler_t = detail::cbor_tag_handler_t;
|
||||||
/// how to encode BJData
|
/// how to encode BJData
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/bjdata_version_t/
|
||||||
using bjdata_version_t = detail::bjdata_version_t;
|
using bjdata_version_t = detail::bjdata_version_t;
|
||||||
/// base class used to inject custom functionality into each instance of basic_json
|
/// base class used to inject custom functionality into each instance of basic_json
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
|
/// @sa https://json.nlohmann.me/api/basic_json/json_base_class_t/
|
||||||
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
|
using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
|
||||||
/// helper type for initializer lists of basic_json values
|
/// 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>>;
|
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;
|
using input_format_t = detail::input_format_t;
|
||||||
/// SAX interface type, see @ref nlohmann::json_sax
|
/// 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>;
|
using json_sax_t = json_sax<basic_json>;
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
@@ -365,15 +374,19 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// the template arguments passed to class @ref basic_json.
|
/// the template arguments passed to class @ref basic_json.
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
|
#if defined(JSON_HAS_CPP_14)
|
||||||
/// @brief default object key comparator type
|
/// @brief default object key comparator type
|
||||||
/// The actual object key comparator type (@ref object_comparator_t) may be
|
/// The actual object key comparator type (@ref object_comparator_t) may be
|
||||||
/// different.
|
/// different.
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
|
/// @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
|
// 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)
|
// in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
|
||||||
using default_object_comparator_t = std::less<>;
|
using default_object_comparator_t = std::less<>;
|
||||||
#else
|
#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>;
|
using default_object_comparator_t = std::less<StringType>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2267,6 +2280,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// other constructors and destructor //
|
// other constructors and destructor //
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/basic_json/
|
||||||
template<typename JsonRef,
|
template<typename JsonRef,
|
||||||
detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
|
detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
|
||||||
std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
|
std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
|
||||||
@@ -2852,6 +2866,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
|
@throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
|
||||||
|
|
||||||
@since version 2.1.0
|
@since version 2.1.0
|
||||||
|
|
||||||
|
@sa https://json.nlohmann.me/api/basic_json/get/
|
||||||
*/
|
*/
|
||||||
template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
|
template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
|
||||||
#if defined(JSON_HAS_CPP_14)
|
#if defined(JSON_HAS_CPP_14)
|
||||||
@@ -2895,6 +2911,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
@sa see @ref get_ptr() for explicit pointer-member access
|
@sa see @ref get_ptr() for explicit pointer-member access
|
||||||
|
|
||||||
@since version 1.0.0
|
@since version 1.0.0
|
||||||
|
|
||||||
|
@sa https://json.nlohmann.me/api/basic_json/get/
|
||||||
*/
|
*/
|
||||||
template<typename PointerType, typename std::enable_if<
|
template<typename PointerType, typename std::enable_if<
|
||||||
std::is_pointer<PointerType>::value, int>::type = 0>
|
std::is_pointer<PointerType>::value, int>::type = 0>
|
||||||
@@ -2921,6 +2939,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
// specialization to allow calling get_to with a basic_json value
|
// specialization to allow calling get_to with a basic_json value
|
||||||
// see https://github.com/nlohmann/json/issues/2175
|
// see https://github.com/nlohmann/json/issues/2175
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
|
||||||
template<typename ValueType,
|
template<typename ValueType,
|
||||||
detail::enable_if_t <
|
detail::enable_if_t <
|
||||||
detail::is_basic_json<ValueType>::value,
|
detail::is_basic_json<ValueType>::value,
|
||||||
@@ -2931,6 +2950,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/get_to/
|
||||||
template <
|
template <
|
||||||
typename T, std::size_t N,
|
typename T, std::size_t N,
|
||||||
typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||||
@@ -2995,6 +3015,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
@since version 1.0.0
|
@since version 1.0.0
|
||||||
*/
|
*/
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_ValueType/
|
||||||
template < typename ValueType, typename std::enable_if <
|
template < typename ValueType, typename std::enable_if <
|
||||||
detail::conjunction <
|
detail::conjunction <
|
||||||
detail::negation<std::is_pointer<ValueType>>,
|
detail::negation<std::is_pointer<ValueType>>,
|
||||||
@@ -3303,12 +3324,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
|
// these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
|
||||||
// (they seemingly cannot be constrained to resolve the ambiguity)
|
// (they seemingly cannot be constrained to resolve the ambiguity)
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
reference operator[](T* key)
|
reference operator[](T* key)
|
||||||
{
|
{
|
||||||
return operator[](typename object_t::key_type(key));
|
return operator[](typename object_t::key_type(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/operator[]/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
const_reference operator[](T* key) const
|
const_reference operator[](T* key) const
|
||||||
{
|
{
|
||||||
@@ -3486,6 +3509,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return found != nullptr ? found->template get<ReturnType>() : std::forward<ValueType>(default_value);
|
return found != nullptr ? found->template get<ReturnType>() : std::forward<ValueType>(default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/value/
|
||||||
template < class ValueType, class BasicJsonType, detail::enable_if_t <
|
template < class ValueType, class BasicJsonType, detail::enable_if_t <
|
||||||
detail::is_basic_json<BasicJsonType>::value
|
detail::is_basic_json<BasicJsonType>::value
|
||||||
&& detail::is_getable<basic_json_t, ValueType>::value
|
&& detail::is_getable<basic_json_t, ValueType>::value
|
||||||
@@ -3496,6 +3520,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return value(ptr.convert(), default_value);
|
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,
|
template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
|
||||||
detail::enable_if_t <
|
detail::enable_if_t <
|
||||||
detail::is_basic_json<BasicJsonType>::value
|
detail::is_basic_json<BasicJsonType>::value
|
||||||
@@ -3859,6 +3884,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return ptr.contains(this);
|
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>
|
template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
|
||||||
@@ -5354,6 +5380,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
|
||||||
static basic_json parse(detail::span_input_adapter&& i,
|
static basic_json parse(detail::span_input_adapter&& i,
|
||||||
@@ -5391,6 +5418,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, true).accept(true);
|
return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments, ignore_trailing_commas, true).accept(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
|
||||||
static bool accept(detail::span_input_adapter&& i,
|
static bool accept(detail::span_input_adapter&& i,
|
||||||
@@ -5822,6 +5850,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::cbor, strict, allow_exceptions, error_handler, tag_handler);
|
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::cbor, strict, allow_exceptions, error_handler, tag_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
|
||||||
@@ -5833,6 +5862,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
|
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_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
|
||||||
static basic_json from_cbor(detail::span_input_adapter&& i,
|
static basic_json from_cbor(detail::span_input_adapter&& i,
|
||||||
@@ -5868,6 +5898,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::msgpack, strict, allow_exceptions, error_handler);
|
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::msgpack, strict, allow_exceptions, error_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
|
||||||
@@ -5878,6 +5909,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
|
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_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
|
||||||
static basic_json from_msgpack(detail::span_input_adapter&& i,
|
static basic_json from_msgpack(detail::span_input_adapter&& i,
|
||||||
@@ -5912,6 +5944,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::ubjson, strict, allow_exceptions, error_handler);
|
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::ubjson, strict, allow_exceptions, error_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
|
||||||
@@ -5922,6 +5955,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
|
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_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
|
||||||
static basic_json from_ubjson(detail::span_input_adapter&& i,
|
static basic_json from_ubjson(detail::span_input_adapter&& i,
|
||||||
@@ -6004,6 +6038,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bson, strict, allow_exceptions, error_handler);
|
return from_binary_impl(detail::input_adapter(std::move(first), std::move(last)), input_format_t::bson, strict, allow_exceptions, error_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
|
||||||
@@ -6014,6 +6049,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return from_bson(ptr, ptr + len, strict, allow_exceptions);
|
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_WARN_UNUSED_RESULT
|
||||||
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
|
JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
|
||||||
static basic_json from_bson(detail::span_input_adapter&& i,
|
static basic_json from_bson(detail::span_input_adapter&& i,
|
||||||
@@ -6038,6 +6074,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return ptr.get_unchecked(this);
|
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>
|
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)
|
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)
|
reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
|
||||||
@@ -6052,6 +6089,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return ptr.get_unchecked(this);
|
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>
|
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)
|
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
|
const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
|
||||||
@@ -6066,6 +6104,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return ptr.get_checked(this);
|
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>
|
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)
|
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)
|
reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
|
||||||
@@ -6080,6 +6119,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return ptr.get_checked(this);
|
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>
|
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)
|
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
|
const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
|
||||||
|
|||||||
@@ -34,30 +34,41 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
|||||||
{
|
{
|
||||||
using key_type = Key;
|
using key_type = Key;
|
||||||
using mapped_type = T;
|
using mapped_type = T;
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/Container/
|
||||||
using Container = std::vector<std::pair<const Key, T>, Allocator>;
|
using Container = std::vector<std::pair<const Key, T>, Allocator>;
|
||||||
using iterator = typename Container::iterator;
|
using iterator = typename Container::iterator;
|
||||||
using const_iterator = typename Container::const_iterator;
|
using const_iterator = typename Container::const_iterator;
|
||||||
using size_type = typename Container::size_type;
|
using size_type = typename Container::size_type;
|
||||||
using value_type = typename Container::value_type;
|
using value_type = typename Container::value_type;
|
||||||
#ifdef JSON_HAS_CPP_14
|
#ifdef JSON_HAS_CPP_14
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
|
||||||
using key_compare = std::equal_to<>;
|
using key_compare = std::equal_to<>;
|
||||||
#else
|
#else
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/key_compare/
|
||||||
using key_compare = std::equal_to<Key>;
|
using key_compare = std::equal_to<Key>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Explicit constructors instead of `using Container::Container`
|
// Explicit constructors instead of `using Container::Container`
|
||||||
// otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
|
// 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{} {}
|
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} {}
|
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>
|
template <class It>
|
||||||
ordered_map(It first, It last, const Allocator& alloc = Allocator())
|
ordered_map(It first, It last, const Allocator& alloc = Allocator())
|
||||||
: Container{first, last, alloc} {}
|
: 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() )
|
ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
|
||||||
: Container{init, alloc} {}
|
: Container{init, alloc} {}
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/ordered_map/
|
||||||
ordered_map(const ordered_map&) = default;
|
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;
|
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;
|
~ordered_map() = default;
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/operator=/
|
||||||
ordered_map& operator=(const ordered_map& other)
|
ordered_map& operator=(const ordered_map& other)
|
||||||
{
|
{
|
||||||
if (this != &other)
|
if (this != &other)
|
||||||
@@ -68,6 +79,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
|||||||
return *this;
|
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)
|
ordered_map& operator=(ordered_map&& other) noexcept(std::is_nothrow_move_assignable<Container>::value)
|
||||||
{
|
{
|
||||||
Container::operator=(std::move(static_cast<Container&>(other)));
|
Container::operator=(std::move(static_cast<Container&>(other)));
|
||||||
@@ -103,6 +115,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
|
||||||
template<class V, detail::enable_if_t<
|
template<class V, detail::enable_if_t<
|
||||||
detail::is_constructible<T, V>::value, int> = 0>
|
detail::is_constructible<T, V>::value, int> = 0>
|
||||||
std::pair<iterator, bool> emplace(const key_type& key, V && t)
|
std::pair<iterator, bool> emplace(const key_type& key, V && t)
|
||||||
@@ -116,6 +129,7 @@ public:
|
|||||||
return {std::prev(this->end()), true};
|
return {std::prev(this->end()), true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/emplace/
|
||||||
template<class KeyType, class V, detail::enable_if_t<
|
template<class KeyType, class V, detail::enable_if_t<
|
||||||
detail::conjunction<detail::is_usable_as_key_type<key_compare, key_type, KeyType>,
|
detail::conjunction<detail::is_usable_as_key_type<key_compare, key_type, KeyType>,
|
||||||
detail::is_constructible<T, V>>::value, int> = 0>
|
detail::is_constructible<T, V>>::value, int> = 0>
|
||||||
@@ -130,11 +144,13 @@ public:
|
|||||||
return {std::prev(this->end()), true};
|
return {std::prev(this->end()), true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
|
||||||
T& operator[](const key_type& key)
|
T& operator[](const key_type& key)
|
||||||
{
|
{
|
||||||
return emplace(key, T{}).first->second;
|
return emplace(key, T{}).first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||||
T & operator[](KeyType && key)
|
T & operator[](KeyType && key)
|
||||||
@@ -142,11 +158,13 @@ public:
|
|||||||
return emplace(std::forward<KeyType>(key), T{}).first->second;
|
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
|
const T& operator[](const key_type& key) const
|
||||||
{
|
{
|
||||||
return at(key);
|
return at(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/operator[]/
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||||
const T & operator[](KeyType && key) const
|
const T & operator[](KeyType && key) const
|
||||||
@@ -154,6 +172,7 @@ public:
|
|||||||
return at(std::forward<KeyType>(key));
|
return at(std::forward<KeyType>(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/at/
|
||||||
T& at(const key_type& key)
|
T& at(const key_type& key)
|
||||||
{
|
{
|
||||||
const auto it = find_impl(*this, key);
|
const auto it = find_impl(*this, key);
|
||||||
@@ -164,6 +183,7 @@ public:
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/at/
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||||
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
@@ -176,6 +196,7 @@ public:
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/at/
|
||||||
const T& at(const key_type& key) const
|
const T& at(const key_type& key) const
|
||||||
{
|
{
|
||||||
const auto it = find_impl(*this, key);
|
const auto it = find_impl(*this, key);
|
||||||
@@ -186,6 +207,7 @@ public:
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/at/
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
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)
|
const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
@@ -198,6 +220,7 @@ public:
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
|
||||||
size_type erase(const key_type& key)
|
size_type erase(const key_type& key)
|
||||||
{
|
{
|
||||||
const auto it = find_impl(*this, key);
|
const auto it = find_impl(*this, key);
|
||||||
@@ -209,6 +232,7 @@ public:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||||
size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
@@ -222,11 +246,13 @@ public:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
|
||||||
iterator erase(iterator pos)
|
iterator erase(iterator pos)
|
||||||
{
|
{
|
||||||
return erase(pos, std::next(pos));
|
return erase(pos, std::next(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/erase/
|
||||||
iterator erase(iterator first, iterator last)
|
iterator erase(iterator first, iterator last)
|
||||||
{
|
{
|
||||||
if (first == last)
|
if (first == last)
|
||||||
@@ -280,11 +306,13 @@ public:
|
|||||||
return Container::begin() + offset;
|
return Container::begin() + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/count/
|
||||||
size_type count(const key_type& key) const
|
size_type count(const key_type& key) const
|
||||||
{
|
{
|
||||||
return find_impl(*this, key) != this->end() ? 1 : 0;
|
return find_impl(*this, key) != this->end() ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/count/
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
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)
|
size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
@@ -292,11 +320,13 @@ public:
|
|||||||
return find_impl(*this, key) != this->end() ? 1 : 0;
|
return find_impl(*this, key) != this->end() ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/find/
|
||||||
iterator find(const key_type& key)
|
iterator find(const key_type& key)
|
||||||
{
|
{
|
||||||
return find_impl(*this, key);
|
return find_impl(*this, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/find/
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||||
iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
@@ -304,11 +334,13 @@ public:
|
|||||||
return find_impl(*this, key);
|
return find_impl(*this, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/find/
|
||||||
const_iterator find(const key_type& key) const
|
const_iterator find(const key_type& key) const
|
||||||
{
|
{
|
||||||
return find_impl(*this, key);
|
return find_impl(*this, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/find/
|
||||||
template<class KeyType, detail::enable_if_t<
|
template<class KeyType, detail::enable_if_t<
|
||||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
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)
|
const_iterator find(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
@@ -316,11 +348,13 @@ public:
|
|||||||
return find_impl(*this, key);
|
return find_impl(*this, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
|
||||||
std::pair<iterator, bool> insert( value_type&& value )
|
std::pair<iterator, bool> insert( value_type&& value )
|
||||||
{
|
{
|
||||||
return emplace(value.first, std::move(value.second));
|
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 )
|
std::pair<iterator, bool> insert( const value_type& value )
|
||||||
{
|
{
|
||||||
const auto it = find_impl(*this, value.first);
|
const auto it = find_impl(*this, value.first);
|
||||||
@@ -336,6 +370,7 @@ public:
|
|||||||
using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
|
using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
|
||||||
std::input_iterator_tag>::value>::type;
|
std::input_iterator_tag>::value>::type;
|
||||||
|
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/insert/
|
||||||
template<typename InputIt, typename = require_input_iter<InputIt>>
|
template<typename InputIt, typename = require_input_iter<InputIt>>
|
||||||
void insert(InputIt first, InputIt last)
|
void insert(InputIt first, InputIt last)
|
||||||
{
|
{
|
||||||
|
|||||||
+491
-715
File diff suppressed because it is too large
Load Diff
@@ -1,599 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array> // array
|
|
||||||
#include <cstdint> // uint32_t, uint64_t
|
|
||||||
|
|
||||||
// Number tokens that are hard to round correctly, with the IEEE-754 binary64
|
|
||||||
// and binary32 bits of their correctly rounded values (ties to even; infinity
|
|
||||||
// for an overflow, a signed zero for an underflow).
|
|
||||||
//
|
|
||||||
// For doubles and floats around 0, the smallest normal number, 1, 2^24, 2^53,
|
|
||||||
// 0.1, and the largest finite number, and for random ones, the exact midpoint
|
|
||||||
// m to the next number gives: m, m with one unit more and less in the last
|
|
||||||
// digit, m with "01" and "0...01" appended, m with trailing zeros, and m cut
|
|
||||||
// after 17 to 30 digits (rounded down and up, so that the rounding is decided
|
|
||||||
// after the 19th digit), in fixed and exponent notation, 30% of them negative.
|
|
||||||
// Tokens longer than 80 characters are left out, except for four of 700 digits
|
|
||||||
// and more. Zeros, underflow, overflow, huge exponents, and integers beyond 64
|
|
||||||
// bits complete the set. Of the 508 tokens, 134 (as double) and 150 (as
|
|
||||||
// float) need the exact comparison with the midpoint (detail::digit_comparison()).
|
|
||||||
//
|
|
||||||
// The expected bits were computed with exact rational arithmetic in Python
|
|
||||||
// (fractions.Fraction) and cross-checked with Python's float(); strtod_l and
|
|
||||||
// strtof_l of Apple's libc and of glibc agree. Generated by
|
|
||||||
// compact_hard_cases.py 5 (with hard_cases.py), see the pull request that
|
|
||||||
// added this file.
|
|
||||||
|
|
||||||
namespace float_hard_cases
|
|
||||||
{
|
|
||||||
|
|
||||||
struct hard_case
|
|
||||||
{
|
|
||||||
const char* token;
|
|
||||||
std::uint64_t bits64;
|
|
||||||
std::uint32_t bits32;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline const std::array<hard_case, 508>& cases()
|
|
||||||
{
|
|
||||||
static const std::array<hard_case, 508> table =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
{"-2.4703282292062327e-324", 0x8000000000000000u, 0x80000000u},
|
|
||||||
{"24703282292062328e-340", 0x0000000000000001u, 0x00000000u},
|
|
||||||
{"247032822920623272e-341", 0x0000000000000000u, 0x00000000u},
|
|
||||||
{"-0.2470328229206232721e-323", 0x8000000000000001u, 0x80000000u},
|
|
||||||
{"-0.24703282292062327208e-323", 0x8000000000000000u, 0x80000000u},
|
|
||||||
{"-2.4703282292062327209e-324", 0x8000000000000001u, 0x80000000u},
|
|
||||||
{"2.47032822920623272088e-324", 0x0000000000000000u, 0x00000000u},
|
|
||||||
{"247032822920623272089e-344", 0x0000000000000001u, 0x00000000u},
|
|
||||||
{"-247032822920623272088284396434e-353", 0x8000000000000000u, 0x80000000u},
|
|
||||||
{"0.247032822920623272088284396435e-323", 0x0000000000000001u, 0x00000000u},
|
|
||||||
{"-74109846876186981e-340", 0x8000000000000001u, 0x80000000u},
|
|
||||||
{"0.74109846876186982e-323", 0x0000000000000002u, 0x00000000u},
|
|
||||||
{"-0.7410984687618698162e-323", 0x8000000000000001u, 0x80000000u},
|
|
||||||
{"-7.410984687618698163e-324", 0x8000000000000002u, 0x80000000u},
|
|
||||||
{"7.4109846876186981626e-324", 0x0000000000000001u, 0x00000000u},
|
|
||||||
{"-74109846876186981627e-343", 0x8000000000000002u, 0x80000000u},
|
|
||||||
{"-741098468761869816264e-344", 0x8000000000000001u, 0x80000000u},
|
|
||||||
{"0.741098468761869816265e-323", 0x0000000000000002u, 0x00000000u},
|
|
||||||
{"0.741098468761869816264853189302e-323", 0x0000000000000001u, 0x00000000u},
|
|
||||||
{"-7.41098468761869816264853189303e-324", 0x8000000000000002u, 0x80000000u},
|
|
||||||
{"0.22250738585072006e-307", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
|
||||||
{"2.2250738585072007e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
|
||||||
{"2.225073858507200641e-308", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
|
||||||
{"-2225073858507200642e-326", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
|
||||||
{"22250738585072006419e-327", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
|
||||||
{"0.2225073858507200642e-307", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
|
||||||
{"0.222507385850720064199e-307", 0x000FFFFFFFFFFFFEu, 0x00000000u},
|
|
||||||
{"2.225073858507200642e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
|
||||||
{"-2.22507385850720064199176395546e-308", 0x800FFFFFFFFFFFFEu, 0x80000000u},
|
|
||||||
{"222507385850720064199176395547e-337", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
|
||||||
{"-2.2250738585072011e-308", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
|
||||||
{"-22250738585072012e-324", 0x8010000000000000u, 0x80000000u},
|
|
||||||
{"-2225073858507201136e-326", 0x800FFFFFFFFFFFFFu, 0x80000000u},
|
|
||||||
{"0.2225073858507201137e-307", 0x0010000000000000u, 0x00000000u},
|
|
||||||
{"0.2225073858507201136e-307", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
|
||||||
{"-2.2250738585072011361e-308", 0x8010000000000000u, 0x80000000u},
|
|
||||||
{"2.22507385850720113605e-308", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
|
||||||
{"222507385850720113606e-328", 0x0010000000000000u, 0x00000000u},
|
|
||||||
{"22250738585072011360574097967e-336", 0x000FFFFFFFFFFFFFu, 0x00000000u},
|
|
||||||
{"0.222507385850720113605740979671e-307", 0x0010000000000000u, 0x00000000u},
|
|
||||||
{"22250738585072016e-324", 0x0010000000000000u, 0x00000000u},
|
|
||||||
{"0.22250738585072017e-307", 0x0010000000000001u, 0x00000000u},
|
|
||||||
{"0.222507385850720163e-307", 0x0010000000000000u, 0x00000000u},
|
|
||||||
{"2.225073858507201631e-308", 0x0010000000000001u, 0x00000000u},
|
|
||||||
{"-2.2250738585072016301e-308", 0x8010000000000000u, 0x80000000u},
|
|
||||||
{"22250738585072016302e-327", 0x0010000000000001u, 0x00000000u},
|
|
||||||
{"-222507385850720163012e-328", 0x8010000000000000u, 0x80000000u},
|
|
||||||
{"0.222507385850720163013e-307", 0x0010000000000001u, 0x00000000u},
|
|
||||||
{"0.222507385850720163012305563795e-307", 0x0010000000000000u, 0x00000000u},
|
|
||||||
{"-2.22507385850720163012305563796e-308", 0x8010000000000001u, 0x80000000u},
|
|
||||||
{"0.17976931348623156E+309", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
|
||||||
{"1.7976931348623157e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
|
||||||
{"1.797693134862315608e308", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
|
||||||
{"-1797693134862315609e290", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
|
||||||
{"-17976931348623156083e289", 0xFFEFFFFFFFFFFFFEu, 0xFF800000u},
|
|
||||||
{"-0.17976931348623156084E+309", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
|
||||||
{"0.179769313486231560835E+309", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
|
||||||
{"-1.79769313486231560836e308", 0xFFEFFFFFFFFFFFFFu, 0xFF800000u},
|
|
||||||
{"1.79769313486231560835325876058e308", 0x7FEFFFFFFFFFFFFEu, 0x7F800000u},
|
|
||||||
{"179769313486231560835325876059e279", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
|
||||||
{"1.7976931348623158e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
|
||||||
{"17976931348623159e292", 0x7FF0000000000000u, 0x7F800000u},
|
|
||||||
{"1797693134862315807e290", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
|
||||||
{"0.1797693134862315808E+309", 0x7FF0000000000000u, 0x7F800000u},
|
|
||||||
{"0.17976931348623158079E+309", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
|
||||||
{"-1.797693134862315808e308", 0xFFF0000000000000u, 0xFF800000u},
|
|
||||||
{"1.79769313486231580793e308", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
|
||||||
{"179769313486231580794e288", 0x7FF0000000000000u, 0x7F800000u},
|
|
||||||
{"179769313486231580793728971405e279", 0x7FEFFFFFFFFFFFFFu, 0x7F800000u},
|
|
||||||
{"-0.179769313486231580793728971406E+309", 0xFFF0000000000000u, 0xFF800000u},
|
|
||||||
{"100000000000000011102230246251565404236316680908203125e-53", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"-1.00000000000000011102230246251565404236316680908203126", 0xBFF0000000000001u, 0xBF800000u},
|
|
||||||
{"1.00000000000000011102230246251565404236316680908203124e0", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"10000000000000001110223024625156540423631668090820312501e-55", 0x3FF0000000000001u, 0x3F800000u},
|
|
||||||
{"1.00000000000000011102230246251565404236316680908203125000000000000000000001", 0x3FF0000000000001u, 0x3F800000u},
|
|
||||||
{"10000000000000001e-16", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"1.0000000000000002", 0x3FF0000000000001u, 0x3F800000u},
|
|
||||||
{"1.000000000000000111", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"1.000000000000000112e0", 0x3FF0000000000001u, 0x3F800000u},
|
|
||||||
{"1.000000000000000111e0", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"-10000000000000001111e-19", 0xBFF0000000000001u, 0xBF800000u},
|
|
||||||
{"-100000000000000011102e-20", 0xBFF0000000000000u, 0xBF800000u},
|
|
||||||
{"-1.00000000000000011103", 0xBFF0000000000001u, 0xBF800000u},
|
|
||||||
{"1.00000000000000011102230246251", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"1.00000000000000011102230246252e0", 0x3FF0000000000001u, 0x3F800000u},
|
|
||||||
{"-0.999999999999999944488848768742172978818416595458984375", 0xBFF0000000000000u, 0xBF800000u},
|
|
||||||
{"-9.99999999999999944488848768742172978818416595458984376e-1", 0xBFF0000000000000u, 0xBF800000u},
|
|
||||||
{"999999999999999944488848768742172978818416595458984374e-54", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
|
||||||
{"0.99999999999999994448884876874217297881841659545898437501", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"9.99999999999999944488848768742172978818416595458984375000000000000000000001e-1", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"-0.99999999999999994", 0xBFEFFFFFFFFFFFFFu, 0xBF800000u},
|
|
||||||
{"9.9999999999999995e-1", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"9.999999999999999444e-1", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
|
||||||
{"9999999999999999445e-19", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"99999999999999994448e-20", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
|
||||||
{"-0.99999999999999994449", 0xBFF0000000000000u, 0xBF800000u},
|
|
||||||
{"0.999999999999999944488", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
|
||||||
{"-9.99999999999999944489e-1", 0xBFF0000000000000u, 0xBF800000u},
|
|
||||||
{"9.99999999999999944488848768742e-1", 0x3FEFFFFFFFFFFFFFu, 0x3F800000u},
|
|
||||||
{"999999999999999944488848768743e-30", 0x3FF0000000000000u, 0x3F800000u},
|
|
||||||
{"-9.007199254740993e15", 0xC340000000000000u, 0xDA000000u},
|
|
||||||
{"9007199254740994e0", 0x4340000000000001u, 0x5A000000u},
|
|
||||||
{"9007199254740992", 0x4340000000000000u, 0x5A000000u},
|
|
||||||
{"9.00719925474099301e15", 0x4340000000000001u, 0x5A000000u},
|
|
||||||
{"9007199254740993000000000000000000001e-21", 0x4340000000000001u, 0x5A000000u},
|
|
||||||
{"-9007199254740993.000000000000000000000000000000", 0xC340000000000000u, 0xDA000000u},
|
|
||||||
{"90071992547409915e-1", 0x4340000000000000u, 0x5A000000u},
|
|
||||||
{"-9007199254740991.6", 0xC340000000000000u, 0xDA000000u},
|
|
||||||
{"9.0071992547409914e15", 0x433FFFFFFFFFFFFFu, 0x5A000000u},
|
|
||||||
{"-9007199254740991501e-3", 0xC340000000000000u, 0xDA000000u},
|
|
||||||
{"9007199254740991.5000000000000000000001", 0x4340000000000000u, 0x5A000000u},
|
|
||||||
{"9.0071992547409915000000000000000000000000000000e15", 0x4340000000000000u, 0x5A000000u},
|
|
||||||
{"0.100000000000000012490009027033011079765856266021728515625", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
|
||||||
{"1.00000000000000012490009027033011079765856266021728515626e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
|
||||||
{"100000000000000012490009027033011079765856266021728515624e-57", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
|
||||||
{"0.10000000000000001249000902703301107976585626602172851562501", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
|
||||||
{"0.10000000000000001", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
|
||||||
{"1.0000000000000002e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
|
||||||
{"-1.000000000000000124e-1", 0xBFB999999999999Au, 0xBDCCCCCDu},
|
|
||||||
{"1000000000000000125e-19", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
|
||||||
{"-10000000000000001249e-20", 0xBFB999999999999Au, 0xBDCCCCCDu},
|
|
||||||
{"0.1000000000000000125", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
|
||||||
{"0.10000000000000001249", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
|
||||||
{"1.00000000000000012491e-1", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
|
||||||
{"1.00000000000000012490009027033e-1", 0x3FB999999999999Au, 0x3DCCCCCDu},
|
|
||||||
{"100000000000000012490009027034e-30", 0x3FB999999999999Bu, 0x3DCCCCCDu},
|
|
||||||
{"2.45134755833537796875e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
|
||||||
{"-245134755833537796876e-6", 0xC2EBDE5C4164D83Au, 0xD75EF2E2u},
|
|
||||||
{"-245134755833537.796874", 0xC2EBDE5C4164D839u, 0xD75EF2E2u},
|
|
||||||
{"2.4513475583353779687501e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
|
||||||
{"245134755833537796875000000000000000000001e-27", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
|
||||||
{"245134755833537.796875000000000000000000000000000000", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
|
||||||
{"2.4513475583353779e14", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
|
||||||
{"2451347558335378e-1", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
|
||||||
{"2451347558335377968e-4", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
|
||||||
{"245134755833537.7969", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
|
||||||
{"245134755833537.79687", 0x42EBDE5C4164D839u, 0x575EF2E2u},
|
|
||||||
{"2.4513475583353779688e14", 0x42EBDE5C4164D83Au, 0x575EF2E2u},
|
|
||||||
{"181510327827821147441864013671875e-23", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
|
||||||
{"-1815103278.27821147441864013671876", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
|
||||||
{"1.81510327827821147441864013671874e9", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
|
||||||
{"18151032782782114744186401367187501e-25", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
|
||||||
{"1815103278.27821147441864013671875000000000000000000001", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
|
||||||
{"-1.81510327827821147441864013671875000000000000000000000000000000e9", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
|
||||||
{"18151032782782114e-7", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
|
||||||
{"1815103278.2782115", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
|
||||||
{"1815103278.278211474", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
|
||||||
{"-1.815103278278211475e9", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
|
||||||
{"1.8151032782782114744e9", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
|
||||||
{"-18151032782782114745e-10", 0xC1DB0C11CB91CE38u, 0xCED8608Eu},
|
|
||||||
{"181510327827821147441e-11", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
|
||||||
{"1815103278.27821147442", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
|
||||||
{"1815103278.27821147441864013671", 0x41DB0C11CB91CE37u, 0x4ED8608Eu},
|
|
||||||
{"1.81510327827821147441864013672e9", 0x41DB0C11CB91CE38u, 0x4ED8608Eu},
|
|
||||||
{"3809325632181785344", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
|
||||||
{"3.809325632181785345e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
|
||||||
{"3809325632181785343e0", 0x43CA6EB8BD69FE29u, 0x5E5375C6u},
|
|
||||||
{"3809325632181785344.01", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
|
||||||
{"3.809325632181785344000000000000000000001e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
|
||||||
{"3809325632181785344000000000000000000000000000000e-30", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
|
||||||
{"3809325632181785300", 0x43CA6EB8BD69FE29u, 0x5E5375C6u},
|
|
||||||
{"3.8093256321817854e18", 0x43CA6EB8BD69FE2Au, 0x5E5375C6u},
|
|
||||||
{"4.046966549916366943359375e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
|
||||||
{"4046966549916366943359376e-12", 0x428D7210076CE2F0u, 0x546B9080u},
|
|
||||||
{"4046966549916.366943359374", 0x428D7210076CE2EFu, 0x546B9080u},
|
|
||||||
{"4.04696654991636694335937501e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
|
||||||
{"4046966549916366943359375000000000000000000001e-33", 0x428D7210076CE2F0u, 0x546B9080u},
|
|
||||||
{"-4046966549916.366943359375000000000000000000000000000000", 0xC28D7210076CE2F0u, 0xD46B9080u},
|
|
||||||
{"4.0469665499163669e12", 0x428D7210076CE2EFu, 0x546B9080u},
|
|
||||||
{"4046966549916367e-3", 0x428D7210076CE2F0u, 0x546B9080u},
|
|
||||||
{"4046966549916366943e-6", 0x428D7210076CE2EFu, 0x546B9080u},
|
|
||||||
{"4046966549916.366944", 0x428D7210076CE2F0u, 0x546B9080u},
|
|
||||||
{"4046966549916.3669433", 0x428D7210076CE2EFu, 0x546B9080u},
|
|
||||||
{"4.0469665499163669434e12", 0x428D7210076CE2F0u, 0x546B9080u},
|
|
||||||
{"-4.04696654991636694335e12", 0xC28D7210076CE2EFu, 0xD46B9080u},
|
|
||||||
{"404696654991636694336e-8", 0x428D7210076CE2F0u, 0x546B9080u},
|
|
||||||
{"28093802557000874e154", 0x63529C3B77330BDBu, 0x7F800000u},
|
|
||||||
{"0.28093802557000875E+171", 0x63529C3B77330BDCu, 0x7F800000u},
|
|
||||||
{"0.2809380255700087447E+171", 0x63529C3B77330BDBu, 0x7F800000u},
|
|
||||||
{"2.809380255700087448e170", 0x63529C3B77330BDCu, 0x7F800000u},
|
|
||||||
{"2.8093802557000874472e170", 0x63529C3B77330BDBu, 0x7F800000u},
|
|
||||||
{"28093802557000874473e151", 0x63529C3B77330BDCu, 0x7F800000u},
|
|
||||||
{"280938025570008744728e150", 0x63529C3B77330BDBu, 0x7F800000u},
|
|
||||||
{"-0.280938025570008744729E+171", 0xE3529C3B77330BDCu, 0xFF800000u},
|
|
||||||
{"-0.280938025570008744728403667979E+171", 0xE3529C3B77330BDBu, 0xFF800000u},
|
|
||||||
{"2.8093802557000874472840366798e170", 0x63529C3B77330BDCu, 0x7F800000u},
|
|
||||||
{"0.39523280297734525e-154", 0x1FE0F51BF17FD374u, 0x00000000u},
|
|
||||||
{"-3.9523280297734526e-155", 0x9FE0F51BF17FD375u, 0x80000000u},
|
|
||||||
{"3.952328029773452547e-155", 0x1FE0F51BF17FD374u, 0x00000000u},
|
|
||||||
{"3952328029773452548e-173", 0x1FE0F51BF17FD375u, 0x00000000u},
|
|
||||||
{"-39523280297734525478e-174", 0x9FE0F51BF17FD374u, 0x80000000u},
|
|
||||||
{"0.39523280297734525479e-154", 0x1FE0F51BF17FD375u, 0x00000000u},
|
|
||||||
{"-0.395232802977345254787e-154", 0x9FE0F51BF17FD374u, 0x80000000u},
|
|
||||||
{"-3.95232802977345254788e-155", 0x9FE0F51BF17FD375u, 0x80000000u},
|
|
||||||
{"-3.95232802977345254787245825501e-155", 0x9FE0F51BF17FD374u, 0x80000000u},
|
|
||||||
{"395232802977345254787245825502e-184", 0x1FE0F51BF17FD375u, 0x00000000u},
|
|
||||||
{"-1.0790205420931879e-276", 0x86A3209CA6233255u, 0x80000000u},
|
|
||||||
{"-1079020542093188e-291", 0x86A3209CA6233256u, 0x80000000u},
|
|
||||||
{"-1079020542093187947e-294", 0x86A3209CA6233255u, 0x80000000u},
|
|
||||||
{"0.1079020542093187948e-275", 0x06A3209CA6233256u, 0x00000000u},
|
|
||||||
{"0.1079020542093187947e-275", 0x06A3209CA6233255u, 0x00000000u},
|
|
||||||
{"1.0790205420931879471e-276", 0x06A3209CA6233256u, 0x00000000u},
|
|
||||||
{"1.07902054209318794701e-276", 0x06A3209CA6233255u, 0x00000000u},
|
|
||||||
{"-107902054209318794702e-296", 0x86A3209CA6233256u, 0x80000000u},
|
|
||||||
{"107902054209318794701153285302e-305", 0x06A3209CA6233255u, 0x00000000u},
|
|
||||||
{"-0.107902054209318794701153285303e-275", 0x86A3209CA6233256u, 0x80000000u},
|
|
||||||
{"58530471071351308e-228", 0x1413B446E6A16A3Bu, 0x00000000u},
|
|
||||||
{"-0.58530471071351309e-211", 0x9413B446E6A16A3Cu, 0x80000000u},
|
|
||||||
{"0.5853047107135130893e-211", 0x1413B446E6A16A3Bu, 0x00000000u},
|
|
||||||
{"-5.853047107135130894e-212", 0x9413B446E6A16A3Cu, 0x80000000u},
|
|
||||||
{"5.853047107135130893e-212", 0x1413B446E6A16A3Bu, 0x00000000u},
|
|
||||||
{"58530471071351308931e-231", 0x1413B446E6A16A3Cu, 0x00000000u},
|
|
||||||
{"-585304710713513089304e-232", 0x9413B446E6A16A3Bu, 0x80000000u},
|
|
||||||
{"0.585304710713513089305e-211", 0x1413B446E6A16A3Cu, 0x00000000u},
|
|
||||||
{"-0.585304710713513089304248824438e-211", 0x9413B446E6A16A3Bu, 0x80000000u},
|
|
||||||
{"5.85304710713513089304248824439e-212", 0x1413B446E6A16A3Cu, 0x00000000u},
|
|
||||||
{"0.19334214893983531e-78", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
|
||||||
{"1.9334214893983532e-79", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
|
||||||
{"1.933421489398353102e-79", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
|
||||||
{"1933421489398353103e-97", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
|
||||||
{"19334214893983531023e-98", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
|
||||||
{"0.19334214893983531024e-78", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
|
||||||
{"0.193342148939835310231e-78", 0x2F96ECBF1CFB10F6u, 0x00000000u},
|
|
||||||
{"1.93342148939835310232e-79", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
|
||||||
{"-1.93342148939835310231359014704e-79", 0xAF96ECBF1CFB10F6u, 0x80000000u},
|
|
||||||
{"193342148939835310231359014705e-108", 0x2F96ECBF1CFB10F7u, 0x00000000u},
|
|
||||||
{"2.9873358928024455e227", 0x6F2938807814E8A2u, 0x7F800000u},
|
|
||||||
{"29873358928024456e211", 0x6F2938807814E8A3u, 0x7F800000u},
|
|
||||||
{"298733589280244551e210", 0x6F2938807814E8A2u, 0x7F800000u},
|
|
||||||
{"0.2987335892802445511E+228", 0x6F2938807814E8A3u, 0x7F800000u},
|
|
||||||
{"0.29873358928024455109E+228", 0x6F2938807814E8A2u, 0x7F800000u},
|
|
||||||
{"2.987335892802445511e227", 0x6F2938807814E8A3u, 0x7F800000u},
|
|
||||||
{"-2.98733589280244551098e227", 0xEF2938807814E8A2u, 0xFF800000u},
|
|
||||||
{"-298733589280244551099e207", 0xEF2938807814E8A3u, 0xFF800000u},
|
|
||||||
{"298733589280244551098081559931e198", 0x6F2938807814E8A2u, 0x7F800000u},
|
|
||||||
{"0.298733589280244551098081559932E+228", 0x6F2938807814E8A3u, 0x7F800000u},
|
|
||||||
{"7.0064923216240853e-46", 0x3690000000000000u, 0x00000000u},
|
|
||||||
{"70064923216240854e-62", 0x3690000000000000u, 0x00000001u},
|
|
||||||
{"7006492321624085354e-64", 0x3690000000000000u, 0x00000000u},
|
|
||||||
{"-0.7006492321624085355e-45", 0xB690000000000000u, 0x80000001u},
|
|
||||||
{"0.70064923216240853546e-45", 0x3690000000000000u, 0x00000000u},
|
|
||||||
{"7.0064923216240853547e-46", 0x3690000000000000u, 0x00000001u},
|
|
||||||
{"7.00649232162408535461e-46", 0x3690000000000000u, 0x00000000u},
|
|
||||||
{"-700649232162408535462e-66", 0xB690000000000000u, 0x80000001u},
|
|
||||||
{"-700649232162408535461864791644e-75", 0xB690000000000000u, 0x80000000u},
|
|
||||||
{"0.700649232162408535461864791645e-45", 0x3690000000000000u, 0x00000001u},
|
|
||||||
{"21019476964872256e-61", 0x36A8000000000000u, 0x00000001u},
|
|
||||||
{"0.21019476964872257e-44", 0x36A8000000000000u, 0x00000002u},
|
|
||||||
{"-0.2101947696487225606e-44", 0xB6A8000000000000u, 0x80000001u},
|
|
||||||
{"-2.101947696487225607e-45", 0xB6A8000000000000u, 0x80000002u},
|
|
||||||
{"2.1019476964872256063e-45", 0x36A8000000000000u, 0x00000001u},
|
|
||||||
{"21019476964872256064e-64", 0x36A8000000000000u, 0x00000002u},
|
|
||||||
{"-210194769648722560638e-65", 0xB6A8000000000000u, 0x80000001u},
|
|
||||||
{"-0.210194769648722560639e-44", 0xB6A8000000000000u, 0x80000002u},
|
|
||||||
{"-0.210194769648722560638559437493e-44", 0xB6A8000000000000u, 0x80000001u},
|
|
||||||
{"2.10194769648722560638559437494e-45", 0x36A8000000000000u, 0x00000002u},
|
|
||||||
{"0.11754941406275178e-37", 0x380FFFFFA0000000u, 0x007FFFFEu},
|
|
||||||
{"1.1754941406275179e-38", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
|
||||||
{"-1.175494140627517859e-38", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
|
||||||
{"117549414062751786e-55", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
|
||||||
{"-11754941406275178592e-57", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
|
||||||
{"0.11754941406275178593e-37", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
|
||||||
{"0.117549414062751785924e-37", 0x380FFFFFA0000000u, 0x007FFFFEu},
|
|
||||||
{"1.17549414062751785925e-38", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
|
||||||
{"-1.17549414062751785924617589866e-38", 0xB80FFFFFA0000000u, 0x807FFFFEu},
|
|
||||||
{"117549414062751785924617589867e-67", 0x380FFFFFA0000000u, 0x007FFFFFu},
|
|
||||||
{"1.1754942807573642e-38", 0x380FFFFFDFFFFFFFu, 0x007FFFFFu},
|
|
||||||
{"11754942807573643e-54", 0x380FFFFFE0000000u, 0x00800000u},
|
|
||||||
{"1175494280757364291e-56", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
|
||||||
{"0.1175494280757364292e-37", 0x380FFFFFE0000000u, 0x00800000u},
|
|
||||||
{"0.11754942807573642917e-37", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
|
||||||
{"-1.1754942807573642918e-38", 0xB80FFFFFE0000000u, 0x80800000u},
|
|
||||||
{"-1.17549428075736429172e-38", 0xB80FFFFFE0000000u, 0x807FFFFFu},
|
|
||||||
{"-117549428075736429173e-58", 0xB80FFFFFE0000000u, 0x80800000u},
|
|
||||||
{"117549428075736429172788299103e-67", 0x380FFFFFE0000000u, 0x007FFFFFu},
|
|
||||||
{"0.117549428075736429172788299104e-37", 0x380FFFFFE0000000u, 0x00800000u},
|
|
||||||
{"11754944208872107e-54", 0x3810000010000000u, 0x00800000u},
|
|
||||||
{"-0.11754944208872108e-37", 0xB810000010000000u, 0x80800001u},
|
|
||||||
{"0.1175494420887210724e-37", 0x3810000010000000u, 0x00800000u},
|
|
||||||
{"1.175494420887210725e-38", 0x3810000010000000u, 0x00800001u},
|
|
||||||
{"1.1754944208872107242e-38", 0x3810000010000000u, 0x00800000u},
|
|
||||||
{"-11754944208872107243e-57", 0xB810000010000000u, 0x80800001u},
|
|
||||||
{"-11754944208872107242e-57", 0xB810000010000000u, 0x80800000u},
|
|
||||||
{"0.117549442088721072421e-37", 0x3810000010000000u, 0x00800001u},
|
|
||||||
{"0.11754944208872107242095900834e-37", 0x3810000010000000u, 0x00800000u},
|
|
||||||
{"-1.17549442088721072420959008341e-38", 0xB810000010000000u, 0x80800001u},
|
|
||||||
{"340282336497324057985868971510891282432", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
|
||||||
{"-3.40282336497324057985868971510891282433e38", 0xC7EFFFFFD0000000u, 0xFF7FFFFFu},
|
|
||||||
{"340282336497324057985868971510891282431e0", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
|
||||||
{"340282336497324057985868971510891282432.01", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
|
||||||
{"3.40282336497324057985868971510891282432000000000000000000001e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
|
||||||
{"340282336497324057985868971510891282432000000000000000000000000000000e-30", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
|
||||||
{"340282336497324050000000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
|
||||||
{"3.4028233649732406e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
|
||||||
{"3.402823364973240579e38", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
|
||||||
{"340282336497324058e21", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
|
||||||
{"34028233649732405798e19", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
|
||||||
{"340282336497324057990000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
|
||||||
{"340282336497324057985000000000000000000", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
|
||||||
{"3.40282336497324057986e38", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
|
||||||
{"3.4028233649732405798586897151e38", 0x47EFFFFFD0000000u, 0x7F7FFFFEu},
|
|
||||||
{"340282336497324057985868971511e9", 0x47EFFFFFD0000000u, 0x7F7FFFFFu},
|
|
||||||
{"3.40282356779733661637539395458142568448e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
|
||||||
{"-340282356779733661637539395458142568449e0", 0xC7EFFFFFF0000000u, 0xFF800000u},
|
|
||||||
{"340282356779733661637539395458142568447", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
|
||||||
{"3.4028235677973366163753939545814256844801e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
|
||||||
{"340282356779733661637539395458142568448000000000000000000001e-21", 0x47EFFFFFF0000000u, 0x7F800000u},
|
|
||||||
{"340282356779733661637539395458142568448.000000000000000000000000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
|
||||||
{"3.4028235677973366e38", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
|
||||||
{"-34028235677973367e22", 0xC7EFFFFFF0000000u, 0xFF800000u},
|
|
||||||
{"3402823567797336616e20", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
|
||||||
{"340282356779733661700000000000000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
|
||||||
{"340282356779733661630000000000000000000", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
|
||||||
{"3.4028235677973366164e38", 0x47EFFFFFF0000000u, 0x7F800000u},
|
|
||||||
{"3.40282356779733661637e38", 0x47EFFFFFF0000000u, 0x7F7FFFFFu},
|
|
||||||
{"340282356779733661638e18", 0x47EFFFFFF0000000u, 0x7F800000u},
|
|
||||||
{"-340282356779733661637539395458e9", 0xC7EFFFFFF0000000u, 0xFF7FFFFFu},
|
|
||||||
{"340282356779733661637539395459000000000", 0x47EFFFFFF0000000u, 0x7F800000u},
|
|
||||||
{"-1000000059604644775390625e-24", 0xBFF0000010000000u, 0xBF800000u},
|
|
||||||
{"-1.000000059604644775390626", 0xBFF0000010000000u, 0xBF800001u},
|
|
||||||
{"-1.000000059604644775390624e0", 0xBFF0000010000000u, 0xBF800000u},
|
|
||||||
{"100000005960464477539062501e-26", 0x3FF0000010000000u, 0x3F800001u},
|
|
||||||
{"1.000000059604644775390625000000000000000000001", 0x3FF0000010000000u, 0x3F800001u},
|
|
||||||
{"1.000000059604644775390625000000000000000000000000000000e0", 0x3FF0000010000000u, 0x3F800000u},
|
|
||||||
{"-10000000596046447e-16", 0xBFF0000010000000u, 0xBF800000u},
|
|
||||||
{"1.0000000596046448", 0x3FF0000010000000u, 0x3F800001u},
|
|
||||||
{"1.000000059604644775", 0x3FF0000010000000u, 0x3F800000u},
|
|
||||||
{"-1.000000059604644776e0", 0xBFF0000010000000u, 0xBF800001u},
|
|
||||||
{"-1.0000000596046447753e0", 0xBFF0000010000000u, 0xBF800000u},
|
|
||||||
{"10000000596046447754e-19", 0x3FF0000010000000u, 0x3F800001u},
|
|
||||||
{"100000005960464477539e-20", 0x3FF0000010000000u, 0x3F800000u},
|
|
||||||
{"-1.0000000596046447754", 0xBFF0000010000000u, 0xBF800001u},
|
|
||||||
{"0.9999999701976776123046875", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
|
||||||
{"9.999999701976776123046876e-1", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
|
||||||
{"9999999701976776123046874e-25", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
|
||||||
{"0.999999970197677612304687501", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
|
||||||
{"-9.999999701976776123046875000000000000000000001e-1", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
|
||||||
{"-9999999701976776123046875000000000000000000000000000000e-55", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
|
||||||
{"0.99999997019767761", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
|
||||||
{"-9.9999997019767762e-1", 0xBFEFFFFFF0000000u, 0xBF800000u},
|
|
||||||
{"-9.999999701976776123e-1", 0xBFEFFFFFF0000000u, 0xBF7FFFFFu},
|
|
||||||
{"9999999701976776124e-19", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
|
||||||
{"9999999701976776123e-19", 0x3FEFFFFFF0000000u, 0x3F7FFFFFu},
|
|
||||||
{"0.99999997019767761231", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
|
||||||
{"-0.999999970197677612304", 0xBFEFFFFFF0000000u, 0xBF7FFFFFu},
|
|
||||||
{"9.99999970197677612305e-1", 0x3FEFFFFFF0000000u, 0x3F800000u},
|
|
||||||
{"-1.6777217e7", 0xC170000010000000u, 0xCB800000u},
|
|
||||||
{"16777218e0", 0x4170000020000000u, 0x4B800001u},
|
|
||||||
{"16777216", 0x4170000000000000u, 0x4B800000u},
|
|
||||||
{"-1.677721701e7", 0xC17000001028F5C3u, 0xCB800001u},
|
|
||||||
{"-16777217000000000000000000001e-21", 0xC170000010000000u, 0xCB800001u},
|
|
||||||
{"16777217.000000000000000000000000000000", 0x4170000010000000u, 0x4B800000u},
|
|
||||||
{"167772155e-1", 0x416FFFFFF0000000u, 0x4B800000u},
|
|
||||||
{"16777215.6", 0x416FFFFFF3333333u, 0x4B800000u},
|
|
||||||
{"1.67772154e7", 0x416FFFFFECCCCCCDu, 0x4B7FFFFFu},
|
|
||||||
{"16777215501e-3", 0x416FFFFFF0083127u, 0x4B800000u},
|
|
||||||
{"-16777215.5000000000000000000001", 0xC16FFFFFF0000000u, 0xCB800000u},
|
|
||||||
{"-1.67772155000000000000000000000000000000e7", 0xC16FFFFFF0000000u, 0xCB800000u},
|
|
||||||
{"0.1000000052154064178466796875", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
|
||||||
{"1.000000052154064178466796876e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
|
||||||
{"-1000000052154064178466796874e-28", 0xBFB99999B0000000u, 0xBDCCCCCDu},
|
|
||||||
{"0.100000005215406417846679687501", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
|
||||||
{"1.000000052154064178466796875000000000000000000001e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
|
||||||
{"-1000000052154064178466796875000000000000000000000000000000e-58", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
|
||||||
{"-0.10000000521540641", 0xBFB99999AFFFFFFFu, 0xBDCCCCCDu},
|
|
||||||
{"-1.0000000521540642e-1", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
|
||||||
{"1.000000052154064178e-1", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
|
||||||
{"-1000000052154064179e-19", 0xBFB99999B0000000u, 0xBDCCCCCEu},
|
|
||||||
{"10000000521540641784e-20", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
|
||||||
{"0.10000000521540641785", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
|
||||||
{"0.100000005215406417846", 0x3FB99999B0000000u, 0x3DCCCCCDu},
|
|
||||||
{"1.00000005215406417847e-1", 0x3FB99999B0000000u, 0x3DCCCCCEu},
|
|
||||||
{"5.429001220703125e3", 0x40B5350050000000u, 0x45A9A802u},
|
|
||||||
{"-5429001220703126e-12", 0xC0B5350050000001u, 0xC5A9A803u},
|
|
||||||
{"-5429.001220703124", 0xC0B535004FFFFFFFu, 0xC5A9A802u},
|
|
||||||
{"5.42900122070312501e3", 0x40B5350050000000u, 0x45A9A803u},
|
|
||||||
{"5429001220703125000000000000000000001e-33", 0x40B5350050000000u, 0x45A9A803u},
|
|
||||||
{"-5429.001220703125000000000000000000000000000000", 0xC0B5350050000000u, 0xC5A9A802u},
|
|
||||||
{"503719056e0", 0x41BE062490000000u, 0x4DF03124u},
|
|
||||||
{"503719057", 0x41BE062491000000u, 0x4DF03125u},
|
|
||||||
{"5.03719055e8", 0x41BE06248F000000u, 0x4DF03124u},
|
|
||||||
{"50371905601e-2", 0x41BE062490028F5Cu, 0x4DF03125u},
|
|
||||||
{"503719056.000000000000000000001", 0x41BE062490000000u, 0x4DF03125u},
|
|
||||||
{"5.03719056000000000000000000000000000000e8", 0x41BE062490000000u, 0x4DF03124u},
|
|
||||||
{"-92331620", 0xC196037990000000u, 0xCCB01BCCu},
|
|
||||||
{"9.233163e7", 0x41960379B8000000u, 0x4CB01BCEu},
|
|
||||||
{"9233161e1", 0x4196037968000000u, 0x4CB01BCBu},
|
|
||||||
{"92331620.1", 0x4196037990666666u, 0x4CB01BCDu},
|
|
||||||
{"9.233162000000000000000000001e7", 0x4196037990000000u, 0x4CB01BCDu},
|
|
||||||
{"9233162000000000000000000000000000000e-29", 0x4196037990000000u, 0x4CB01BCCu},
|
|
||||||
{"3.002458625e6", 0x4146E82D50000000u, 0x4A37416Au},
|
|
||||||
{"3002458626e-3", 0x4146E82D5020C49Cu, 0x4A37416Bu},
|
|
||||||
{"3002458.624", 0x4146E82D4FDF3B64u, 0x4A37416Au},
|
|
||||||
{"-3.00245862501e6", 0xC146E82D500053E3u, 0xCA37416Bu},
|
|
||||||
{"3002458625000000000000000000001e-24", 0x4146E82D50000000u, 0x4A37416Bu},
|
|
||||||
{"3002458.625000000000000000000000000000000", 0x4146E82D50000000u, 0x4A37416Au},
|
|
||||||
{"-1095485584696182596504479582065262592e1", 0xC7A07BA830000000u, 0xFD03DD42u},
|
|
||||||
{"10954855846961825965044795820652625930", 0x47A07BA830000000u, 0x7D03DD42u},
|
|
||||||
{"1.095485584696182596504479582065262591e37", 0x47A07BA830000000u, 0x7D03DD41u},
|
|
||||||
{"109548558469618259650447958206526259201e-1", 0x47A07BA830000000u, 0x7D03DD42u},
|
|
||||||
{"-10954855846961825965044795820652625920.00000000000000000001", 0xC7A07BA830000000u, 0xFD03DD42u},
|
|
||||||
{"1.095485584696182596504479582065262592000000000000000000000000000000e37", 0x47A07BA830000000u, 0x7D03DD42u},
|
|
||||||
{"10954855846961825e21", 0x47A07BA830000000u, 0x7D03DD41u},
|
|
||||||
{"10954855846961826000000000000000000000", 0x47A07BA830000000u, 0x7D03DD42u},
|
|
||||||
{"10954855846961825960000000000000000000", 0x47A07BA830000000u, 0x7D03DD41u},
|
|
||||||
{"1.095485584696182597e37", 0x47A07BA830000000u, 0x7D03DD42u},
|
|
||||||
{"1.0954855846961825965e37", 0x47A07BA830000000u, 0x7D03DD41u},
|
|
||||||
{"-10954855846961825966e18", 0xC7A07BA830000000u, 0xFD03DD42u},
|
|
||||||
{"10954855846961825965e18", 0x47A07BA830000000u, 0x7D03DD41u},
|
|
||||||
{"10954855846961825965100000000000000000", 0x47A07BA830000000u, 0x7D03DD42u},
|
|
||||||
{"10954855846961825965044795820600000000", 0x47A07BA830000000u, 0x7D03DD41u},
|
|
||||||
{"-1.09548558469618259650447958207e37", 0xC7A07BA830000000u, 0xFD03DD42u},
|
|
||||||
{"1.6449216019182103706535606608388384863861375606575165875256061553955078126e-21", 0x3B9F125A50000000u, 0x1CF892D3u},
|
|
||||||
{"-16449216019182103706535606608388384863861375606575165875256061553955078124e-94", 0xBB9F125A50000000u, 0x9CF892D2u},
|
|
||||||
{"-0.0000000000000000000016449216019182103", 0xBB9F125A50000000u, 0x9CF892D2u},
|
|
||||||
{"-1.6449216019182104e-21", 0xBB9F125A50000000u, 0x9CF892D3u},
|
|
||||||
{"-1.64492160191821037e-21", 0xBB9F125A50000000u, 0x9CF892D2u},
|
|
||||||
{"-1644921601918210371e-39", 0xBB9F125A50000000u, 0x9CF892D3u},
|
|
||||||
{"-16449216019182103706e-40", 0xBB9F125A50000000u, 0x9CF892D2u},
|
|
||||||
{"0.0000000000000000000016449216019182103707", 0x3B9F125A50000000u, 0x1CF892D3u},
|
|
||||||
{"0.00000000000000000000164492160191821037065", 0x3B9F125A50000000u, 0x1CF892D2u},
|
|
||||||
{"1.64492160191821037066e-21", 0x3B9F125A50000000u, 0x1CF892D3u},
|
|
||||||
{"1.64492160191821037065356066083e-21", 0x3B9F125A50000000u, 0x1CF892D2u},
|
|
||||||
{"164492160191821037065356066084e-50", 0x3B9F125A50000000u, 0x1CF892D3u},
|
|
||||||
{"6.565061509609222412109375e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
|
||||||
{"6565061509609222412109376e-25", 0x3FE5021930000000u, 0x3F2810CAu},
|
|
||||||
{"0.6565061509609222412109374", 0x3FE5021930000000u, 0x3F2810C9u},
|
|
||||||
{"6.56506150960922241210937501e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
|
||||||
{"6565061509609222412109375000000000000000000001e-46", 0x3FE5021930000000u, 0x3F2810CAu},
|
|
||||||
{"-0.6565061509609222412109375000000000000000000000000000000", 0xBFE5021930000000u, 0xBF2810CAu},
|
|
||||||
{"6.5650615096092224e-1", 0x3FE5021930000000u, 0x3F2810C9u},
|
|
||||||
{"-65650615096092225e-17", 0xBFE5021930000000u, 0xBF2810CAu},
|
|
||||||
{"6565061509609222412e-19", 0x3FE5021930000000u, 0x3F2810C9u},
|
|
||||||
{"0.6565061509609222413", 0x3FE5021930000000u, 0x3F2810CAu},
|
|
||||||
{"0.65650615096092224121", 0x3FE5021930000000u, 0x3F2810C9u},
|
|
||||||
{"6.5650615096092224122e-1", 0x3FE5021930000000u, 0x3F2810CAu},
|
|
||||||
{"-6.5650615096092224121e-1", 0xBFE5021930000000u, 0xBF2810C9u},
|
|
||||||
{"656506150960922241211e-21", 0x3FE5021930000000u, 0x3F2810CAu},
|
|
||||||
{"18014627239033005156980393746124491372029297053813934326171875e-77", 0x3CA9F63970000000u, 0x254FB1CCu},
|
|
||||||
{"0.00000000000000018014627239033005156980393746124491372029297053813934326171876", 0x3CA9F63970000000u, 0x254FB1CCu},
|
|
||||||
{"-1.8014627239033005156980393746124491372029297053813934326171874e-16", 0xBCA9F63970000000u, 0xA54FB1CBu},
|
|
||||||
{"1801462723903300515698039374612449137202929705381393432617187501e-79", 0x3CA9F63970000000u, 0x254FB1CCu},
|
|
||||||
{"18014627239033005e-32", 0x3CA9F63970000000u, 0x254FB1CBu},
|
|
||||||
{"0.00000000000000018014627239033006", 0x3CA9F63970000000u, 0x254FB1CCu},
|
|
||||||
{"0.0000000000000001801462723903300515", 0x3CA9F63970000000u, 0x254FB1CBu},
|
|
||||||
{"-1.801462723903300516e-16", 0xBCA9F63970000000u, 0xA54FB1CCu},
|
|
||||||
{"-1.8014627239033005156e-16", 0xBCA9F63970000000u, 0xA54FB1CBu},
|
|
||||||
{"18014627239033005157e-35", 0x3CA9F63970000000u, 0x254FB1CCu},
|
|
||||||
{"180146272390330051569e-36", 0x3CA9F63970000000u, 0x254FB1CBu},
|
|
||||||
{"-0.00000000000000018014627239033005157", 0xBCA9F63970000000u, 0xA54FB1CCu},
|
|
||||||
{"0.000000000000000180146272390330051569803937461", 0x3CA9F63970000000u, 0x254FB1CBu},
|
|
||||||
{"1.80146272390330051569803937462e-16", 0x3CA9F63970000000u, 0x254FB1CCu},
|
|
||||||
{"0.05534819327294826507568359375", 0x3FAC569930000000u, 0x3D62B4CAu},
|
|
||||||
{"-5.534819327294826507568359376e-2", 0xBFAC569930000000u, 0xBD62B4CAu},
|
|
||||||
{"5534819327294826507568359374e-29", 0x3FAC569930000000u, 0x3D62B4C9u},
|
|
||||||
{"-0.0553481932729482650756835937501", 0xBFAC569930000000u, 0xBD62B4CAu},
|
|
||||||
{"5.534819327294826507568359375000000000000000000001e-2", 0x3FAC569930000000u, 0x3D62B4CAu},
|
|
||||||
{"5534819327294826507568359375000000000000000000000000000000e-59", 0x3FAC569930000000u, 0x3D62B4CAu},
|
|
||||||
{"0.055348193272948265", 0x3FAC569930000000u, 0x3D62B4C9u},
|
|
||||||
{"5.5348193272948266e-2", 0x3FAC569930000000u, 0x3D62B4CAu},
|
|
||||||
{"5.534819327294826507e-2", 0x3FAC569930000000u, 0x3D62B4C9u},
|
|
||||||
{"5534819327294826508e-20", 0x3FAC569930000000u, 0x3D62B4CAu},
|
|
||||||
{"55348193272948265075e-21", 0x3FAC569930000000u, 0x3D62B4C9u},
|
|
||||||
{"-0.055348193272948265076", 0xBFAC569930000000u, 0xBD62B4CAu},
|
|
||||||
{"0.0553481932729482650756", 0x3FAC569930000000u, 0x3D62B4C9u},
|
|
||||||
{"-5.53481932729482650757e-2", 0xBFAC569930000000u, 0xBD62B4CAu},
|
|
||||||
{"5.179692133247783258005389047985340416e36", 0x478F2C9450000000u, 0x7C7964A2u},
|
|
||||||
{"5179692133247783258005389047985340417e0", 0x478F2C9450000000u, 0x7C7964A3u},
|
|
||||||
{"5179692133247783258005389047985340415", 0x478F2C9450000000u, 0x7C7964A2u},
|
|
||||||
{"-5.17969213324778325800538904798534041601e36", 0xC78F2C9450000000u, 0xFC7964A3u},
|
|
||||||
{"-5179692133247783258005389047985340416000000000000000000001e-21", 0xC78F2C9450000000u, 0xFC7964A3u},
|
|
||||||
{"-5179692133247783258005389047985340416.000000000000000000000000000000", 0xC78F2C9450000000u, 0xFC7964A2u},
|
|
||||||
{"5.1796921332477832e36", 0x478F2C9450000000u, 0x7C7964A2u},
|
|
||||||
{"51796921332477833e20", 0x478F2C9450000000u, 0x7C7964A3u},
|
|
||||||
{"-5179692133247783258e18", 0xC78F2C9450000000u, 0xFC7964A2u},
|
|
||||||
{"5179692133247783259000000000000000000", 0x478F2C9450000000u, 0x7C7964A3u},
|
|
||||||
{"5179692133247783258000000000000000000", 0x478F2C9450000000u, 0x7C7964A2u},
|
|
||||||
{"5.1796921332477832581e36", 0x478F2C9450000000u, 0x7C7964A3u},
|
|
||||||
{"-5.179692133247783258e36", 0xC78F2C9450000000u, 0xFC7964A2u},
|
|
||||||
{"-517969213324778325801e16", 0xC78F2C9450000000u, 0xFC7964A3u},
|
|
||||||
{"517969213324778325800538904798e7", 0x478F2C9450000000u, 0x7C7964A2u},
|
|
||||||
{"5179692133247783258005389047990000000", 0x478F2C9450000000u, 0x7C7964A3u},
|
|
||||||
{
|
|
||||||
"0.22250738585072011360574097967091319759348195463516456480234261097248222220210769455165295239081350"
|
|
||||||
"8791414915891303962110687008643869459464552765720740782062174337998814106326732925355228688137214901"
|
|
||||||
"2981122451451889849057222307285255133155755015914397476397983411801999323962548289017107081850690630"
|
|
||||||
"6666559949382757725720157630626906633326475653000092458883164330377797918696120494973903778297049050"
|
|
||||||
"5108060994073026293712895895000358379996720725430436028407889577179615094551674824347103070260914462"
|
|
||||||
"1572289880258182545180325707018860872113128079512233426288368622321503775666622503982534335974568884"
|
|
||||||
"4239002654981983854879482922068947216898310996983658468140228542433306603398508864458040010349339704"
|
|
||||||
"2756718644338377048603786162277173854562306587467901408672332763671875e-307", 0x0010000000000000u, 0x00000000u
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"2.22507385850720113605740979670913197593481954635164564802342610972482222202107694551652952390813508"
|
|
||||||
"7914149158913039621106870086438694594645527657207407820621743379988141063267329253552286881372149012"
|
|
||||||
"9811224514518898490572223072852551331557550159143974763979834118019993239625482890171070818506906306"
|
|
||||||
"6665599493827577257201576306269066333264756530000924588831643303777979186961204949739037782970490505"
|
|
||||||
"1080609940730262937128958950003583799967207254304360284078895771796150945516748243471030702609144621"
|
|
||||||
"5722898802581825451803257070188608721131280795122334262883686223215037756666225039825343359745688844"
|
|
||||||
"2390026549819838548794829220689472168983109969836584681402285424333066033985088644580400103493397042"
|
|
||||||
"756718644338377048603786162277173854562306587467901408672332763671875000000000000000000001e-308", 0x0010000000000000u, 0x00000000u
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"0.11754942807573642917278829910357665133228589927589904276829631184250030649651730385585324256680905"
|
|
||||||
"8189392089843750000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"00000000000000000000000000000000000000000000000000000000000000000e-37", 0x380FFFFFE0000000u, 0x00800000u
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"1175494280757364291727882991035766513322858992758990427682963118425003064965173038558532425668090581"
|
|
||||||
"8939208984375000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
"00000000000001e-751", 0x380FFFFFE0000000u, 0x00800000u
|
|
||||||
},
|
|
||||||
{"0", 0x0000000000000000u, 0x00000000u},
|
|
||||||
{"-0", 0x8000000000000000u, 0x80000000u},
|
|
||||||
{"0.0", 0x0000000000000000u, 0x00000000u},
|
|
||||||
{"-0.0", 0x8000000000000000u, 0x80000000u},
|
|
||||||
{"0e999999999999999999999", 0x0000000000000000u, 0x00000000u},
|
|
||||||
{"-0.000e-99999", 0x8000000000000000u, 0x80000000u},
|
|
||||||
{"1e-400", 0x0000000000000000u, 0x00000000u},
|
|
||||||
{"-1e-400", 0x8000000000000000u, 0x80000000u},
|
|
||||||
{"1e400", 0x7FF0000000000000u, 0x7F800000u},
|
|
||||||
{"-1e400", 0xFFF0000000000000u, 0xFF800000u},
|
|
||||||
{"1e-50", 0x358DEE7A4AD4B81Fu, 0x00000000u},
|
|
||||||
{"-1e-50", 0xB58DEE7A4AD4B81Fu, 0x80000000u},
|
|
||||||
{"1e39", 0x48078287F49C4A1Du, 0x7F800000u},
|
|
||||||
{"-1e39", 0xC8078287F49C4A1Du, 0xFF800000u},
|
|
||||||
{"1e99999999999999999999999999", 0x7FF0000000000000u, 0x7F800000u},
|
|
||||||
{"1e-99999999999999999999999999", 0x0000000000000000u, 0x00000000u},
|
|
||||||
{"1e0000000000000000000000000000000000000000308", 0x7FE1CCF385EBC8A0u, 0x7F800000u},
|
|
||||||
{"123456789012345678901234567890e-30", 0x3FBF9ADD3746F65Fu, 0x3DFCD6EAu},
|
|
||||||
{"18446744073709551615", 0x43F0000000000000u, 0x5F800000u},
|
|
||||||
{"18446744073709551616", 0x43F0000000000000u, 0x5F800000u},
|
|
||||||
{"-9223372036854775808", 0xC3E0000000000000u, 0xDF000000u},
|
|
||||||
{"-9223372036854775809", 0xC3E0000000000000u, 0xDF000000u},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return table;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace float_hard_cases
|
|
||||||
+110
-277
@@ -13,18 +13,15 @@
|
|||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
|
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
|
#include <cfloat> // FLT_EVAL_METHOD
|
||||||
#include <cstdint> // uint32_t, uint64_t
|
#include <cstdint> // uint32_t, uint64_t
|
||||||
#include <cstdio> // snprintf
|
|
||||||
#include <cstdlib> // strtod
|
#include <cstdlib> // strtod
|
||||||
#include <cstring> // memcpy
|
#include <cstring> // memcpy
|
||||||
#include <map> // map
|
|
||||||
#include <sstream> // stringstream
|
#include <sstream> // stringstream
|
||||||
#include <string> // string
|
#include <string> // string
|
||||||
#include <utility> // pair
|
#include <utility> // pair
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
#include "float_hard_cases.hpp"
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
// shortcut to scan a string literal
|
// shortcut to scan a string literal
|
||||||
@@ -260,7 +257,7 @@ TEST_CASE("lexer number fast path")
|
|||||||
"123456789012345678901234567890", // huge -> float
|
"123456789012345678901234567890", // huge -> float
|
||||||
"0.30000000000000004", "2.2250738585072014e-308", "1e308",
|
"0.30000000000000004", "2.2250738585072014e-308", "1e308",
|
||||||
// high-precision / wide-exponent values that exercise the
|
// high-precision / wide-exponent values that exercise the
|
||||||
// Eisel-Lemire path beyond the Clinger subset
|
// std::from_chars (Eisel-Lemire) path beyond the Clinger subset
|
||||||
"1.7976931348623157e308", "1.2345678901234567e-250",
|
"1.7976931348623157e308", "1.2345678901234567e-250",
|
||||||
"9007199254740993", "5e-324", "1e-320"
|
"9007199254740993", "5e-324", "1e-320"
|
||||||
};
|
};
|
||||||
@@ -282,18 +279,20 @@ TEST_CASE("lexer number fast path")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("significant digits around Clinger's fast path")
|
SECTION("significant-digit gate for the Clinger fast path")
|
||||||
{
|
{
|
||||||
// Clinger's fast path needs a significand of at most 2^53, which
|
// Clinger's fast path needs a significand below 2^53, so it cannot
|
||||||
// tokens with 17 or more significant digits exceed. The conversion
|
// succeed once the mantissa has 17 or more significant digits (the
|
||||||
// splits the token at the positions the scanners recorded, so leading
|
// significand would be at least 10^16). The lexer skips the attempt
|
||||||
// zeros must not count as digits - "0.1234567890123456" has 16
|
// there. That is only allowed to save work: every value must still come
|
||||||
// significant digits, not 17 - and both scanners must agree.
|
// out bit-exactly, and both scanners must agree. In particular the gate
|
||||||
|
// must not fire for tokens whose leading zeros merely look like extra
|
||||||
|
// digits - "0.1234567890123456" has 16 significant digits, not 17.
|
||||||
const std::vector<std::string> numbers =
|
const std::vector<std::string> numbers =
|
||||||
{
|
{
|
||||||
"1234567890123456", // 16 significant digits
|
"1234567890123456", // 16 significant digits
|
||||||
"12345678901234567", // 17
|
"12345678901234567", // 17 -> attempt skipped
|
||||||
"123456789012345678", // 18
|
"123456789012345678", // 18 -> attempt skipped
|
||||||
"0.1234567890123456", // 16: the leading "0" is not significant
|
"0.1234567890123456", // 16: the leading "0" is not significant
|
||||||
"0.12345678901234567", // 17
|
"0.12345678901234567", // 17
|
||||||
"0.00000000000000001", // 1, in a long token
|
"0.00000000000000001", // 1, in a long token
|
||||||
@@ -664,145 +663,46 @@ TEST_CASE("lexer string fast path")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
TEST_CASE("parse_float_fast declines what it cannot convert exactly")
|
||||||
{
|
{
|
||||||
// the index of the decimal point (or npos) and of the end of the mantissa of a
|
// The lexer only hands well-formed numbers to parse_float_fast, so the
|
||||||
// number token, which the lexer records while scanning it
|
// malformed ones below can only be passed to it directly. Declining is
|
||||||
std::pair<std::size_t, std::size_t> float_token_layout(const std::string& s)
|
// always safe: the caller then falls back to a slower, exact conversion.
|
||||||
{
|
const auto fast = [](const std::string & s, double & out)
|
||||||
std::size_t dot = std::string::npos;
|
|
||||||
std::size_t mantissa_end = s.size();
|
|
||||||
for (std::size_t i = 0; i < s.size(); ++i)
|
|
||||||
{
|
{
|
||||||
if (s[i] == '.')
|
return nlohmann::detail::parse_float_fast(s.data(), s.data() + s.size(), out);
|
||||||
{
|
|
||||||
dot = i;
|
|
||||||
}
|
|
||||||
else if (s[i] == 'e' || s[i] == 'E')
|
|
||||||
{
|
|
||||||
mantissa_end = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {dot, mantissa_end};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FloatType>
|
|
||||||
FloatType parse_native(const std::string& s)
|
|
||||||
{
|
|
||||||
const auto layout = float_token_layout(s);
|
|
||||||
return nlohmann::detail::parse_float_native<FloatType>(s.data(), s.data() + s.size(), layout.first, layout.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint64_t bits_of(double d)
|
|
||||||
{
|
|
||||||
std::uint64_t b = 0;
|
|
||||||
std::memcpy(&b, &d, sizeof(b));
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t bits_of(float f)
|
|
||||||
{
|
|
||||||
std::uint32_t b = 0;
|
|
||||||
std::memcpy(&b, &f, sizeof(b));
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint64_t native_bits64(const std::string& s)
|
|
||||||
{
|
|
||||||
return bits_of(parse_native<double>(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t native_bits32(const std::string& s)
|
|
||||||
{
|
|
||||||
return bits_of(parse_native<float>(s));
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("parse_float_native rounds correctly")
|
|
||||||
{
|
|
||||||
SECTION("double")
|
|
||||||
{
|
|
||||||
CHECK(native_bits64("1.5") == 0x3FF8000000000000u);
|
|
||||||
CHECK(native_bits64("0.1") == 0x3FB999999999999Au);
|
|
||||||
CHECK(native_bits64("-0.0") == 0x8000000000000000u);
|
|
||||||
CHECK(native_bits64("0e999999999999999999999") == 0u);
|
|
||||||
// 2^53 + 1 is exactly between two doubles: ties to even, unless more digits follow
|
|
||||||
CHECK(native_bits64("9007199254740993") == 0x4340000000000000u);
|
|
||||||
CHECK(native_bits64("9007199254740993.0000000000000000001") == 0x4340000000000001u);
|
|
||||||
CHECK(native_bits64("9007199254740992.9999999999999999999") == 0x4340000000000000u);
|
|
||||||
// 1 + 2^-53 exactly (a tie), and one unit in the 55th digit around it
|
|
||||||
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203125") == 0x3FF0000000000000u);
|
|
||||||
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203126") == 0x3FF0000000000001u);
|
|
||||||
CHECK(native_bits64("1.00000000000000011102230246251565404236316680908203124") == 0x3FF0000000000000u);
|
|
||||||
// subnormal and overflow boundaries
|
|
||||||
CHECK(native_bits64("2.4703282292062327e-324") == 0u);
|
|
||||||
CHECK(native_bits64("2.4703282292062328e-324") == 1u);
|
|
||||||
CHECK(native_bits64("2.2250738585072011e-308") == 0x000FFFFFFFFFFFFFu);
|
|
||||||
CHECK(native_bits64("2.2250738585072012e-308") == 0x0010000000000000u);
|
|
||||||
CHECK(native_bits64("1.7976931348623157e308") == 0x7FEFFFFFFFFFFFFFu);
|
|
||||||
CHECK(native_bits64("1.7976931348623159e308") == 0x7FF0000000000000u);
|
|
||||||
CHECK(native_bits64("-1e400") == 0xFFF0000000000000u);
|
|
||||||
CHECK(native_bits64("-1e-400") == 0x8000000000000000u);
|
|
||||||
// exponents and zeros far beyond the range cancel out
|
|
||||||
CHECK(native_bits64("0." + std::string(1000, '0') + "1e1001") == 0x3FF0000000000000u);
|
|
||||||
CHECK(native_bits64("1" + std::string(1000, '0') + "e-1000") == 0x3FF0000000000000u);
|
|
||||||
CHECK(native_bits64("1e-99999999999999999999999") == 0u);
|
|
||||||
CHECK(native_bits64("1E+99999999999999999999999") == 0x7FF0000000000000u);
|
|
||||||
// more digits than any midpoint has (769): only whether a nonzero digit follows matters
|
|
||||||
const std::string tie = "1.00000000000000011102230246251565404236316680908203125";
|
|
||||||
CHECK(native_bits64(tie + std::string(800, '0')) == 0x3FF0000000000000u);
|
|
||||||
CHECK(native_bits64(tie + std::string(800, '0') + "1") == 0x3FF0000000000001u);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("float")
|
|
||||||
{
|
|
||||||
CHECK(native_bits32("1.5") == 0x3FC00000u);
|
|
||||||
CHECK(native_bits32("0.1") == 0x3DCCCCCDu);
|
|
||||||
CHECK(native_bits32("-0.0") == 0x80000000u);
|
|
||||||
// 2^24 + 1 is exactly between two floats
|
|
||||||
CHECK(native_bits32("16777217") == 0x4B800000u);
|
|
||||||
CHECK(native_bits32("16777217.000000000000000000001") == 0x4B800001u);
|
|
||||||
CHECK(native_bits32("16777218.999999999999999999999") == 0x4B800001u);
|
|
||||||
CHECK(native_bits32("16777219") == 0x4B800002u);
|
|
||||||
// subnormal and overflow boundaries
|
|
||||||
CHECK(native_bits32("3.4028235677973366e38") == 0x7F7FFFFFu);
|
|
||||||
CHECK(native_bits32("3.4028235677973367e38") == 0x7F800000u);
|
|
||||||
CHECK(native_bits32("7.006492321624085e-46") == 0u);
|
|
||||||
CHECK(native_bits32("7.006492321624086e-46") == 1u);
|
|
||||||
CHECK(native_bits32("1.1754942e-38") == 0x007FFFFFu);
|
|
||||||
CHECK(native_bits32("-1.17549435e-38") == 0x80800000u);
|
|
||||||
CHECK(native_bits32("1e39") == 0x7F800000u);
|
|
||||||
CHECK(native_bits32("-1e-50") == 0x80000000u);
|
|
||||||
// not rounded through double: its double would round to another float
|
|
||||||
CHECK(native_bits32("1.00000005960464477539062500000000001") == 0x3F800001u);
|
|
||||||
CHECK(native_bits32("9007199254740993") == 0x5A000000u);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("the conversion shared with other parsers")
|
|
||||||
{
|
|
||||||
// convert_float() gives the lexer's results, for every type
|
|
||||||
const std::vector<std::string> tokens =
|
|
||||||
{
|
|
||||||
"0", "-0.0", "1.5", "0.1", "1e-400", "-2.5E+3", "123456789012345678901234567890",
|
|
||||||
"9007199254740993.0000000000000000001", "4.9406564584124654e-324"
|
|
||||||
};
|
};
|
||||||
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
double out = 0;
|
||||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
|
||||||
for (const auto& t : tokens)
|
#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0
|
||||||
{
|
// without true double precision, the fast path declines everything
|
||||||
CAPTURE(t);
|
CHECK_FALSE(fast("1.5", out));
|
||||||
const auto layout = float_token_layout(t);
|
#else
|
||||||
const char* const first = t.data();
|
CHECK(fast("1.5", out));
|
||||||
const char* const last = first + t.size();
|
CHECK(out == 1.5);
|
||||||
const auto d = nlohmann::detail::convert_float<double>(first, last, layout.first, layout.second);
|
CHECK(fast("+2.5e1", out));
|
||||||
const auto f = nlohmann::detail::convert_float<float>(first, last, layout.first, layout.second);
|
CHECK(out == 25.0);
|
||||||
const auto ld = nlohmann::detail::convert_float<long double>(first, last, layout.first, layout.second);
|
CHECK(fast("-25E-1", out));
|
||||||
CHECK(bits_of(d) == bits_of(json::parse(t).get<double>()));
|
CHECK(out == -2.5);
|
||||||
CHECK(bits_of(f) == bits_of(float_json::parse(t).get<float>()));
|
CHECK(fast("1e", out));
|
||||||
CHECK(ld == long_double_json::parse(t).get<long double>());
|
CHECK(out == 1.0);
|
||||||
}
|
#endif
|
||||||
}
|
|
||||||
|
// not a number
|
||||||
|
CHECK_FALSE(fast("", out));
|
||||||
|
CHECK_FALSE(fast("-", out));
|
||||||
|
CHECK_FALSE(fast(".", out));
|
||||||
|
CHECK_FALSE(fast("1.2.3", out));
|
||||||
|
CHECK_FALSE(fast("1x", out));
|
||||||
|
CHECK_FALSE(fast("1e+", out));
|
||||||
|
CHECK_FALSE(fast("1e1x", out));
|
||||||
|
|
||||||
|
// numbers that are not represented exactly on the fast path
|
||||||
|
CHECK_FALSE(fast("12345678901234567890", out));
|
||||||
|
CHECK_FALSE(fast("1e10000", out));
|
||||||
|
CHECK_FALSE(fast("9007199254740993", out));
|
||||||
|
CHECK_FALSE(fast("1e23", out));
|
||||||
|
CHECK_FALSE(fast("1e-23", out));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -906,6 +806,40 @@ std::size_t big_bit_length(const big_uint& a)
|
|||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::uint64_t bits_of(double d)
|
||||||
|
{
|
||||||
|
std::uint64_t b = 0;
|
||||||
|
std::memcpy(&b, &d, sizeof(b));
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eisel_lemire(const std::string& s, double& out)
|
||||||
|
{
|
||||||
|
return nlohmann::detail::parse_float_eisel_lemire(s.data(), s.data() + s.size(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// significant digits of a token, without trailing zeros
|
||||||
|
std::size_t significant_digits(const std::string& s)
|
||||||
|
{
|
||||||
|
std::string digits;
|
||||||
|
for (const char c : s)
|
||||||
|
{
|
||||||
|
if (c == 'e' || c == 'E')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c >= '0' && c <= '9' && !(digits.empty() && c == '0'))
|
||||||
|
{
|
||||||
|
digits += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!digits.empty() && digits.back() == '0')
|
||||||
|
{
|
||||||
|
digits.pop_back();
|
||||||
|
}
|
||||||
|
return digits.size();
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("Eisel-Lemire float conversion")
|
TEST_CASE("Eisel-Lemire float conversion")
|
||||||
@@ -1303,33 +1237,26 @@ TEST_CASE("Eisel-Lemire float conversion")
|
|||||||
for (const auto& c : known)
|
for (const auto& c : known)
|
||||||
{
|
{
|
||||||
CAPTURE(c.first)
|
CAPTURE(c.first)
|
||||||
CHECK(native_bits64(c.first) == c.second);
|
double out = 0;
|
||||||
}
|
if (eisel_lemire(c.first, out))
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("binary32")
|
|
||||||
{
|
{
|
||||||
using binary32 = nlohmann::detail::ieee_binary_format<24>;
|
CHECK(bits_of(out) == c.second);
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 1) == 0x3F800000u);
|
}
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-1, 1) == 0x3DCCCCCDu);
|
else
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-1, 15) == 0x3FC00000u);
|
{
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 16777217) == 0x4B800000u); // tie, to even
|
// only tokens with more than 19 significant digits are left to
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(0, 16777219) == 0x4B800002u); // tie, to even
|
// strtod: those whose value lies too close to a tie
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-45, 1) == 0x00000001u);
|
CHECK(significant_digits(c.first) > 19);
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-46, 7) == 0x00000000u);
|
}
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-46, 8) == 0x00000001u);
|
}
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-65, 9999999999999999999u) == 0x00000000u);
|
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(20, 3402823466385288598u) == 0x7F7FFFFFu);
|
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(20, 3402823669209384635u) == 0x7F800000u);
|
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(39, 1) == 0x7F800000u);
|
|
||||||
CHECK(nlohmann::detail::eisel_lemire<binary32>(-5, 0) == 0x00000000u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("round trip")
|
SECTION("round trip")
|
||||||
{
|
{
|
||||||
// every double written by to_chars and read back, and its 17-digit
|
// every double written by to_chars and read back, also with trailing
|
||||||
// form with trailing digits that make the token longer than 19 digits
|
// digits that make the token longer than 19 digits
|
||||||
std::uint64_t state = 5295;
|
std::uint64_t state = 5295;
|
||||||
|
std::size_t declined = 0;
|
||||||
for (int i = 0; i < 200000; ++i)
|
for (int i = 0; i < 200000; ++i)
|
||||||
{
|
{
|
||||||
state ^= state << 13u;
|
state ^= state << 13u;
|
||||||
@@ -1351,51 +1278,30 @@ TEST_CASE("Eisel-Lemire float conversion")
|
|||||||
const char* end = nlohmann::detail::to_chars(buffer.data(), buffer.data() + buffer.size(), d);
|
const char* end = nlohmann::detail::to_chars(buffer.data(), buffer.data() + buffer.size(), d);
|
||||||
const std::string token(buffer.data(), static_cast<std::size_t>(end - buffer.data()));
|
const std::string token(buffer.data(), static_cast<std::size_t>(end - buffer.data()));
|
||||||
CAPTURE(token)
|
CAPTURE(token)
|
||||||
CHECK(native_bits64(token) == b);
|
double out = 0;
|
||||||
|
REQUIRE(eisel_lemire(token, out));
|
||||||
|
CHECK(bits_of(out) == b);
|
||||||
|
|
||||||
// insert digits before the exponent of the 17-digit form: that
|
// insert digits before the exponent: the value moves by far less
|
||||||
// form lies strictly inside the rounding interval of the double
|
// than the distance to the rounding boundary, so it must not change
|
||||||
// (the shortest one may lie on its boundary), and the digits move
|
std::string longer = token;
|
||||||
// it by far less than the distance to the boundary, so the value
|
|
||||||
// must not change
|
|
||||||
std::array<char, 64> digits17{};
|
|
||||||
static_cast<void>(std::snprintf(digits17.data(), digits17.size(), "%.17g", d)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
|
||||||
std::string longer = digits17.data();
|
|
||||||
const std::size_t e = longer.find('e');
|
const std::size_t e = longer.find('e');
|
||||||
const std::size_t dot = longer.find('.');
|
const std::size_t dot = longer.find('.');
|
||||||
const std::string extra = dot == std::string::npos ? ".000000000000000000001" : "000000000000000000001";
|
const std::string extra = dot == std::string::npos ? ".000000000000000000001" : "000000000000000000001";
|
||||||
longer.insert(e == std::string::npos ? longer.size() : e, extra);
|
longer.insert(e == std::string::npos ? longer.size() : e, extra);
|
||||||
CAPTURE(longer)
|
CAPTURE(longer)
|
||||||
CHECK(native_bits64(longer) == b);
|
if (eisel_lemire(longer, out))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("round trip, binary32")
|
|
||||||
{
|
{
|
||||||
std::uint32_t state = 5295;
|
CHECK(bits_of(out) == b);
|
||||||
for (int i = 0; i < 100000; ++i)
|
|
||||||
{
|
|
||||||
state ^= state << 13u;
|
|
||||||
state ^= state >> 17u;
|
|
||||||
state ^= state << 5u;
|
|
||||||
std::uint32_t b = state;
|
|
||||||
if ((b & 0x7F800000u) == 0x7F800000u)
|
|
||||||
{
|
|
||||||
continue; // infinity or NaN
|
|
||||||
}
|
}
|
||||||
if (i % 4 == 0)
|
else
|
||||||
{
|
{
|
||||||
b &= 0x807FFFFFu; // subnormals
|
// w and w + 1 round differently: only when the value is very
|
||||||
|
// close to a rounding boundary
|
||||||
|
++declined;
|
||||||
}
|
}
|
||||||
float f = 0;
|
|
||||||
std::memcpy(&f, &b, sizeof(f));
|
|
||||||
|
|
||||||
std::array<char, 64> buffer{};
|
|
||||||
const char* end = nlohmann::detail::to_chars(buffer.data(), buffer.data() + buffer.size(), f);
|
|
||||||
const std::string token(buffer.data(), static_cast<std::size_t>(end - buffer.data()));
|
|
||||||
CAPTURE(token);
|
|
||||||
CHECK(native_bits32(token) == b);
|
|
||||||
}
|
}
|
||||||
|
CHECK(declined < 1000); // 107 of the 200,000
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("used by the lexer")
|
SECTION("used by the lexer")
|
||||||
@@ -1409,76 +1315,3 @@ TEST_CASE("Eisel-Lemire float conversion")
|
|||||||
"[json.exception.out_of_range.406] number overflow parsing '1.7976931348623159e308'", json::out_of_range&);
|
"[json.exception.out_of_range.406] number overflow parsing '1.7976931348623159e308'", json::out_of_range&);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
using float_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, float>;
|
|
||||||
|
|
||||||
// the bits of the float that parse() gives for a token, via both scanners;
|
|
||||||
// the value must be the same for both
|
|
||||||
template<typename Json, typename Bits>
|
|
||||||
void check_parse(const std::string& token, Bits expected, Bits infinity)
|
|
||||||
{
|
|
||||||
std::stringstream stream(token);
|
|
||||||
if ((expected & ~(Bits{1} << (8 * sizeof(Bits) - 1))) == infinity)
|
|
||||||
{
|
|
||||||
Json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = Json::parse(token), ("[json.exception.out_of_range.406] number overflow parsing '" + token + "'").c_str(), typename Json::out_of_range&);
|
|
||||||
CHECK_THROWS_WITH_AS(_ = Json::parse(stream), ("[json.exception.out_of_range.406] number overflow parsing '" + token + "'").c_str(), typename Json::out_of_range&);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const Json contiguous = Json::parse(token);
|
|
||||||
const Json streamed = Json::parse(stream);
|
|
||||||
if (contiguous.is_number_float()) // not an integer that fits
|
|
||||||
{
|
|
||||||
CHECK(bits_of(contiguous.template get<typename Json::number_float_t>()) == expected);
|
|
||||||
CHECK(bits_of(streamed.template get<typename Json::number_float_t>()) == expected);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CHECK(streamed.is_number_integer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("float conversion of hard cases")
|
|
||||||
{
|
|
||||||
// see float_hard_cases.hpp
|
|
||||||
for (const auto& c : float_hard_cases::cases())
|
|
||||||
{
|
|
||||||
const std::string token = c.token;
|
|
||||||
CAPTURE(token);
|
|
||||||
CHECK(native_bits64(token) == c.bits64);
|
|
||||||
CHECK(native_bits32(token) == c.bits32);
|
|
||||||
check_parse<json>(token, c.bits64, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
check_parse<float_json>(token, c.bits32, std::uint32_t{0x7F800000u});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("float overflow and underflow in the parser")
|
|
||||||
{
|
|
||||||
SECTION("double")
|
|
||||||
{
|
|
||||||
check_parse<json>("1.7976931348623157e308", std::uint64_t{0x7FEFFFFFFFFFFFFFu}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
check_parse<json>("1.7976931348623159e308", std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
check_parse<json>("-1e309", std::uint64_t{0xFFF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
check_parse<json>("1" + std::string(400, '0'), std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
check_parse<json>("1e99999999999999999999", std::uint64_t{0x7FF0000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
// an underflow gives a zero with the sign of the token
|
|
||||||
check_parse<json>("1e-400", std::uint64_t{0}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
check_parse<json>("-1e-400", std::uint64_t{0x8000000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
check_parse<json>("-2.4703282292062327e-324", std::uint64_t{0x8000000000000000u}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
check_parse<json>("0." + std::string(400, '0') + "1", std::uint64_t{0}, std::uint64_t{0x7FF0000000000000u});
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("float")
|
|
||||||
{
|
|
||||||
check_parse<float_json>("3.4028234e38", std::uint32_t{0x7F7FFFFFu}, std::uint32_t{0x7F800000u});
|
|
||||||
check_parse<float_json>("3.4028236e38", std::uint32_t{0x7F800000u}, std::uint32_t{0x7F800000u});
|
|
||||||
check_parse<float_json>("-1e39", std::uint32_t{0xFF800000u}, std::uint32_t{0x7F800000u});
|
|
||||||
check_parse<float_json>("1e-46", std::uint32_t{0}, std::uint32_t{0x7F800000u});
|
|
||||||
check_parse<float_json>("-1e-46", std::uint32_t{0x80000000u}, std::uint32_t{0x7F800000u});
|
|
||||||
check_parse<float_json>("-7.006492321624085e-46", std::uint32_t{0x80000000u}, std::uint32_t{0x7F800000u});
|
|
||||||
check_parse<float_json>("-7.006492321624086e-46", std::uint32_t{0x80000001u}, std::uint32_t{0x7F800000u});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -260,11 +260,10 @@ struct LocaleSwitchingSax final: public nlohmann::json_sax<json>
|
|||||||
|
|
||||||
TEST_CASE("locale changes between lexer construction and number conversion (#5198)")
|
TEST_CASE("locale changes between lexer construction and number conversion (#5198)")
|
||||||
{
|
{
|
||||||
// float and double are converted without the locale. A long double that
|
// The numbers are chosen so that the conversion also takes the strtod
|
||||||
// is not binary64 can take the strtold fallback, which honors the locale
|
// fallback, which honors the locale that is current at conversion time:
|
||||||
// that is current at conversion time. The numbers are chosen so that it
|
// too many significant digits for Clinger's fast path, an underflow that
|
||||||
// does: too many significant digits for Clinger's fast path, an underflow
|
// std::from_chars rejects, and a plain value.
|
||||||
// that std::from_chars rejects, and a plain value.
|
|
||||||
const std::vector<std::string> numbers = {"3.14159265358979323846", "1.5e-400", "12.34", "-0.000123456789012345678"};
|
const std::vector<std::string> numbers = {"3.14159265358979323846", "1.5e-400", "12.34", "-0.000123456789012345678"};
|
||||||
std::string text = "[";
|
std::string text = "[";
|
||||||
for (const auto& n : numbers)
|
for (const auto& n : numbers)
|
||||||
@@ -328,8 +327,7 @@ TEST_CASE("locale changes between lexer construction and number conversion (#519
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a long double goes through std::strtold unless it is binary64 or
|
// a long double goes through std::strtold unless std::from_chars supports it
|
||||||
// std::from_chars supports it
|
|
||||||
{
|
{
|
||||||
bool switched = false;
|
bool switched = false;
|
||||||
const auto cb = [&](int /*depth*/, long_double_json::parse_event_t event, long_double_json& /*parsed*/) noexcept
|
const auto cb = [&](int /*depth*/, long_double_json::parse_event_t event, long_double_json& /*parsed*/) noexcept
|
||||||
@@ -355,15 +353,8 @@ TEST_CASE("locale with a multi-byte decimal point")
|
|||||||
{
|
{
|
||||||
// Some locales use a decimal point that is not a single character, e.g.
|
// Some locales use a decimal point that is not a single character, e.g.
|
||||||
// U+066B ARABIC DECIMAL SEPARATOR (two bytes in UTF-8). It cannot be
|
// U+066B ARABIC DECIMAL SEPARATOR (two bytes in UTF-8). It cannot be
|
||||||
// substituted in place for '.', so the strtold fallback (only for long
|
// substituted in place for '.', so the strtod fallback stops early. The
|
||||||
// double formats other than binary64) converts a copy of the token with
|
// conversion must still terminate rather than retry forever.
|
||||||
// the whole decimal point instead (#5660). The values must be those of the
|
|
||||||
// "C" locale.
|
|
||||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t, std::uint64_t, long double>;
|
|
||||||
const char* const long_double_numbers = "[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]";
|
|
||||||
REQUIRE(std::setlocale(LC_NUMERIC, "C") != nullptr);
|
|
||||||
const long_double_json expected_long_double = long_double_json::parse(long_double_numbers);
|
|
||||||
|
|
||||||
const std::array<const char*, 6> names = {{"ar_EG.UTF-8", "ar_SA.UTF-8", "fa_IR.UTF-8", "ps_AF.UTF-8", "ar_EG", "fa_IR"}};
|
const std::array<const char*, 6> names = {{"ar_EG.UTF-8", "ar_SA.UTF-8", "fa_IR.UTF-8", "ps_AF.UTF-8", "ar_EG", "fa_IR"}};
|
||||||
bool tested = false;
|
bool tested = false;
|
||||||
for (const char* name : names)
|
for (const char* name : names)
|
||||||
@@ -381,20 +372,12 @@ TEST_CASE("locale with a multi-byte decimal point")
|
|||||||
tested = true;
|
tested = true;
|
||||||
|
|
||||||
// too many significant digits for Clinger's fast path, and an underflow
|
// too many significant digits for Clinger's fast path, and an underflow
|
||||||
// that std::from_chars rejects: double does not depend on the locale
|
// that std::from_chars rejects: both reach the strtod fallback
|
||||||
json j;
|
json j;
|
||||||
CHECK_NOTHROW(j = json::parse("[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]"));
|
CHECK_NOTHROW(j = json::parse("[3.14159265358979323846, 1.5e-400, -0.000123456789012345678]"));
|
||||||
CHECK(j.is_array());
|
CHECK(j.is_array());
|
||||||
CHECK(j[0] == 3.14159265358979323846);
|
|
||||||
CHECK(j[1] == 0.0);
|
|
||||||
CHECK(j[2] == -0.000123456789012345678);
|
|
||||||
CHECK(json::accept("3.14159265358979323846"));
|
CHECK(json::accept("3.14159265358979323846"));
|
||||||
|
|
||||||
// a long double that reaches the strtold fallback is not truncated
|
|
||||||
long_double_json ld;
|
|
||||||
CHECK_NOTHROW(ld = long_double_json::parse(long_double_numbers));
|
|
||||||
CHECK(ld == expected_long_double);
|
|
||||||
|
|
||||||
// a value the locale-independent paths convert is not affected
|
// a value the locale-independent paths convert is not affected
|
||||||
CHECK(json::parse("12.5") == 12.5);
|
CHECK(json::parse("12.5") == 12.5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,393 @@
|
|||||||
|
# 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 an `@sa` URL into the documentation site
|
||||||
|
(`https://json.nlohmann.me/`) — a genuine documentation leak. `@sa` links to anything else, such as GitHub
|
||||||
|
issues, are ignored. 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
+183
@@ -0,0 +1,183 @@
|
|||||||
|
#!/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
|
||||||
|
# subprocess is only called with fixed argument lists, never through a shell.
|
||||||
|
import subprocess # nosec B404
|
||||||
|
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( # nosec B603 B607
|
||||||
|
['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
+133
@@ -0,0 +1,133 @@
|
|||||||
|
#!/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 anywhere under include_dir.
|
||||||
|
|
||||||
|
A reference is any #ifdef, #ifndef, or defined() check.
|
||||||
|
|
||||||
|
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
+297
@@ -0,0 +1,297 @@
|
|||||||
|
#!/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
|
||||||
|
# subprocess is only called with fixed argument lists, never through a shell.
|
||||||
|
import subprocess # nosec B404
|
||||||
|
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( # nosec B603 B607
|
||||||
|
['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( # nosec B603 B607
|
||||||
|
['git', 'archive', ref, '--', 'include'],
|
||||||
|
capture_output=True, check=True
|
||||||
|
)
|
||||||
|
subprocess.run(['tar', '-x', '-C', tmpdir], input=archive.stdout, check=True) # nosec B603 B607
|
||||||
|
|
||||||
|
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( # nosec B603
|
||||||
|
[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
+690
@@ -0,0 +1,690 @@
|
|||||||
|
#!/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
|
||||||
|
# subprocess is only called with fixed argument lists, never through a shell.
|
||||||
|
import subprocess # nosec B404
|
||||||
|
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+(?=::|$)')
|
||||||
|
|
||||||
|
# Only @sa URLs into the documentation site are doc links; internal code also uses @sa to point at
|
||||||
|
# GitHub issues (e.g. the recursion-limit rationale), which is not a documentation leak.
|
||||||
|
DOCS_SITE_URL = 'https://json.nlohmann.me/'
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
"""
|
||||||
|
Return the normalized source text of a declaration's own signature.
|
||||||
|
|
||||||
|
The signature covers the 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:
|
||||||
|
"""
|
||||||
|
Return the name component of a cursor's identity.
|
||||||
|
|
||||||
|
This is 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:
|
||||||
|
"""
|
||||||
|
Return a stable, overload-disambiguating identity key for a cursor.
|
||||||
|
|
||||||
|
The key is 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( # nosec B603 B607
|
||||||
|
['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( # nosec B603
|
||||||
|
[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: # nosec B112
|
||||||
|
# libclang rejected this candidate; try the next one.
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
filename = cindex.conf.get_filename()
|
||||||
|
if filename:
|
||||||
|
cindex.conf.set_library_file(filename)
|
||||||
|
return True
|
||||||
|
except Exception: # nosec B110
|
||||||
|
# No usable default library; the caller reports the failure.
|
||||||
|
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 an @sa URL into the documentation site (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 and leaked_url.startswith(DOCS_SITE_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: # nosec B110
|
||||||
|
# Alias without a resolvable declaration: no @sa to follow.
|
||||||
|
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.
|
||||||
|
|
||||||
|
The surface is 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()
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# API surface history
|
||||||
|
|
||||||
|
One file per released `v3.*` tag: `<tag>.json`, e.g. `v3.12.0.json`. Each is the output of
|
||||||
|
`extract_api.py --surface-output` at that tag (see `tools/api_checker/README.md` for the schema),
|
||||||
|
with additional immutable provenance in `meta`: `ref`, `commit` (the tag's resolved commit sha),
|
||||||
|
`generated_at`, and `generator`.
|
||||||
|
|
||||||
|
## Conventions
|
||||||
|
|
||||||
|
- **Immutable once committed.** Files here are never hand-edited or silently regenerated.
|
||||||
|
`snapshot_release.py` refuses to overwrite an existing file unless `--force` is passed, and that
|
||||||
|
should only happen for a deliberate, reviewed fix — the resulting diff should be inspected before
|
||||||
|
committing, same as any other source change.
|
||||||
|
- **Generated by `tools/api_checker/snapshot_release.py`**, run manually as part of cutting a
|
||||||
|
release (see `tools/api_checker/README.md`'s "Workflow: Release Checklist"). Not CI-automated.
|
||||||
|
- **`diff_api.py` uses these automatically.** `--old`/`--new` check here first (matching the ref
|
||||||
|
string to `<ref>.json`) before falling back to live `git archive` extraction — see
|
||||||
|
`tools/api_checker/README.md`'s `diff_api.py` section.
|
||||||
|
|
||||||
|
## Coverage
|
||||||
|
|
||||||
|
Backfilled: every `v3.*` tag from `v3.1.0` through the latest release at backfill time
|
||||||
|
(`v3.12.0`), covering the full public API history of the `include/nlohmann/` header layout.
|
||||||
|
|
||||||
|
## Format history
|
||||||
|
|
||||||
|
- **`format_version: 2`** (current). Fixed `extract_api.py`'s `ABI_TAG_PATTERN` to also strip the
|
||||||
|
pre-rename ABI inline-namespace form used by `v3.11.0`/`v3.11.1`: `json_v3_11_0` (no `_abi`
|
||||||
|
segment), renamed to today's `json_abi_v3_11_2`-style tag starting with `v3.11.2`. Under
|
||||||
|
`format_version: 1`, that older form wasn't recognized, so every `scope`/`signature` at
|
||||||
|
`v3.11.0`/`v3.11.1` retained the raw un-stripped namespace segment, making `diff_api.py` report
|
||||||
|
essentially the entire API (~330 of ~332 entries) as removed-and-readded across
|
||||||
|
`v3.10.5`->`v3.11.0`->`v3.11.1`->`v3.11.2` — a bug of the same shape as the earlier USR-arity
|
||||||
|
issue documented in `extract_api.py`'s `identity_key()` docstring, caught the same way: by
|
||||||
|
actually diffing real consecutive release pairs instead of trusting the extractor in isolation.
|
||||||
|
All 27 files were regenerated under `format_version: 2`; only `v3.11.0.json` and `v3.11.1.json`
|
||||||
|
actually changed content (every other tag's scopes never contained the old-style tag).
|
||||||
|
|
||||||
|
- **`format_version: 3`**. Found via a CI run on a different machine than the one that generated
|
||||||
|
`format_version: 2`, which is exactly the failure class this versioning exists to catch:
|
||||||
|
1. `JSON_HAS_RANGES` auto-detects via the standard library's `__cpp_lib_ranges` feature-test
|
||||||
|
macro. That macro isn't reliably gated to C++20 mode by every stdlib -- undefined under
|
||||||
|
`-std=c++17` with macOS's libc++, but defined under the identical flag with the Ubuntu
|
||||||
|
stdlib used in CI -- so `parse()`/`accept()`/`from_bjdata()`/etc. extracted a different
|
||||||
|
signature (with or without a `SentinelType` parameter) purely depending on which machine
|
||||||
|
ran the extraction. Now pinned to `-DJSON_HAS_RANGES=0` in `extract_api.py`'s parse
|
||||||
|
arguments: the deterministic choice, since forcing `1` was tried first and found to
|
||||||
|
actually fail to parse on a stdlib without full `<ranges>` support even when the macro
|
||||||
|
claims otherwise.
|
||||||
|
2. `get_identity_name()` used `cursor.spelling` verbatim for `CONVERSION_FUNCTION` cursors,
|
||||||
|
which libclang renders as its own internally-canonicalized form of the return type rather
|
||||||
|
than what's literally written. Confirmed for `json_pointer::operator string_t()`: spelled
|
||||||
|
`"operator nlohmann::json_pointer::string_t_helper<type-parameter-0-0>::type"` on one
|
||||||
|
machine and `"operator typename string_t_helper<type-parameter-0-0>::type"` on another,
|
||||||
|
with both machines pinned to the identical `libclang==18.1.1` wheel and the JSON_HAS_RANGES
|
||||||
|
fix above ruled out as the cause. Now derived from the cursor's own raw source text instead
|
||||||
|
(regex over `get_signature_text()`'s output), immune to libclang's dependent-type
|
||||||
|
resolution differences and incidentally more readable
|
||||||
|
(`"operator string_t"`/`"operator ValueType"` instead of the libclang-internal forms).
|
||||||
|
All 27 files were regenerated under `format_version: 3`.
|
||||||
|
|
||||||
|
## Known gaps
|
||||||
|
|
||||||
|
- **`v3.0.0`, `v3.0.1`**: not backfilled. These predate the `include/nlohmann/` directory
|
||||||
|
structure entirely — headers lived under `src/` at that point (a single `src/json.hpp`). Since
|
||||||
|
`extract_api.py` hardcodes `include/nlohmann/json.hpp` as the entry point,
|
||||||
|
`snapshot_release.py --all-tags` fails cleanly on these two refs (`git archive ... -- include`
|
||||||
|
finds nothing) rather than silently producing a wrong/empty result. Not pursued: two tags,
|
||||||
|
immediately superseded by `v3.1.0`, and supporting the pre-restructuring layout would need a
|
||||||
|
separate header/include-path convention with no other benefit. If full pre-3.1 coverage is ever
|
||||||
|
wanted, `extract_api.py` would need a `src/json.hpp`-aware mode first.
|
||||||
|
- Pre-`v3.0.0` tags (`v1.x`, `v2.x`, `v3.0.0-rc*`, etc.) were never attempted — out of scope for
|
||||||
|
this backfill; see `tools/api_checker/POLICY.md` for the stated boundary.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user