diff --git a/.drone.star b/.drone.star index 13853ace4..5ba95ce09 100644 --- a/.drone.star +++ b/.drone.star @@ -18,7 +18,7 @@ def main(ctx): linux_cxx("gcc 11 s390x", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,2a'}, arch="s390x", globalenv=globalenv), linux_cxx("Clang 12 arm64", "clang++-12", packages="clang-12 libstdc++-9-dev", llvm_os="focal", llvm_ver="12", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={'B2_TOOLSET': 'clang-12', 'B2_CXXSTD': '17,20', 'DRONE_JOB_UUID': '7719a1c783m'}, arch="arm64", globalenv=globalenv), linux_cxx("gcc 11 arm64", "g++-11", packages="g++-11", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2004:multiarch", environment={'B2_TOOLSET': 'gcc-11', 'B2_CXXSTD': '17,2a', 'DRONE_JOB_UUID': '0716d9708dm'}, arch="arm64", globalenv=globalenv), - linux_cxx("docs", "g++", packages="docbook docbook-xml docbook-xsl python3-jinja2 xsltproc flex libfl-dev bison unzip rsync", buildtype="docs", buildscript="drone", image="cppalliance/droneubuntu1804:1", environment={'COMMENT': 'docs', 'DRONE_JOB_UUID': 'b6589fc6ab'}, globalenv=globalenv), + linux_cxx("docs", "g++", buildtype="docs", buildscript="drone", image="cppalliance/boost_superproject_build:24.04-v3", environment={'COMMENT': 'docs', 'DRONE_JOB_UUID': 'b6589fc6ab'}, globalenv=globalenv), linux_cxx("codecov", "g++-8", packages="g++-8", buildtype="codecov", buildscript="drone", image=linuxglobalimage, environment={'COMMENT': 'codecov.io', 'LCOV_BRANCH_COVERAGE': '0', 'B2_CXXSTD': '17', 'B2_TOOLSET': 'gcc-8', 'B2_DEFINES': 'BOOST_JSON_EXPENSIVE_TESTS BOOST_NO_STRESS_TEST=1', 'CODECOV_TOKEN': {'from_secret': 'codecov_token'}, 'B2_FLAGS': 'warnings=extra warnings-as-errors=on linkflags=-lstdc++fs', 'DRONE_JOB_UUID': '356a192b79'}, globalenv=globalenv), linux_cxx("Valgrind", "clang++-14", packages="clang-14 libc6-dbg libc++-dev libstdc++-9-dev", llvm_os="jammy", llvm_ver="14", buildscript="drone", buildtype="valgrind", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'valgrind', 'B2_TOOLSET': 'clang-14', 'B2_CXXSTD': '11,14,17', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'B2_VARIANT': 'debug', 'B2_TESTFLAGS': 'testing.launcher=valgrind', 'VALGRIND_OPTS': '--error-exitcode=1'}, globalenv=globalenv), linux_cxx("ASan GCC", "g++-12", packages="g++-12", buildscript="drone", buildtype="boost", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'asan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-12', 'B2_CXXSTD': '11,14,17', 'B2_ASAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1', 'DRONE_EXTRA_PRIVILEGED': 'True'}, globalenv=globalenv, privileged=True), diff --git a/.drone/drone.sh b/.drone/drone.sh index d373b096f..255eb531f 100755 --- a/.drone/drone.sh +++ b/.drone/drone.sh @@ -103,36 +103,20 @@ elif [ "$DRONE_JOB_BUILDTYPE" == "docs" ]; then echo '==================================> INSTALL' -export SELF=`basename $REPO_NAME` - -pwd -cd .. -mkdir -p $HOME/cache && cd $HOME/cache -if [ ! -d doxygen ]; then git clone -b 'Release_1_8_15' --depth 1 https://github.com/doxygen/doxygen.git && echo "not-cached" ; else echo "cached" ; fi -cd doxygen -cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release -cd build -sudo make install -cd ../.. -cd .. -BOOST_BRANCH=develop && [ "$TRAVIS_BRANCH" == "master" ] && BOOST_BRANCH=master || true -git clone -b $BOOST_BRANCH https://github.com/boostorg/boost.git boost-root --depth 1 -cd boost-root -export BOOST_ROOT=$(pwd) -git submodule update --init libs/context -git submodule update --init tools/boostbook -git submodule update --init tools/boostdep +common_install git submodule update --init tools/docca -git submodule update --init tools/quickbook -rsync -av $TRAVIS_BUILD_DIR/ libs/$SELF -python tools/boostdep/depinst/depinst.py ../tools/quickbook -./bootstrap.sh -./b2 headers +git submodule update --init tools/boostlook echo '==================================> SCRIPT' -echo "using doxygen ; using boostbook ; using python : : python3 ;" > tools/build/src/user-config.jam -./b2 -j3 libs/$SELF/doc//boostrelease +cat >$BOOST_ROOT/tools/build/src/user-config.jam <>. +* {issue}644[#644] Add GDB pretty printers for Boost.JSON types. + +.Fixes +* {issue}1057[#1057] Use correct 64bit full multiplication for MinGW on ARM64. +* {issue}1048[#1048] Fix parse_into handling of tuple of the wrong size. +* {issue}1047[#1047] Check for input size larger than allowed size of sequence. +* {issue}1034[#1034] Fix `value_ref` segfaulting on GCC 14. + +== Boost 1.86.0 + +.Deprecation +* Support for GCC versions older than version 5.0 is deprecated and **will + stop in Boost 1.88.0**. + +.API Changes +* {issue}941[#941] `source_location` parameter was added to throwing accessor + functions. + +.New Features +* {issue}940[#940] Parse option to tolerate invalid UTF-16 surrogate pairs, + and produce https://simonsapin.github.io/wtf-8/[WTF-8]. +* {issue}941[#941] Added accessor functions that return `system::result`. + +.Fixes +* Handle missing error case in direct parsing. + +== Boost 1.85.0 + +.New Dependencies +* {issue}915[#915] Boost.Endian is now used to deal with endianness. + +.API Changes +* {issue}881[#881] Aliases to Boost.System and Boost.Container components are + deprecated and **will be completely removed in 1.87.0**. + +.New Features +* {issue}979[#979] Conversion of described classes supports private members +* {issue}979[#979] Rvalue reference overload for `visit`. +* {issue}977[#977] Add conversion support for path-like types. + +.Fixes +* {issue}988[#988] Parsing into described classes correctly considers inherited + members. +* {issue}975[#975] Conversion of self-referential sequences is disabled. +* {issue}952[#952] Fixed reference handling in visit. + +== Boost 1.84.0 + +.API Changes +* {issue}925[#925] Add a conversion category for variants. +* {issue}833[#833] Add a conversion category for optionals. +* {issue}840[#840] Relax iterator requirements for constructors from iterator + pairs. + +.New Features +* {issue}627[#627] Parsing directly into user types. See + <>. + +.Fixes +* {issue}933[#933] Fix reading beyond input buffer. +* {issue}920[#920] Fix inconsistent choice of init list constructor. +* Documentation improvements. + +== Boost 1.83.0 + +.API Changes +* {issue}859[#859] The library now only throws {ref_system_error}, except for + when allocation failed, in which case `std::bad_alloc` is thrown. +* {issue}884[#884] Serialization behavior can now be changed by + <>. + +.New Features +* {issue}819[#819] <>. +* {issue}599[#599] <> for more precise + number parsing. +* {issue}885[#885] Support <> in stream + `operator<<`. +* {issue}397[#397] <> to allow `Infinity` and + `NaN` JSON literals. +* {issue}901[#901] <> that only validates + numbers rather than parsing them. +* {issue}892[#892] Numbers with exponent larger than `INT_MAX` are accepted by + the parser and treated as infinity. + +.Fixes +* {issue}901[#901] Fix `object` member functions that should provide strong + guarantee. +* {issue}887[#887] Fix ambiguity of `end` call when `boost/range.hpp` is + included. +* {issue}902[#902] Fix ASan failures. +* {issue}904[#904] Fix error message for `error::size_mismatch`. +* Fix conversion into tuple with const elements. + +== Boost 1.82.0 + +.New Features +* {issue}800[#800] <>. +* {issue}570[#570] <>. + +.Improvements +* {issue}848[#848] <> . +* {issue}807[#807] `value_to` supports missing elements for `std::optional`. +* Documentation improvements. + +.Fixes +* {issue}876[#876] Fix parser suspend inside an escape character. +* {issue}814[#814] Make sentinel() return a unique pointer. + +== Boost 1.81.0 + +.API Changes +* {issue}686[#686] Conversion traits were redesigned. +* {issue}756[#756] Removed `condition::assign_error`. +* {issue}758[#758] Removed `generic_category` alias. + +.New Features + +* {issue}749[#749] `object::stable_erase`. +* {issue}778[#778] Added error condition for generic library errors. +* {issue}619[#619] Added `parse` overload for `std::istream`. +* {issue}619[#619] `operator>>` for `value`. + +.Improvements +* {issue}686[#686] Null-like type conversion support (including + `std::nullptr_t`). +* {issue}736[#736] Non-throwing conversion from `value` to user types. +* {issue}677[#677] `value_to/from` supports `std::optional` and + `std::nullopt_t`. +* {issue}517[#517] `value_to/from` supports `std::variant` and `std::monotype`. +* {issue}626[#626] `value_to/from` supports supports described classes + and enums. +* {issue}757[#757] Rvalue ref-qualified accessors for `value`. + +.Fixes +* {issue}745[#745] Support for self-swap and self-move in `string`. +* {issue}747[#747] Support for self-swap and self-move in `array`. +* {issue}735[#735] Replaced C floating point constants with C++ equivalents. +* Documentation improvements. + +== Boost 1.80.0 + +.API Changes +* {issue}703[#703] Add non-const `value::at` overloads. +* {issue}717[#717] Add the ability to manually choose endianness of the + platform. +* Add `string::subview()` overload. + +.Fixes +* {issue}692[#692] Fix segfault in `array::erase(it)`. +* {issue}697[#697] Fix low performance of `serialize` on libc{pp}. +* {issue}708[#708] Fix ambiguous conversion to `std::string_view` on GCC 8. +* {issue}717[#717] Fix parsing on big-endian platforms. +* {issue}726[#726] Fix handling of comment after trailing comma. +* Minor documentation fixes. + +== Boost 1.79.0 + +.API Changes +* {issue}650[#650] Standalone mode of the library is removed. Users who wish to + continue using standalone JSON can switch to + https://github.com/CPPAlliance/standalone-json.git[the C++ Alliance fork]. + +.New Features +* {issue}480[#480] Add support for JSON Pointer. + +.Improvements +* Add `std::error_code` overloads. +* {issue}680[#680] Add `boost::source_location` to `error_codes`. + +.Fixes +* {issue}668[#668] Naturally grow string during serialization. + +== Boost 1.78.0 + +.API Changes +* {issue}628[#628] Standalone mode of the library is removed. + +== Boost 1.78.0 + +.API Changes +* {issue}628[#628] Standalone mode of the library is deprecated. + +.New Features +* {issue}549[#549] {issue}550[#550] Allow external libraries to forward declare + <> and <>. + +.Fixes +* {issue}608[#608] {issue}612[#612] Fixed signed integer overflow in number + parsing. +* {issue}620[#620] Documentation fixes. + +.Improvements +* {issue}557[#557] Add support for `/Zc:implicitNoexcept-` on MSVC. + +== Boost 1.77.0 + +.New Features +* {issue}538[#538] <>. +* {issue}521[#521] {std_hash} specializations for JSON types. + +.Fixes +* <> deallocates the correct size. +* Fixed crash when constructing <> from a pair of iterators + that form an empty range. +* <> allocates with the correct alignment. + +.Improvements +* <> supports `TupleLike` types. +* <> and <> support + {std_array} and similar types. + +== Boost 1.76.0 + +.Fixes +* {issue}481[#481] Refactored <> implementation; + user customizations are now always preferred over library-provided overloads. +* {issue}484[#484] Fixed imprecise parsing for some floating point numbers. +* {issue}485[#485] Fixed link errors in standalone mode, when used alongside + Boost. +* {issue}497[#497] Fix Boost.Build builds on GCC 4.8. + +== Boost 1.75.0 + +* Initial release. diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index f83aa1486..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,145 +0,0 @@ -Boost 1.87.0 - -* Conversion into structs ignores unknown keys. -* Exception wrapping behaviour for `value_to` is simplified. -* Deprecated initilaizer list behavior was removed. -* Deprecated type aliases were removed. -* Direct serialization. -* Add GDB pretty printers for Boost.JSON types. -* Use correct 64bit full multiplication for MinGW on ARM64. -* Fix parse_into handling of tuple of the wrong size. -* Check for input size larger than allowed size of sequence. -* Fix `value_ref` segfaulting on GCC 14. - -Boost 1.86.0 - -* Support for GCC versions older than version 5.0 is deprecated and **will stop - in Boost 1.88.0**. -* `source_location` parameter was added to throwing accessor functions. -* Parse option to tolerate invalid UTF-16 surrogate pairs, and - produce [WTF-8](https://simonsapin.github.io/wtf-8/). -* Added accessor functions that return `system::result`. -* Handle missing error case in direct parsing. - -Boost 1.85.0 - -* Boost.Endian is now used to deal with endianness. -* Aliases to Boost.System and Boost.Container components are deprecated and - will be completely removed in 1.87.0. -* Conversion of described classes supports private members -* Rvalue reference overload for `visit`. -* Add conversion support for path-like types. -* Parsing into described classes correctly considers inherited members. -* Conversion of self-referential sequences is disabled. -* Fixed reference handling in visit. - -Boost 1.84.0 - -* Add a conversion category for variants. -* Add a conversion category for optionals. -* Relax iterator requirements for constructors from iterator pairs. -* Parsing directly into user types. -* Fix reading beyond input buffer. -* Fix inconsistent choice of init list constructor. - -Boost 1.83.0 - -* The library now only throws `system_error`, except for when allocation - failed, in which case `std::bad_alloc` is thrown. -* Serialization behavior can now be changed by `serialize_options`. -* Contextual conversions. -* Parser option for more precise number parsing. -* Support `parse_options` in stream `operator<<`. -* Parser option to allow `Infinity` and `NaN` JSON literals. -* Parser mode that only validates numbers rather than parsing them. -* Numbers with exponent larger than `INT_MAX` are accepted by the parser and - treated as infinity. -* Fix `object` member functions that should provide strong guarantee. -* Fix ambiguity of `end` call when `boost/range.hpp` is included. -* Fix ASan failures. -* Fix error message for `error::size_mismatch`. -* Fix conversion into tuple with const elements. - -Boost 1.82.0 - -* `set_at_pointer`. -* `boost::hash` support. -* Caller-provided serializer storage. -* `value_to` supports missing elements for `std::optional`. -* Fix parser suspend inside an escape character. -* Make sentinel() return a unique pointer. -* Documentation improvements. - -Boost 1.81.0 - -* Conversion traits were redesigned. -* Removed `condition::assign_error`. -* Removed `generic_category` alias. -* `object::stable_erase`. -* Added error condition for generic library errors. -* Added `parse` overload for `std::istream`. -* `operator>>` for `value`. -* Null-like type conversion support (including `std::nullptr_t`). -* Non-throwing conversion from `value` to user types. -* `value_to/from` supports `std::optional` and `std::nullopt_t`. -* `value_to/from` supports `std::variant` and `std::monotype`. -* `value_to/from` supports supports described classes and enums. -* Rvalue ref-qualified accessors for `value`. -* Support for self-swap and self-move in `string`. -* Support for self-swap and self-move in `array`. -* Replaced C floating point constants with C++ equivalents. -* Documentation improvements. - -Boost 1.80.0 - -* Add non-const `value::at` overloads. -* Add the ability to manually choose endianness of the platform. -* Add `string::subview()` overload. -* Fix segfault in `array::erase(it)`. -* Fix low performance of `serialize` on libc++. -* Fix ambiguous conversion to `std::string_view` on GCC 8. -* Fix parsing on big-endian platforms. -* Fix handling of comment after trailing comma. -* Minor documentation fixes. - -Boost 1.79.0 - -* Standalone mode of the library is removed. Users who wish to - continue using standalone JSON can switch to - [the C++ Alliance fork](https://github.com/CPPAlliance/standalone-json.git). -* Add support for JSON Pointer. -* Add `std::error_code` overloads. -* Add `boost::source_location` to `error_codes`. -* Naturally grow string during serialization. - -Boost 1.78.0 -* Standalone mode of the library is deprecated. -* Allow external libraries to forward declare `value_to` and `value_from`. -* Fixed signed integer overflow in number parsing. -* Documentation fixes. -* Add support for `/Zc:implicitNoexcept-` on MSVC. - -Boost 1.77.0: - -* Implicit conversion operator from `string` to `std::string_view`. -* `value_to` supports `TupleLike` types. -* `value_to` and `value_from` support `std::array` and similar types. -* `object` deallocates the correct size. -* Fixed crash when constructing `array` from a pair of iterators that form an - empty range. -* `key_value_pair` allocates with the correct alignment. -* `std::hash` specializations for json types. - -Boost 1.76.0: - -* Refactored `value_from` implementation; user customizations are now always - preferred over library-provided overloads. -* Fixed imprecise parsing for some floating point numbers. -* Fixed link errors in standalone mode, when used alongside Boost. -* Fix Boost.Build builds on GCC 4.8. - --------------------------------------------------------------------------------- - -Boost 1.75.0: - -Initial release. diff --git a/README.adoc b/README.adoc new file mode 100644 index 000000000..b5e45cac3 --- /dev/null +++ b/README.adoc @@ -0,0 +1,258 @@ +ifdef::env-github[] + +:tip-caption: :bulb: +:note-caption: :information_source: +:important-caption: :heavy_exclamation_mark: +:caution-caption: :fire: +:warning-caption: :warning: +:source-highlighter: rouge +:source-language: c++ + +[link=https://www.boost.org/doc/libs/release/libs/json] +image:https://raw.githubusercontent.com/CPPAlliance/json/master/doc/images/repo-logo-3.png[Boost.JSON] + +[cols=3] +|=== +|Branch +|https://github.com/boostorg/json/tree/master[`master`] +|https://github.com/boostorg/json/tree/develop[`develop`] + +|https://azure.microsoft.com/en-us/services/devops/pipelines/[Azure] +a| +[link=https://vinniefalco.visualstudio.com/json/_build/latest?definitionId=5&branchName=master] +image::https://img.shields.io/azure-devops/build/vinniefalco/2571d415-8cc8-4120-a762-c03a8eda0659/8/master[Build Status] +a| +[link=https://vinniefalco.visualstudio.com/json/_build/latest?definitionId=8&branchName=develop] +image::https://img.shields.io/azure-devops/build/vinniefalco/2571d415-8cc8-4120-a762-c03a8eda0659/8/develop[Build Status] + +|Docs +a| +[link=https://www.boost.org/doc/libs/master/libs/json/] +image::https://img.shields.io/badge/docs-master-brightgreen.svg[Documentation] +a| +[link=https://www.boost.org/doc/libs/develop/libs/json/] +image::https://img.shields.io/badge/docs-develop-brightgreen.svg[Documentation] + +|https://drone.io/[Drone] +a| +[link=https://drone.cpp.al/boostorg/json] +image::https://drone.cpp.al/api/badges/boostorg/json/status.svg?ref=refs/heads/master[Build Status] +a| +[link=https://drone.cpp.al/boostorg/json] +image::https://drone.cpp.al/api/badges/boostorg/json/status.svg?ref=refs/heads/develop[Build Status] + +|Matrix +a| +[link=http://www.boost.org/development/tests/master/developer/json.html] +image::https://img.shields.io/badge/matrix-master-brightgreen.svg[Matrix] +a| +[link=https://img.shields.io/badge/matrix-develop-brightgreen.svg] +image::https://img.shields.io/badge/matrix-develop-brightgreen.svg[Matrix] + +|Fuzzing +| --- +a| +[link=https://github.com/boostorg/json/workflows/fuzz/badge.svg?branch=develop] +image::https://github.com/boostorg/json/workflows/fuzz/badge.svg?branch=develop[fuzz] + +|https://ci.appveyor.com/[Appveyor] +a| +[link=https://ci.appveyor.com/project/vinniefalco/cppalliance-json/branch/master] +image::https://ci.appveyor.com/api/projects/status/8csswcnmfm798203?branch=master&svg=true[Build status] +a| +[link=https://ci.appveyor.com/project/vinniefalco/cppalliance-json/branch/develop] +image::https://ci.appveyor.com/api/projects/status/8csswcnmfm798203?branch=develop&svg=true[Build status] + +|https://codecov.io[codecov.io] +a| +[link=https://codecov.io/gh/boostorg/json/branch/master] +image::https://codecov.io/gh/boostorg/json/branch/master/graph/badge.svg[codecov] +a| +[link=https://codecov.io/gh/boostorg/json/branch/develop] +image::https://codecov.io/gh/boostorg/json/branch/develop/graph/badge.svg[codecov] +|=== + += Boost.JSON + +endif::[] + +[pagelevels=0,toclevels=0] +== Overview +Boost.JSON is a portable {cpp} library which provides containers and algorithms +that implement https://json.org/[JavaScript Object Notation], or simply "JSON", +a lightweight data-interchange format. This format is easy for humans to read +and write, and easy for machines to parse and generate. It is based on a subset +of the JavaScript Programming Language +(https://www.ecma-international.org/ecma-262/10.0/index.html[Standard +ECMA-262]), and is currently standardised in +(https://datatracker.ietf.org/doc/html/rfc8259[RFC 8259]). JSON is a text +format that is language-independent but uses conventions that are familiar to +programmers of the C-family of languages, including C, {cpp}, C#, Java, +JavaScript, Perl, Python, and many others. These properties make JSON an ideal +data-interchange language. + +This library focuses on a common and popular use-case: parsing and serializing +to and from a container called `value` which holds JSON types. Any `value` +which you build can be serialized and then deserialized, guaranteeing that the +result will be equal to the original value. Whatever JSON output you produce +with this library will be readable by most common JSON implementations in any +language. + +The `value` container is designed to be well suited as a vocabulary type +appropriate for use in public interfaces and libraries, allowing them to be +composed. The library restricts the representable data types to the ranges +which are almost universally accepted by most JSON implementations, especially +JavaScript. The parser and serializer are both highly performant, meeting or +exceeding the benchmark performance of the best comparable libraries. +Allocators are very well supported. Code which uses these types will be easy to +understand, flexible, and performant. + +Boost.JSON offers these features: + +* Fast compilation +* Require only {cpp}11 +* Fast streaming parser and serializer +* Constant-time key lookup for objects +* Options to allow non-standard JSON +* Easy and safe modern API with allocator support +* Optional header-only, without linking to a library + +ifdef::env-github[] + +Visit https://boost.org/libs/json for complete documentation. + +endif::[] + +=== Requirements + +* Requires only {cpp}11 +* Link to a built static or dynamic Boost library (build instructions can be + found https://www.boost.org/doc/libs/release/more/getting_started/index.html[here]), + or use header-only (see below) +* Additional link to Boost.Container may be required + (as described in its + https://www.boost.org/doc/libs/release/doc/html/container.html#container.intro.introduction_building_container[documentation]) +* Supports `-fno-exceptions`, detected automatically (but read + <> on this page). + +The library relies heavily on these well known C++ types in +its interfaces (henceforth termed _standard types_): + +* `string_view` +* `memory_resource`, `polymorphic_allocator` +* `error_category`, `error_code`, `error_condition`, `system_error` + +==== Header-Only +To use as header-only; that is, to eliminate the requirement to link a program +to a static or dynamic Boost.JSON library, simply place the following line in +exactly one new or existing source file in your project. + +[source] +---- +#include +---- + +MSVC users must also define the macro `BOOST_JSON_NO_LIB` to disable +auto-linking. Note, that if you also want to avoid linking to Boost.Container, +which is a dependency of Boost.JSON, you have to define +`BOOST_CONTAINER_NO_LIB`. In order to disable auto-linking to Boost libraries +completely you can define `BOOST_ALL_NO_LIB` instead. + +==== Disabling Exceptions +In order to support building with exceptions disabled this library uses another +Boost library, +https://www.boost.org/doc/libs/release/libs/throw_exception[Boost.ThrowException]. +This allows to automatically discover whether exception support is available. +On the other hand, as explained in Boost.ThrowException's documentation, if +exceptions are disabled, the users need to provide their own implementation for +`boost::throw_exception`, in order to link their binaries successfully. Here's +a very simple example of such implementation: + +[source] +---- +void throw_exception( const std::exception&, const boost::source_location& ) +{ + std::printf("Exceptions are not supported!"); + std::abort(); +} +---- + +==== Embedded +Boost.JSON works great on embedded devices. The library uses local stack +buffers to increase the performance of some operations. On Intel platforms +these buffers are large (4KB), while on non-Intel platforms they are small (256 +bytes). To adjust the size of the stack buffers for embedded applications +define this macro when building the library or including the function +definitions: + +[source] +---- +#define BOOST_JSON_STACK_BUFFER_SIZE 1024 +#include +---- + +==== Endianness +Boost.JSON uses +https://www.boost.org/doc/libs/release/libs/endian/doc/html/endian.html[Boost.Endian] +in order to support both little endian and big endian platforms. + +==== Supported Compilers +Boost.JSON has been tested with the following compilers: + +* clang: 3.5, 3.6, 3.7, 3.8, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +* gcc: 5, 6, 7, 8, 9, 10, 11, 12 +* msvc: 14.0, 14.1, 14.2, 14.3 + +==== Supported JSON Text +The library expects input text to be encoded using UTF-8, which is +a requirement put on all JSON exchanged between systems by the +(https://datatracker.ietf.org/doc/html/rfc8259#section-8.1[RFC]). Similarly, +the text generated by the library is valid UTF-8. + +The RFC does not allow byte order marks (BOM) to appear in JSON text, so the +library considers BOM syntax errors. + +The library supports several popular JSON extensions. These have to be +explicitly enabled. + +ifdef::env-github[] + +==== Visual Studio Solution + +[source,shell] +---- +cmake -G "Visual Studio 16 2019" -A Win32 -B bin -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/msvc.cmake +cmake -G "Visual Studio 16 2019" -A x64 -B bin64 -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/msvc.cmake +---- + +endif::[] + +=== Quality Assurance +The development infrastructure for the library includes these per-commit +analyses: + +* Coverage reports +* Benchmark performance comparisons +* Compilation and tests on Drone.io, Azure Pipelines, Appveyor +* Fuzzing using clang-llvm and machine learning + +==== Security Review (Bishop Fox) +As part of our commitment to producing the very finest C++ libraries that +application developers can trust, the C++ Alliance has commissioned Bishop Fox +to perform a security audit of the Boost.JSON library. The report is linked +here: + +https://cppalliance.org/pdf/C%20Plus%20Plus%20Alliance%20-%20Boost%20JSON%20Security%20Assessment%202020%20-%20Assessment%20Report%20-%2020210317.pdf[C Plus Plus Alliance - Boost JSON Security Assessment 2020 - Assessment Report - 20210317] + +=== Credits +This library wouldn't be where it is today without the help of +https://github.com/pdimov[Peter Dimov] for design advice and optimization +assistance. + +ifdef::env-github[] + +== License +Distributed under the Boost Software License, Version 1.0. (See accompanying +file link:LICENSE_1_0.txt[LICENSE_1_0.txt] or copy at +https://www.boost.org/LICENSE_1_0.txt). + +endif::[] diff --git a/README.md b/README.md deleted file mode 100644 index 61c705106..000000000 --- a/README.md +++ /dev/null @@ -1,146 +0,0 @@ -[![Boost.JSON](https://raw.githubusercontent.com/CPPAlliance/json/master/doc/images/repo-logo-3.png)](https://www.boost.org/doc/libs/release/libs/json) - -Branch | [`master`](https://github.com/boostorg/json/tree/master) | [`develop`](https://github.com/boostorg/json/tree/develop) | ---------------- | ----------------------------------------------------------- | ------------------------------------------------------------- | -[Azure](https://azure.microsoft.com/en-us/services/devops/pipelines/) | [![Build Status](https://img.shields.io/azure-devops/build/vinniefalco/2571d415-8cc8-4120-a762-c03a8eda0659/8/master)](https://vinniefalco.visualstudio.com/json/_build/latest?definitionId=5&branchName=master) | [![Build Status](https://img.shields.io/azure-devops/build/vinniefalco/2571d415-8cc8-4120-a762-c03a8eda0659/8/develop)](https://vinniefalco.visualstudio.com/json/_build/latest?definitionId=8&branchName=develop) -Docs | [![Documentation](https://img.shields.io/badge/docs-master-brightgreen.svg)](https://www.boost.org/doc/libs/master/libs/json/) | [![Documentation](https://img.shields.io/badge/docs-develop-brightgreen.svg)](https://www.boost.org/doc/libs/develop/libs/json/) -[Drone](https://drone.io/) | [![Build Status](https://drone.cpp.al/api/badges/boostorg/json/status.svg?ref=refs/heads/master)](https://drone.cpp.al/boostorg/json) | [![Build Status](https://drone.cpp.al/api/badges/boostorg/json/status.svg?ref=refs/heads/develop)](https://drone.cpp.al/boostorg/json) -Matrix | [![Matrix](https://img.shields.io/badge/matrix-master-brightgreen.svg)](http://www.boost.org/development/tests/master/developer/json.html) | [![Matrix](https://img.shields.io/badge/matrix-develop-brightgreen.svg)](http://www.boost.org/development/tests/develop/developer/json.html) -Fuzzing | --- | [![fuzz](https://github.com/boostorg/json/workflows/fuzz/badge.svg?branch=develop)](https://github.com/boostorg/json/actions?query=workflow%3Afuzz+branch%3Adevelop) -[Appveyor](https://ci.appveyor.com/) | [![Build status](https://ci.appveyor.com/api/projects/status/8csswcnmfm798203?branch=master&svg=true)](https://ci.appveyor.com/project/vinniefalco/cppalliance-json/branch/master) | [![Build status](https://ci.appveyor.com/api/projects/status/8csswcnmfm798203?branch=develop&svg=true)](https://ci.appveyor.com/project/vinniefalco/cppalliance-json/branch/develop) -[codecov.io](https://codecov.io) | [![codecov](https://codecov.io/gh/boostorg/json/branch/master/graph/badge.svg)](https://codecov.io/gh/boostorg/json/branch/master) | [![codecov](https://codecov.io/gh/boostorg/json/branch/develop/graph/badge.svg)](https://codecov.io/gh/boostorg/json/branch/develop) - -# Boost.JSON - -## Overview - -Boost.JSON is a portable C++ library which provides containers and -algorithms that implement -[JavaScript Object Notation](https://json.org/), or simply "JSON", -a lightweight data-interchange format. This format is easy for humans to -read and write, and easy for machines to parse and generate. It is based -on a subset of the JavaScript Programming Language -([Standard ECMA-262](https://www.ecma-international.org/ecma-262/10.0/index.html)), -and is currently standardised in [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259). -JSON is a text format that is language-independent but uses conventions -that are familiar to programmers of the C-family of languages, including -C, C++, C#, Java, JavaScript, Perl, Python, and many others. These -properties make JSON an ideal data-interchange language. - -This library focuses on a common and popular use-case: parsing -and serializing to and from a container called `value` which -holds JSON types. Any `value` which you build can be serialized -and then deserialized, guaranteeing that the result will be equal -to the original value. Whatever JSON output you produce with this -library will be readable by most common JSON implementations -in any language. - -The `value` container is designed to be well suited as a -vocabulary type appropriate for use in public interfaces and -libraries, allowing them to be composed. The library restricts -the representable data types to the ranges which are almost -universally accepted by most JSON implementations, especially -JavaScript. The parser and serializer are both highly performant, -meeting or exceeding the benchmark performance of the best comparable -libraries. Allocators are very well supported. Code which uses these -types will be easy to understand, flexible, and performant. - -Boost.JSON offers these features: - -* Fast compilation -* Require only C++11 -* Fast streaming parser and serializer -* Constant-time key lookup for objects -* Options to allow non-standard JSON -* Easy and safe modern API with allocator support -* Optional header-only, without linking to a library - -Visit https://boost.org/libs/json for complete documentation. - -## Requirements - -* Requires only C++11 -* Link to a built static or dynamic Boost library, or use header-only (see below) -* Supports -fno-exceptions, detected automatically - -The library relies heavily on these well known C++ types in -its interfaces (henceforth termed _standard types_): - -* `string_view` -* `memory_resource`, `polymorphic_allocator` -* `error_category`, `error_code`, `error_condition`, `system_error` - -### Header-Only - -To use as header-only; that is, to eliminate the requirement to -link a program to a static or dynamic Boost.JSON library, simply -place the following line in exactly one new or existing source -file in your project. -``` -#include -``` - -MSVC users must also define the macro `BOOST_JSON_NO_LIB` to disable -auto-linking. Note, that if you also want to avoid linking to Boost.Container, -which is a dependency of Boost.JSON, you have to define -`BOOST_CONTAINER_NO_LIB`. In order to disable auto-linking to Boost libraries -completely you can define `BOOST_ALL_NO_LIB` instead. - -### Embedded - -Boost.JSON works great on embedded devices. The library uses local -stack buffers to increase the performance of some operations. On -Intel platforms these buffers are large (4KB), while on non-Intel -platforms they are small (256 bytes). To adjust the size of the -stack buffers for embedded applications define this macro when -building the library or including the function definitions: -``` -#define BOOST_JSON_STACK_BUFFER_SIZE 1024 -#include -``` -### Endianness - -Boost.JSON uses [Boost.Endian](https://www.boost.org/doc/libs/release/libs/endian/doc/html/endian.html) -in order to support both little endian and big endian platforms. - -### Supported Compilers - -Boost.JSON has been tested with the following compilers: - -* clang: 3.5, 3.6, 3.7, 3.8, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 -* gcc: 5, 6, 7, 8, 9, 10, 11, 12 -* msvc: 14.0, 14.1, 14.2, 14.3 - -### Supported JSON Text - -The library expects input text to be encoded using UTF-8, which is a -requirement put on all JSON exchanged between systems by the -[RFC](https://datatracker.ietf.org/doc/html/rfc8259#section-8.1). Similarly, -the text generated by the library is valid UTF-8. - -The RFC does not allow byte order marks (BOM) to appear in JSON text, so the -library considers BOM syntax errors. - -The library supports several popular JSON extensions. These have to be -explicitly enabled. - -### Visual Studio Solution - - cmake -G "Visual Studio 16 2019" -A Win32 -B bin -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/msvc.cmake - cmake -G "Visual Studio 16 2019" -A x64 -B bin64 -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/msvc.cmake - -## Quality Assurance - -The development infrastructure for the library includes -these per-commit analyses: - -* Coverage reports -* Benchmark performance comparisons -* Compilation and tests on Drone.io, Azure Pipelines, Appveyor -* Fuzzing using clang-llvm and machine learning - -## License - -Distributed under the Boost Software License, Version 1.0. -(See accompanying file [LICENSE_1_0.txt](LICENSE_1_0.txt) or copy at -https://www.boost.org/LICENSE_1_0.txt) diff --git a/doc/Jamfile b/doc/Jamfile index da8dc0088..f79d11ed3 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -8,17 +8,28 @@ # Official repository: https://github.com/boostorg/json # -project json/doc ; +project json/doc + : default-build + on + ; import-search /boost/docca ; +using asciidoctor ; -import boostbook ; +import common ; import docca ; +import feature ; import path ; +import property-set ; -local include-prefix = [ path.root $(__file__:D) [ path.pwd ] ] ; -include-prefix = [ path.native $(include-prefix:D)/include ] ; -docca.pyreference reference.qbk +if ! ( htmldir in [ feature.values asciidoctor-backend ] ) +{ + feature.extend asciidoctor-backend : htmldir ; +} + +local here-dir = [ path.root $(__file__:D) [ path.pwd ] ] ; +local include-prefix = [ path.native $(here-dir:D)/include ] ; +docca.pyreference entities.adoc : [ glob-tree-ex ../include/boost/json : *.hpp *.ipp : detail impl ] externals.hpp : ALIASES="esafe=\"@par Exception Safety\"" @@ -39,54 +50,54 @@ docca.pyreference reference.qbk EXTRACT_LOCAL_CLASSES=NO STRIP_FROM_PATH=$(include-prefix) + reference.adoc.jinja2 config.json ; -#------------------------------------------------------------------------------- -# -# Produce the Boost.Book XML from the QuickBook -# +generate with-path : entities.adoc : @with-path ; install images - : - [ glob images/*.png ] - : - html/json/images + : [ glob images/*.png ] + : html/json/images ; - explicit images ; -xml json_doc - : - qbk/main.qbk - : - reference.qbk - images - ; - -explicit json_doc ; - #------------------------------------------------------------------------------- # # HTML documentation for $(BOOST_ROOT)/doc/html # #------------------------------------------------------------------------------- -boostbook json - : - json_doc - : - boost.root=../../../.. - chapter.autolabel=1 - chunk.section.depth=8 # Depth to which sections should be chunked - chunk.first.sections=1 # Chunk the first top-level section? - toc.section.depth=8 # How deep should recursive sections appear in the TOC? - toc.max.depth=8 # How many levels should be created for each TOC? - generate.section.toc.level=8 # Control depth of TOC generation in sections - generate.toc="chapter toc,title section nop reference nop" - ../../../tools/boostbook/dtd - : - images +local backend-module = [ path.root htmldir.rb $(here-dir) ] ; +html index + : pages/main.adoc + : html + images + with-path + + /boost/boostlook//boostlook + + nofooter + nofootnotes + pagelevels=2 + icons=font + project-dir=$(here-dir:D) + "project-gh=https://github.com/boostorg/json/edit/develop" + + docinfo=shared-head + pages/docinfo.html + + htmldir + htmldir.rb + "-r $(backend-module)" + + attribute-missing=warn + -v + --trace + all:-w + extra:-w + pedantic:-w + on:--failure-level=WARN ; #------------------------------------------------------------------------------- @@ -98,5 +109,23 @@ boostbook json alias boostdoc ; explicit boostdoc ; -alias boostrelease : json ; +alias boostrelease : index ; explicit boostrelease ; + + +#------------------------------------------------------------------------------- +# +# helpers +# + +rule with-path ( project name : ps : src ) +{ + local pwd = [ path.pwd ] ; + local here = [ path.root $(__file__:D) $(pwd) ] ; + + local path = [ path.root [ $(src).name ] [ $(src).path ] ] ; + path = [ path.root $(path) $(pwd) ] ; + path = [ path.relative-to $(here)/pages $(path) ] ; + return [ $(ps).add-raw entities-file=$(path) ] + $(src) ; +} diff --git a/doc/config.json b/doc/config.json index 080e4eb4b..53fac99ab 100644 --- a/doc/config.json +++ b/doc/config.json @@ -1,10 +1,10 @@ { "include_private": false , "legacy_behavior": false , "external_marker": "!EXTERNAL!" -, "link_prefix": "json.ref." +, "link_prefix": "ref_" , "default_namespace": "boost::json" , "convenience_header": "boost/json.hpp" , "replace_strings": - { "__see_below__": "``['see-below]``" + { "__see_below__": "pass:q,a[__see{nbsp}below__]" } } diff --git a/doc/externals.hpp b/doc/externals.hpp index 5b0e48c9a..2f686a46c 100644 --- a/doc/externals.hpp +++ b/doc/externals.hpp @@ -13,13 +13,13 @@ namespace pmr { /// !EXTERNAL! /// -/// @see https://www.boost.org/doc/libs/release/doc/html/boost/container/pmr/polymorphic_allocator.html +/// @see https://www.boost.org/doc/libs/release/doc/html/doxygen/boost_container_header_reference/classboost_1_1container_1_1pmr_1_1polymorphic__allocator.html template struct polymorphic_allocator {}; /// !EXTERNAL! /// -/// @see https://www.boost.org/doc/libs/1_85_0/doc/html/boost/container/pmr/memory_resource.html +/// @see https://www.boost.org/doc/libs/release/doc/html/doxygen/boost_container_header_reference/classboost_1_1container_1_1pmr_1_1memory__resource.html struct memory_resource {}; } // namespace pmr diff --git a/doc/htmldir.rb b/doc/htmldir.rb new file mode 100644 index 000000000..f6a6bd90a --- /dev/null +++ b/doc/htmldir.rb @@ -0,0 +1,458 @@ +# coding: utf-8 + +require 'asciidoctor' +require 'asciidoctor/converter/html5' +require 'pathname' + +class Asciidoctor::AbstractBlock + attr_accessor :real_level + + def document_dirname + if @document.upper_doc && (@document.upper_doc != @document.top_doc) + base = @document.upper_doc.document_filepath() + base.delete_suffix @document.attributes['outfilesuffix'] + else + "." + end + end + + def document_filename + base = @document.id + if @document.upper_doc && (@document.upper_doc != @document.top_doc) + base = base.delete_prefix "#{document.upper_doc.id}_" + end + base + @document.attributes['outfilesuffix'] + end + + def document_filepath + suffix = @document.attributes['outfilesuffix'] + File.join(document_dirname, @document.document_filename).delete_prefix( + ".#{File::SEPARATOR}") + end + + def filepath_to other + self_dir = Pathname.new document_dirname + other_dir = Pathname.new other.document_dirname + ret = File.join( + other_dir.relative_path_from(self_dir), + other.document_filename).delete_prefix(".#{File::SEPARATOR}") + ret + end +end + +class Asciidoctor::Document + attr_accessor :parsed + attr_accessor :processed + attr_accessor :catalog + attr_accessor :child_documents + attr_accessor :top_doc + attr_accessor :upper_doc + attr_accessor :next_doc + attr_accessor :prev_doc + + alias core_init initialize + def initialize data = nil, options = {} + options[:sourcemap] = true + core_init data, options + @child_documents = [] + end + + def content # this is different from the reguar Document.content + super + end + + alias core_docinfo docinfo + def docinfo location = :head, suffix = nil + result = core_docinfo location, suffix + if ((location == :footer) && + (attr? 'source-location', nil, true) && + (attr? 'project-gh', nil, true)) + + result += '' + end + if [:header, :footer].include?(location) + result += '' + end + result + end + + def link_to_target(refid, target = "##{refid}") + doc = @catalog[:refs][refid] + until !doc || (doc.context == :document) + doc = doc.parent + end + if !doc || (doc.id == @id) + target + elsif "##{doc.id}" == target + @document.filepath_to doc + else + "#{@document.filepath_to doc}#{target}" + end + end + + def nav_link_html(target, role) + text = target.captioned_title + title = target.document.doctitle sanitize: true + text = %(#{text}) + link = link_to_target target.id + link = %(#{text}) + %(
  • #{link}
  • ) + end + + def spawn_child_documents + pagelevels = attr('pagelevels', 0).to_i + if pagelevels < 0 + logger.warn "attribute 'pagelevels' must be >= 0; setting to 0" + pagelevels = 0 + end + set_attribute('pagelevels', pagelevels.to_s) + + @id = File.basename( + @attributes['outfile'] , @attributes['outfilesuffix']) + register( + :refs, + [@id, + Asciidoctor::Inline.new( + parent = self, + context = :anchor, + text = doctitle, + opts = {:type => :ref, :id => @id}), + doctitle]) + + @child_documents = generate_child_docs self, pagelevels + @real_level = 0 + + if sections.empty? + attrs = @attributes.clone + @blocks << Asciidoctor::Section.new( + self, + 1, + false, + {:attributes => attrs}) + end + reindex_sections + update_source_location + + @top_doc = self + make_linear_connections + update_real_level self, 0 + + @processed = true + end + + def generate_child_docs(node, doc_pagelevels) + result = [] + node.blocks.filter! do |block| + if block.context != :section + next true + end + + node.document.playback_attributes block.attributes + pagelevels = block.attr('pagelevels', doc_pagelevels).to_i + if block.level > pagelevels || !block.id + next true + end + + result << node.document.spawn_document_from(block, doc_pagelevels) + false + end + result + end + + def spawn_document_from(node, doc_pagelevels) + pagelevels = node.attr('pagelevels', doc_pagelevels).to_i + + attrs = @attributes.clone + attrs['title'] = node.title + attrs['notitle'] = true + attrs['authors'] = nil + attrs['author'] = nil + attrs['pagelevels'] = doc_pagelevels + doc = Asciidoctor::Document.new( + [], + {:attributes => attrs, + :doctype => self.doctype, + :header_footer => !self.attr?(:embedded), + :safe => self.safe}) + doc.id = node.id + doc.catalog = @catalog + doc.child_documents = generate_child_docs(node, pagelevels) + doc.blocks << node + doc.finalize_header({}) + reparent_blocks(node, doc) + doc.real_level = node.real_level + doc.reindex_sections + doc.update_source_location + doc.parsed = true + doc.processed = true + doc + end + + def update_real_level(node, real_level) + node.real_level = real_level + + if node.context == :dlist + node.find_by(context: :list_item).each do |block| + update_real_level block, real_level + 1 + end + else + node.blocks.each do |block| + update_real_level block, real_level + 1 + end + end + end + + def reparent_blocks(node, parent, real_level = node.level) + node.parent = parent + node.real_level = real_level + node.level = node.level - real_level + 1 + + if node.context == :dlist + node.find_by(context: :list_item).each do |block| + reparent_blocks(block, node, real_level + 1) + end + return + end + + node.blocks.each do |block| + reparent_blocks(block, node, real_level + 1) + if block.context == :table + block.columns.each do |col| + col.parent = col.parent + end + block.rows.body.each do |row| + row.each do |cell| + cell.parent = cell.parent + end + end + end + end + end + + def make_linear_connections + prev = self + child_documents.each do |doc| + doc.top_doc = @top_doc + doc.upper_doc = self + doc.prev_doc = prev + prev.next_doc = doc + prev = doc.make_linear_connections + end + prev + end + + def update_source_location + if (first_section.attr? 'source-location', nil, true) + set_attribute( + 'source-location', first_section.attr('source-location')) + return + end + + if !first_section.source_location + return + end + + path = Pathname.new first_section.source_location.file + if (attr? 'project-dir', nil, true) + path = path.relative_path_from(attr 'project-dir') + end + + set_attribute('source-location', path.to_s) + end +end + +class HtmlDirConverter < Asciidoctor::Converter::Html5Converter + include Asciidoctor + include Asciidoctor::Converter + include Asciidoctor::Writer + + register_for 'htmldir' + + attr_accessor :home_doc + + def convert_document(node) + if !node.processed + @home_doc = node + node.spawn_child_documents + end + + return super + end + + def convert_outline(node, opts = {}) + convert_outline_doc node.document, node.top_doc, opts + end + + def convert_outline_doc(doc, node, opts) + toclevels = ((node.first_section.attr 'toclevels') || + opts[:toclevels] || (node.document.attr 'toclevels', 2)).to_i + return unless node.real_level < toclevels + + opts[:toclevels] = toclevels + if node.first_section.id == node.id + sections = node.first_section.sections + else + sections = node.sections + end + children = sections + node.child_documents + convert_outline_ext doc, children, node.real_level + 1, opts + end + + def convert_outline_sect(doc, node, opts) + toclevels = ((node.attr 'toclevels').to_i || opts[:toclevels] || 2) + return unless node.real_level < toclevels + + opts[:toclevels] = toclevels + convert_outline_ext doc, node.sections, node.real_level + 1, opts + end + + def convert_outline_ext(doc, nodes, level, opts) + return unless !nodes.empty? + + sectnumlevels = ( + opts[:sectnumlevels] || + (nodes[0].document.attributes['sectnumlevels'] || 3).to_i) + + result = [%(
      )] + nodes.each do |node| + result << list_item_for_node(doc, node, sectnumlevels) + if node.context == :document + child_toc_level = convert_outline_doc doc, node, opts + else + child_toc_level = convert_outline_sect doc, node, opts + end + result << child_toc_level if child_toc_level + end + result << '
    ' + result.join LF + end + + def list_item_for_node(doc, node, sectnumlevels) + if node.context == :document + section = node.sections[0] + else + section = node + end + + slevel = section.real_level + signifier = nil + sectnum = nil + if !section.caption && ( + section.numbered && slevel <= sectnumlevels) + if slevel < 2 && node.document.doctype == 'book' + case section.sectname + when 'chapter' + signifier = ( + node.document.attributes['chapter-signifier']) + sectnum = section.sectnum + when 'part' + signifier = ( + node.document.attributes['part-signifier']) + sectnum = section.sectnum nil, ':' + else + sectnum = section.sectnum + end + end + end + signifier = signifier ? "#{signifier} " : '' + sectnum = sectnum ? "#{sectnum} " : '' + stitle = %(#{signifier}#{sectnum}#{section.title}) + stitle = stitle.gsub DropAnchorRx, '' if stitle.include? '#{stitle}) + end + + def convert_inline_anchor(node) + if node.type != :xref + return super + end + + if (path = node.attributes['path']) + attrs = append_link_constraint_attrs( + node, node.role ? [%( class="#{node.role}")] : []) + attrs = attrs.join + text = node.text || path + else + attrs = node.role ? %( class="#{node.role}") : '' + unless (text = node.text) + ref = (@refs ||= node.document.catalog[:refs])[ + refid = node.attributes['refid']] || + (refid.nil_or_empty? ? (top = get_root_document node) : nil) + if AbstractNode === ref + if (@resolving_xref ||= (outer = true)) && outer + if (text = ref.xreftext node.attr 'xrefstyle', nil, true) + text = text.gsub DropAnchorRx, '' if text.include? '#{text}) + end + + def write output, target + File.write target, output, mode: Asciidoctor::FILE_WRITE_MODE + write_children( + @home_doc, File.dirname(target), File.extname(target)) + end + + def write_children(doc, outdir, ext) + doc.child_documents.each do |child| + outfile = File.join(outdir, child.document_filepath) + + outfiledir = File.dirname(outfile) + FileUtils.mkdir_p(outfiledir) unless File.exist?(outfiledir) + + File.open(outfile, 'w') do |f| + logger.info %(Writing to '#{outfile}') + f.write(child.convert) + end + write_children(child, outdir, ext) + end + end +end diff --git a/doc/pages/allocators/background.adoc b/doc/pages/allocators/background.adoc new file mode 100644 index 000000000..cdca95e84 --- /dev/null +++ b/doc/pages/allocators/background.adoc @@ -0,0 +1,123 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Background +The first version of allocators in C++ defined the named requirement +{req_Allocator}, and made each standard container a class template +parameterized on the allocator type. For example, here is the declaration for +{std_vector}: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_1,indent=0] +---- + +The standard allocator is {req_DefaultConstructible}. To support stateful +allocators, containers provide additional constructor overloads taking +an allocator instance parameter. + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_2,indent=0] +---- + +While the system works, it has some usability problems: + +* The container must be a class template. +* Parameterizing the allocator on the element type is clumsy. +* The system of allocator traits, especially POCCA and POCMA, + is complicated and error-prone. + +Allocator-based programs which use multiple allocator types incur a greater +number of function template instantiations and are generally slower to compile +because class template function definitions must be visible at all call sites. + +== Polymorphic Allocators + +{cpp}17 improves the allocator model by representing the low-level allocation +operation with an abstract interface called {ref_memory_resource}, which is not +parameterized on the element type, and has no traits: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_3,indent=0] +---- + +The class template {ref_polymorphic_allocator} wraps a {ref_memory_resource} +pointer and meets the requirements of {req_Allocator}, allowing it to be used +where an allocator is expected. The standard provides type aliases using the +polymorphic allocator for standard containers: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_4,indent=0] +---- + +A polymorphic allocator constructs with a pointer to a memory resource: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_5,indent=0] +---- + +The memory resource is passed by pointer; ownership is not transferred. The +caller is responsible for extending the lifetime of the memory resource until +the last container which is using it goes out of scope, otherwise the behavior +is undefined. Sometimes this is the correct model, such as in this example +which uses a monotonic resource constructed from a local stack buffer: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_6,indent=0] +---- + +However, sometimes shared ownership is needed. Specifically, that the lifetime +extension of the memory resource should be automatic. For example, if a library +wants to return a container which owns an instance of the library's custom +memory resource as shown below: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_7,indent=0] +---- + +This can be worked around by declaring the container to use a custom allocator +(perhaps using a `std::shared_ptr< std::pmr::memory_resource >` as a data +member). This hinders library composition; every library now exports unique, +incompatible container types. A raw memory resource pointer is also easy to +misuse: + +[source] +---- +include::../../../test/doc_background.cpp[tag=doc_background_8,indent=0] +---- + +Workarounds for this problem are worse than the problem itself. The library +could return a pair with the vector and +`std::unique_ptr` which the caller must manage. Or +the library could change its function signatures to accept +a {ref_memory_resource}``*`` provided by the caller, where the library also +makes public the desired memory resources (`my_resource` above). + +== Problem Statement + +We would like an allocator model using a single type `T` with the +following properties: + +* `T` is not a class template +* `T` references a {ref_memory_resource} +* `T` supports both reference semantics or shared ownership +* `T` interoperates with code already using `std::pmr` + +Boost.JSON solves this problem by introducing a new smart pointer called +<> which builds upon {cpp}17's memory allocation interfaces, +accomplishing the goals above. As a result, libraries which use this type +compose more easily and enjoy faster compilation, as member functions for +containers which use the type can be defined out-of-line. diff --git a/doc/pages/allocators/overview.adoc b/doc/pages/allocators/overview.adoc new file mode 100644 index 000000000..263efc861 --- /dev/null +++ b/doc/pages/allocators/overview.adoc @@ -0,0 +1,25 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Allocators + +Here we discuss the various allocator models used in the C++ standard, followed +by an explanation of the model used in this library and its benefits. Finally +we discuss how the library interoperates with existing code that uses +polymorphic allocators. + +:leveloffset: +1 + +include::background.adoc[] +include::storage_ptr.adoc[] +include::uses_allocator.adoc[] + +:leveloffset: -1 diff --git a/doc/pages/allocators/storage_ptr.adoc b/doc/pages/allocators/storage_ptr.adoc new file mode 100644 index 000000000..78139860f --- /dev/null +++ b/doc/pages/allocators/storage_ptr.adoc @@ -0,0 +1,222 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += `storage_ptr` +Variable-length containers in this library all use dynamically allocated memory +to store their contents. Callers can gain control over the strategy used for +allocation by specifying a <> in select constructors and +function parameter lists. A <> has these properties: + +* A storage pointer always points to a valid, + type-erased {ref_memory_resource}. + +* Default-constructed storage pointers reference the + <>, an implementation-defined + instance which always uses the equivalent of global operator new and delete. + +* Storage pointers constructed from a {ref_memory_resource} or + {ref_polymorphic_allocator} do not acquire ownership; the caller is + responsible for ensuring that the lifetime of the resource extends until it + is no longer referenced. + +* A storage pointer obtained from <> acquires shared + ownership of the memory resource; the lifetime of the resource is extended + until all copies of the storage pointer are destroyed. + +* The storage pointer remembers the value of <> + before type-erasing the resource, allowing the value to be queried at + run-time. + +This lists all of the allocation-related types and functions available when +using the library: + +.Functions and Types +|=== +|Name|Description + +| <> +| Returns a pointer to a memory resource instance which always throws an + exception upon allocation. This is used to to achieve the invariant that no + parsing or container operation will dynamically allocate memory. + +| <> +| A customization point allowing a memory resource type to indicate that calls + to deallocate are trivial. + +| <> +| A function returning a smart pointer with shared ownership of a newly + allocated memory resource. + +| {ref_memory_resource} +| The abstract base class representing an allocator. + +| <> +| A memory resource which allocates large blocks of memory and has a trivial + deallocate function. Allocated memory is not freed until the resource is + destroyed, making it fast for parsing but not suited for performing + modifications. + +| {ref_polymorphic_allocator} +| An {req_Allocator} which uses a reference to a {ref_memory_resource} to + perform allocations. + +| <> +| A memory resource that uses a single caller provided buffer. No dynamic + allocations are used. This is fast for parsing but not suited for + performing modifications. + +| <> +| A smart pointer through which a {ref_memory_resource} is managed and + accessed. +|=== + +== Default Memory Resource +The default memory resource uses the global `operator new` and `operator +delete` to allocate memory. This resource is not reference counted and has +a non-trivial deallocate function. All default-constructed <> +objects reference the same memory resource: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_1,indent=0] +---- + +Default-constructed library containers use the default memory resource: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_2,indent=0] +---- + +The default memory resource is well suited for general usage. It offers +reasonable performance for parsing, and conservative memory usage for +modification of the contents of containers. + +[NOTE] +This memory resource is not guaranteed to be the same as the result of +`boost::container::pmr::get_default_resource`. It also cannot be changed with +`boost::container::pmr::set_default_resource`. + +== Monotonic Resource +Consider the pattern of memory allocation during parsing: when an array, +object, or string is encountered the parser accumulates elements in its +temporary storage area. When all of the elements are known, a single memory +allocation is requested from the resource when constructing the value. Thus, +parsing only allocates and constructs containers at their final size. Memory is +not reallocated; that is, a memory buffer never needs to grow by allocating +a new larger buffer and deallocating the previous buffer. + +The <> optimizes this memory allocation pattern by +allocating increasingly large blocks of global memory internally and parceling +those blocks out in smaller pieces to fulfill allocation requests. It has +a trivial deallocate function. The monotonic resource does not actually +deallocate memory until the resource is destroyed. Thus, it is ideally suited +for the use-case where JSON is parsed, and the resulting value is then +inspected but not modified. + +The resource to use when constructing values may be specified in calls to +<> as shown here: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_3,indent=0] +---- + +Or, to parse into a value with shared ownership of the memory resource: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_4,indent=0] +---- + +A monotonic resource may be optionally constructed with an initial buffer to +use first, before going to the heap. This allows the caller to use stack space +and avoid dynamic allocations for most parsed JSON, falling back to dynamic +allocation from the heap if the input JSON is larger than average, as shown +here: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_5,indent=0] +---- + +== Static Resource +A <> constructs from a caller-provided buffer, and +satisfies all memory allocation requests from the buffer. Once the buffer is +exhausted, subsequent calls to allocate throw the exception `std::bad_alloc`. +The resource offers a simple invariant: dynamic heap allocations are never +performed. + +To use the resource, construct it with a local buffer: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_6,indent=0] +---- + +== Null Resource +The function <> returns a global instance of the null +resource. This resource offers a simple invariant: all calls to allocate will +throw the exception `std::bad_alloc`. An instance of the null resource can be +used to make parsing guarantee that allocations from the heap are never made. +This is explored in more detail in a later section. + +== Allocator Propagation +The containers <>, <>, and <> all propagate +the memory resource they were constructed with to child elements: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_7,indent=0] +---- + +This propagation acts recursively, containers within containers will +all have the resource propagated. Once a container is constructed, +its memory resource can never be changed. + +== Resource Lifetime +It is important to note that <> supports both shared-ownership +and reference lifetime models. Construction from a memory resource pointer does +not transfer ownership: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_8,indent=0] +---- + +When using a memory resource in this fashion, including the case where +a storage pointer or container is constructed from +a {ref_polymorphic_allocator}, the caller must ensure that the lifetime of the +resource is extended until it is no longer referenced by any variables; +otherwise, undefined behavior is possible. + +Shared ownership is achieved using the function <>, +which creates a new, reference-counted memory resource using a dynamic memory +allocation and returns it as a <>: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_9,indent=0] +---- + +When a storage pointer is constructed this way, the lifetime of the referenced +memory resource is extended until all variables which reference it are +destroyed. + +== User-Defined Resource +To implement custom memory allocation strategies, derive your class from +{ref_memory_resource} and implement the functions `do_allocate`, +`do_deallocate`, and `do_is_equal` as seen in this example below, which logs +each operation it performs to the console: + +[source] +---- +include::../../../test/doc_storage_ptr.cpp[tag=doc_storage_ptr_10,indent=0] +---- diff --git a/doc/pages/allocators/uses_allocator.adoc b/doc/pages/allocators/uses_allocator.adoc new file mode 100644 index 000000000..77b2fd0f2 --- /dev/null +++ b/doc/pages/allocators/uses_allocator.adoc @@ -0,0 +1,44 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#uses_allocator] += Uses-allocator Construction +To support code bases which are already using polymorphic allocators, the +containers in this library support {std_uses_allocator} construction. For +<>, <>, <>, and <>: + +* The nested type `allocator_type` is an alias for + a {ref_polymorphic_allocator}. + +* All eligible constructors which accept <> will also accept + an instance of {ref_polymorphic_allocator} in the same argument position. + +* The member function `get_allocator` returns an instance of + {ref_polymorphic_allocator} constructed from the {ref_memory_resource} used by + the container. Ownership of this memory resource is not transferred. + +Practically, this means that when a library container type is used in +a standard container that uses a polymorphic allocator, the allocator will +propagate to the JSON type. For example: + +[source] +---- +include::../../../test/doc_uses_allocator.cpp[tag=doc_uses_allocator_1,indent=0] +---- + +Library containers can be constructed from polymorphic allocators: + +[source] +---- +include::../../../test/doc_uses_allocator.cpp[tag=doc_uses_allocator_2,indent=0] +---- + +The polymorphic allocator is propagated recursively. Child elements of child +elements will use the same memory resource as the parent. diff --git a/doc/pages/benchmarks.adoc b/doc/pages/benchmarks.adoc new file mode 100644 index 000000000..eeb2dc488 --- /dev/null +++ b/doc/pages/benchmarks.adoc @@ -0,0 +1,229 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[pagelevels=1,toclevels=1] += Benchmarks +This section compares the performance of Boost.JSON with two widely used +libraries with similar functionality: RapidJSON which is known for its +performance, and JSON for Modern C++ which is known for feature-richness. The +bench program measures the throughput of parsing and serialization for the +a set of JSON representing typical workloads. These implementations are +evaluated: + +.Implementations +|=== +|Name|Description + +| *boost(pool)* +| Parses the input using a <>, + which is optimized for parsing without subsequent + modification. + The <> object is reused between + trials, allowing temporary memory allocated by the + implementation to persist and improve performance. + +| *boost* +| Parses the input using the <>, which uses the standard C++ allocator, and is designed for general + use including mutation of the document after it is parsed. The + <> object is reused between trials, allowing temporary + memory allocated by the implementation to persist and improve performance. + +| *rapidjson(pool)* +| Parses the input using an instance of + https://rapidjson.org/classrapidjson_1_1_memory_pool_allocator.html[`MemoryPoolAllocator`], + which is optimized for parsing without subsequent modification. The + https://rapidjson.org/classrapidjson_1_1_generic_document.html[`Document`] + object holding temporary memory is not reused between trials, otherwise + memory consumption grows without bounds and invalidates the benchmark. + +| *rapidjson* +| Parses the input using an instance of + https://rapidjson.org/classrapidjson_1_1_crt_allocator.html[`CrtAllocator`], + which uses the standard C++ allocator, and is designed for general use + including mutation of the document after it is parsed. The + https://rapidjson.org/classrapidjson_1_1_generic_document.html[`Document`] + object holding temporary memory is not reused between trials, otherwise + memory consumption grows without bounds and invalidates the benchmark. + +| *nlohmann* +| Parses the input using the static member function + https://nlohmann.github.io/json/classnlohmann_1_1basic__json_ab330c13ba254ea41fbc1c52c5c610f45.html[`json::parse`], + which uses the default `std` allocator, and is designed for general use + including mutation of the document after it is parsed. This library does not + provide an interface to reuse temporary storage used during parsing or + serialization on subsequent operations. +|=== + +== Methodology +The input files are all loaded first. Then each configuration is run for +a sufficient number of trials to last at least 5 seconds. The elapsed time, +number of invocations (of parse or serialize), and bytes transferred are +emitted as a sample along with a calculation of throughput expressed in +megabytes per second. Several samples (currently five) are generated for each +configuration. All but the median two samples are discarded, with the remaining +samples averaged to produce a single number which is reported as the benchmark +result. + +The input files, available in the bench/data directory, are laid out thusly: + +.Input Files +|=== +|Name|Size|Description + +| <> +| 125KB +| Data from the Apache Jenkins installation. + +| <> +| 2.2MB +| The largest file, containing a large number of 2-element arrays holding + floating-point coordinate pairs. + +| <> +| 1.69MB +| A large JSON with a variety of nesting, types, and lengths. + +| <> +| 64KB +| An export of data from the Github Events API. + +| <> +| 3.25MB +| Google Summer of Code 2018 data. + +| <> +| 216KB +| An array of large objects. + +| <> +| 2.91MB +| A three.js example model serialized to JSON. + +| <> +| 707KB +| A JSON representing a 3D mesh. Contains many floating-point numbers. + +| <> +| 1.54MB +| mesh.json with whitespace added. + +| <> +| 147KB +| A array containing only floating-point numbers. + +| <> +| 499KB +| A JSON with lots of Cyrillic characters. + +| <> +| 617KB +| An export of data from Twitter's API. + +| <> +| 550KB +| twitter.json with whitespace removed and non-ASCII characters replaced with + Unicode escapes. + +| <> +| 521KB +| An export of data from Twitter's API. +|=== + +Hardware used for testing: **Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz**, +Windows 10, 32GB RAM. + +Compilers and optimization flags: gcc 8.1 (-O3), clang 12.0 (-O3), and msvc +19.26 (/O2). + +== Parsing + +=== Parse apache_builds.json +image::images/parse_apache_builds.png[width=668,height=712] + +=== Parse canada.json +image::images/parse_canada.png[width=668,height=712] + +=== Parse citm_catalog.json +image::images/parse_citm_catalog.png[width=668,height=712px] + +=== Parse github_events.json +image::images/parse_github_events.png[width=668,height=712] + +=== Parse gsoc-2018.json +image::images/parse_gsoc_2018.png[width=668,height=712] + +=== Parse instruments.json +image::images/parse_instruments.png[width=668,height=712] + +=== Parse marine_ik.json +image::images/parse_marine_ik.png[width=668,height=712] + +=== Parse mesh.json +image::images/parse_mesh.png[width=668,height=712] + +=== Parse mesh.pretty.json +image::images/parse_mesh_pretty.png[width=668,height=712] + +=== Parse numbers.json +image::images/parse_numbers.png[width=668,height=712] + +=== Parse random.json +image::images/parse_random.png[width=668,height=712] + +=== Parse twitter.json +image::images/parse_twitter.png[width=668,height=712] + +=== Parse twitterescaped.json +image::images/parse_twitterescaped.png[width=668,height=712] + +=== Parse update-center.json +image::images/parse_update_center.png[width=668,height=712] + +== Serialization + +=== Serialize canada.json +image::images/serialize_canada.png[width=668,height=712] + +=== Serialize citm_catalog.json +image::images/serialize_citm_catalog.png[width=668,height=712] + +=== Serialize github_events.json +image::images/serialize_github_events.png[width=668,height=712] + +=== Serialize gsoc-2018.json +image::images/serialize_gsoc_2018.png[width=668,height=712] + +=== Serialize instruments.json +image::images/serialize_instruments.png[width=668,height=712] + +=== Serialize marine_ik.json +image::images/serialize_marine_ik.png[width=668,height=712] + +=== Serialize mesh.json +image::images/serialize_mesh.png[width=668,height=712] + +=== Serialize mesh.pretty.json +image::images/serialize_mesh_pretty.png[width=668,height=712] + +=== Serialize numbers.json +image::images/serialize_numbers.png[width=668,height=712] + +=== Serialize random.json +image::images/serialize_random.png[width=668,height=712] + +=== Serialize twitter.json +image::images/serialize_twitter.png[width=668,height=712] + +=== Serialize twitterescaped.json +image::images/serialize_twitterescaped.png[width=668,height=712] + +=== Serialize update-center.json +image::images/serialize_update_center.png[width=668,height=712px] diff --git a/doc/pages/comparison.adoc b/doc/pages/comparison.adoc new file mode 100644 index 000000000..d63694289 --- /dev/null +++ b/doc/pages/comparison.adoc @@ -0,0 +1,127 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#comparison,pagelevels=1,toclevels=1] += Comparison to Other Libraries + +:icon_good: pass:q[[.green]##✔##] +:icon_bad: pass:q[[.red]##✘##] + +There exist many C++ JSON libraries, but two are particularly noteworthy for +the purpose of comparison: https://rapidjson.org/[RapidJSON], +https://nlohmann.github.io/json/[JSON for Modern {cpp}] (referred to herein as +nlohmann's JSON, or nlohmann), and https://github.com/lemire/simdjson[SIMD +JSON]. + +== Comparison to nlohmann JSON + +Value Type: +https://github.com/nlohmann/json/blob/00cb98a3d170161711ab912ae6acefba31f29f75/include/nlohmann/json.hpp#L165[`nlohmann::basic_json`] + +[source] +---- +template< + template class ObjectType, + template class ArrayType, + class StringType, + class BooleanType, + class NumberIntegerType, + class NumberUnsignedType, + class NumberFloatType, + template class AllocatorType, + template class JSONSerializer + > +class basic_json +{ +private: + .... + friend ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; +... +---- + +This library adopts a "kitchen sink" approach. It contains a wealth of +features, even those with niche uses. Its weakness is that the many template +parameters, while allowing for configurability, inhibit the best possible +optimizations. The consequence is that the library performs poorly. The ability +to configure every aspect of the value type has the paradoxical effect of +making it less suitable as a vocabulary type. + +* {icon_bad} `basic_json` is a class template. Libraries must agree on the + choices of template parameters to be interoperable. + +* {icon_bad} Too much customization. We struggle to see a use case for making + `BooleanType` anything other than `bool`. + +* {icon_bad} Poor separation of concerns. The `basic_json` container + declaration needlessly conflates parsing and serialization APIs. + +* {icon_bad} Limited allocator support. Only stateless allocators are allowed, + which rules out the most important type of allocator, a local arena-based + implementation. + +* {icon_bad} No incremental parsing, no incremental serialization. + +* {icon_bad} Slow parsing and serialization performance. + +* {icon_good} Full-featured, including JSON Pointer, CBOR, and others. + +== Comparison to RapidJSON + +Value Type: +https://github.com/Tencent/rapidjson/blob/bb5f966b9939d6cdfbac3462a0410e185099b3af/include/rapidjson/document.h#L608[`rapidjson::GenericValue`] + +[source] +---- +template > +class GenericValue; + +template +class GenericArray; + +template +class GenericObject; +---- + +* {icon_bad} The value type is not regular. Assignment is destructive, + performing `a = b` is equivalent to `a = std::move(b)`. No copy construction + or copy assignment allowed. + +* {icon_bad} Object types have no hash table or index to reduce the cost of + lookups. + +* {icon_bad} Allocators have reference semantics. Problems with lifetime are + easily encountered. + +* {icon_bad} The interface of the array and object types are considerably + different from their standard library equivalents, and not idiomatic. + +* {icon_bad} No incremental parsing, no incremental serialization. + +* {icon_good} Parsing and serialization performance is better than most other + libraries. + +=== Comparison to SIMD JSON + +[source] +---- +class ParsedJson; +---- + +This is quite an interesting data structure, which is optimized to work well +with the SIMD parser. It makes very good design choices for the intended +use-case. However it is not well suited as a vocabulary type due to the +necessary limitations. + +* {icon_bad} Sequential access only, via `ParsedJson::BasicIterator` + +* {icon_bad} Read-only, can only be populated by the provided SIMD JSON parser. + +* {icon_good} The fastest available JSON parser. diff --git a/doc/pages/conversion/alloc.adoc b/doc/pages/conversion/alloc.adoc new file mode 100644 index 000000000..2dde7cd69 --- /dev/null +++ b/doc/pages/conversion/alloc.adoc @@ -0,0 +1,42 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Allocation Control +As <> creates a <> object, users may want to control +the way memory is allocated for it. For this reason the function has an +optional <> parameter, that is used to set the +{ref_memory_resource} for the result. + +[NOTE] +<> does not have a similar parameter, as <> is not +created. + +As the conversion result is set via an output parameter of type `value&`, the +intended <> is correctly propagated. But users still should +take care to not create temporaries using the default {ref_memory_resource} by +accident. + +For example, consider this alternative implementation of `tag_invoke` for +`ip_address` from the section <>. + +``` +void +tag_invoke( const value_from_tag&, value& jv, ip_address const& addr ) +{ + jv = array{ b[0], b[1], b[2], b[3] }; +} +``` + +This implementation explicitly creates an <> rather than relying on +assignment from an initializer list. But the array uses default +{ref_memory_resource}, not the one used by `jv`. + +To avoid creating such temporaries with an incorrect {ref_memory_resource}, +using <>'s member functions <>, +<>, and <> can be helpful. diff --git a/doc/pages/conversion/context.adoc b/doc/pages/conversion/context.adoc new file mode 100644 index 000000000..a1f4fc6d4 --- /dev/null +++ b/doc/pages/conversion/context.adoc @@ -0,0 +1,150 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Contextual Conversions +Previously in this section we've been assuming that there is a particular +fitting JSON representation for a type. But this is not always the case. Often +one needs to represent particular value with JSON of certain format in one +situation and with another format in a different situation. This can be +achieved with Boost.JSON by providing an extra argument---context. + +Let's implement conversion from `user_ns::ip_address` to a JSON string: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_1,indent=0] +---- + + +These `tag_invoke` overloads take an extra `as_string` parameter, which +disambiguates this specific representation of `ip_address` from all other +potential representations. In order to take advantage of them one needs to pass +an `as_string` object to <> or <> as the last +argument: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_2,indent=0] +---- + +Note, that if there is no dedicated `tag_invoke` overload for a given type and +a given context, the implementation falls back to overloads without context. +Thus it is easy to combine contextual conversions with conversions provided by +the library: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_3,indent=0] +---- + +== Conversions for Third-Party Types +Normally, you wouldn't be able to provide conversions for types from +third-party libraries and standard types, because it would require yout to put +`tag_invoke` overloads into namespaces you do not control. But with contexts +you can put the overloads into your namespaces. This is because the context +will add its associated namespaces into the list of namespaces where +`tag_invoke` overloads are searched. + +As an example, let's implement conversion for +https://en.cppreference.com/w/cpp/chrono/system_clock[``std::chrono::system_clock::time_point``s] +using https://en.wikipedia.org/wiki/ISO_8601[ISO 8601] format. + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_4,indent=0] +---- + +Reverse conversion is left out for brevity. + +The new context is used in a similar fashion to `as_string` previously in this +section. + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_5,indent=0] +---- + +One particular use case that is enabled by contexts is adaptor libraries that +define JSON conversion logic for types from a different library. + +== Passing Data to Conversion Functions +Contexts we used so far were empty classes. But contexts may have data members +and member functions just as any class. Implementers of conversion functions +can take advantage of that to have conversions configurable at runtime or pass +special objects to conversions (e.g. allocators). + +Let's rewrite conversion for ``system_clock::time_point``s to allow any format +supported by `std::strftime`. + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_6,indent=0] +---- + +This `tag_invoke` overload lets us change date conversion format at runtime. +Also note, that there is no ambiguity between `as_iso_8601` overload and +`date_format` overload. You can use both in your program: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_7,indent=0] +---- + +== Combining Contexts +Often it is needed to use several conversion contexts together. For example, +consider a log of remote users identified by IP addresses accessing a system. +We can represent it as +`std::vector>`. We +want to serialize both ``ip_address``es and ``time_point``s as strings, but for +this we need both `as_string` and `as_iso_8601` contexts. To combine several +contexts just use {std_tuple}. Conversion functions will select the first +element of the tuple for which a `tag_invoke` overload exists and will call +that overload. As usual, `tag_invoke` overloads that don't use contexts and +library-provided generic conversions are also supported. Thus, here's our +example: + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_8,indent=0] +---- + +In this snippet `time_point` is converted using `tag_invoke` overload that +takes `as_iso_8601`, `ip_address` is converted using `tag_invoke` overload +that takes `as_string`, and {std_vector} is converted using a generic +conversion provided by the library. + +== Contexts and Composite Types +As was shown previously, generic conversions provided by the library forward +contexts to conversions of nested objects. And in the case when you want to +provide your own conversion function for a composite type enabled by a +particular context, you usually also need to do that. + +Consider this example. As was discussed in a previous section, +<> requires that your key type satisfies +<>. Now, let's say your keys are not string-like, but they +do convert to <>. You can make such maps to also convert to objects +using a context. But if you want to also use another context for values, you +need a way to pass the full combined context to map elements. So, you want the +following test to succeed. + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_9,indent=0] +---- + +For this you will have to use a different overload of `tag_invoke`. This time +it has to be a function template, and it should have two parameters for +contexts. The first parameter is the specific context that disambiguates that +particular overload. The second parameter is the full context passed to +<> or <>. + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_context_conversion_10,indent=0] +---- diff --git a/doc/pages/conversion/custom.adoc b/doc/pages/conversion/custom.adoc new file mode 100644 index 000000000..e01323b1a --- /dev/null +++ b/doc/pages/conversion/custom.adoc @@ -0,0 +1,101 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Custom Conversions +Boost.JSON uses two mechanisms to customize conversion between <> +and user types. One mechanism involves specializing type traits. The other one +is more powerful and requires defining overloads of `tag_invoke`. Both +mechanisms will be further explained in this section. + +== Conversion Traits +Previously a number of conversion type traits, like <> or +<>, were introduced. The library tries the traits one +after another and uses the implementation that corresponds to the first +matching trait. In some cases, though, a type would match a trait with a higher +priority, but the user intends for it to belong to a lower priority category. +If this happens the user can specialize the trait that's not supposed to match +for that type to be an equivalent of `std::false_type`. + +Consider this type: + +[source] +---- +include::../../../test/doc_types.hpp[tag=snippet_conv_spec_trait1,indent=0] +---- + +It exposes both a sequence API and a tuple API. But converting from +<> to `user_ns::ip_address` would not be able to use implementation +for sequences, since those are constructed empty and then populated one element +at a time, while `ip_address` has a fixed size of 4. The tuple conversion would +fit, though. The only problem is that <> has a lower +priority than <>. In order to circumvent this, the user +only needs to specialize <> to not match `ip_address`. + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_conv_spec_trait2,indent=0] +---- + +== `tag_invoke` Overloads +The second, more powerful approach, is to provide the conversion implementation +yourself. With Boost.JSON this is done by defining an overload of `tag_invoke` +function (the benefits of this mechanism are outlined in +http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1895r0.pdf[C++ +proposal P1895]. In essence, `tag_invoke` provides a uniform interface for +defining customization points by using argument-dependent lookup to find +a viable overload from the point at which it is called. As the name suggests, +a tag type is passed as an argument in order to: + +* discard candidates that are unrelated to that particular +customization point, and + +* embed the user-defined type into the arguments list (e.g. by using a tag type +template such as `value_to_tag`) so that its +http://eel.is/c++draft/basic.lookup.argdep#2[associated namespaces and +entities] are examined when name lookup is performed. + +This has the effect of finding user-provided `tag_invoke` overloads, even if +they are declared (lexically) after the definition of the calling function. + +Overloads of `tag_invoke` called by <> take the form: + +``` +void tag_invoke( const value_from_tag&, value&, T ); +``` + +While overloads of `tag_invoke` called by <> take the form: + +``` +T tag_invoke( const value_to_tag< T >&, const value& ); +``` + +If we implemented conversion for `user_ns::ip_address` manually with this +approach, it would look like this: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_tag_invoke_1,indent=0] +---- + +Since the type being converted is embedded into the function's signature, +user-provided overloads are visible to argument-dependent lookup and will be +candidates when a conversion is performed: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_tag_invoke_2,indent=0] +---- + +Users can freely combine types with custom conversions with types with +library-provided conversions. The library handles them correctly: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_tag_invoke_3,indent=0] +---- diff --git a/doc/pages/conversion/direct.adoc b/doc/pages/conversion/direct.adoc new file mode 100644 index 000000000..c1b94f163 --- /dev/null +++ b/doc/pages/conversion/direct.adoc @@ -0,0 +1,43 @@ +//// +Copyright (c) 2023 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Direct Conversion +For large inputs parsing into the library's containers followed by conversion +via <> (or vice versa <> followed by +serialization from a <>) might be prohibitively expensive. For these +cases the library provides components that allow parsing directly into and +serializing directly from user-provided objects. + +The drawback of this approach is that fully custom type representations are +not supported, only the library-provided conversions are. Also all objects that +should be populated by parsing have to be default constructible types. This +includes not only the top-level object, but e.g. elements of containers, +members of described `struct`s, and alternatives of variants. + +That being said, if your types are default-constructible and you don't need the +customisability allowed by <> and <>, then you +can get a significant performance boost with direct conversions. + +Direct parsing is performed by the <> family of functions. The +library provides overloads that take either <> or +`std::istream`, and can report errors either via throwing exceptions or setting +an error code. + +[source] +---- +include::../../../test/snippets.cpp[tag=doc_parse_into_1,indent=0] +---- + +If you need to combine incremental parsing with direct parsing, you can resort +to <>. `parser_for` is an instantiation of +<> that parses into an object of type `T`, and is what +<> uses under the hood. + +Direct serialization doesn't require any special components and works with the +refular <> and <>. diff --git a/doc/pages/conversion/forward.adoc b/doc/pages/conversion/forward.adoc new file mode 100644 index 000000000..582300b08 --- /dev/null +++ b/doc/pages/conversion/forward.adoc @@ -0,0 +1,43 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Avoiding Physical Dependency +Some users, particularly library authors, may wish to provide conversions +between their types and <>, but at the same time would prefer to +avoid having their library depend on Boost.JSON. This is possible to achieve +with the help of a few forward declarations. + +[source] +---- +include::../../../test/doc_forward_conversion.cpp[tag=doc_forward_conversion_1,indent=0] +---- + +Note that <> is declared using an out-parameter, rather then +returning its result. This overload is specifically designed for this use-case. + +After that the definitions of `tag_invoke` overloads should be provided. These +overloads have to be templates, since <> is only forward-declared +and hence is an incomplete type. + +[source] +---- +include::../../../test/doc_forward_conversion.cpp[tag=doc_forward_conversion_2,indent=0] +---- + +As discussed previously, we prefer to define a non-throwing overload of +`tag_invoke` for <>, rather then the throwing overload for +<>, as the latter can fallback to the former without performance +degradation. + +Forward declarations of contextual conversions are done very similarly: + +[source] +---- +include::../../../test/doc_forward_conversion.cpp[tag=doc_forward_conversion_3,indent=0] +---- diff --git a/doc/pages/conversion/guidelines.adoc b/doc/pages/conversion/guidelines.adoc new file mode 100644 index 000000000..9cdf11717 --- /dev/null +++ b/doc/pages/conversion/guidelines.adoc @@ -0,0 +1,40 @@ +//// +Copyright (c) 2023 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Conversion Customization Guidelines +With so many options, it can be hard to choose the best way to customise +conversion for your type. In this section, we will discuss those options and +suggest which to choose when. + +The first advice is to use one of the library-provided conversions, rather then +providing a custom one, unless the resulting format is undesirable. If the +library deduces the wrong conversion category, you can opt out by specialising +the relevant trait to inherit from `std::false_type`. + +If library-provided conversions are suitable for you, you have the option to +use direct conversions. This also puts the requirement of being default +constructible on many of your types. + +The next thing to consider is whether your conversions are intended for +internal use, or whether your users are not members of your team. If your users +are external, then they will ultimately determine the conditions in which these +conversions will be used. Conversely, for internal libraries and applications, +you have the full control of usage conditions. + +If your users are external, they and not you decide whether throwing exceptions +is acceptable. So, in this case it is better to use non-throwing `tag_invoke` +overloads. In addition, for customising conversion of composite types, always +use `tag_invoke` overload with 2 context parameters. This will allow correct +context propagation to elements of composites. This will also allow propagation +of exceptions from conversion of elements. + +Finally, it is worth mentioning that due to the ability to provide conversions +to JSON containers without a binary dependency on the library, you don't have +to push such dependency on your users. This is particularly relevant for +libraries for which interoperation with Boost.JSON is only ancillary. diff --git a/doc/pages/conversion/nothrow.adoc b/doc/pages/conversion/nothrow.adoc new file mode 100644 index 000000000..c73d17af8 --- /dev/null +++ b/doc/pages/conversion/nothrow.adoc @@ -0,0 +1,57 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Non-Throwing Conversions +For the case where throwing exceptions is undesirable, Boost.JSON also provides +a non-throwing version of <>, the function template +<>. It returns {ref_result}, a specialised variant that +either holds a value or an {ref_error_code}. + +[NOTE] +There's no non-throwing equivalent for <>. This is simply +because we haven't yet encountered a situation where <> needed +to communicate an error to the caller. + +The library provides non-throwing conversions for all the categories of types +it supports with <>. If a user wants to use it with custom types, +an overload of `tag_invoke` of this form needs to be provided: + +``` +result_for::type +tag_invoke( const try_value_to_tag< T >&, const value& ); +``` + +For the class `ip_address` from the section <> this +overload may look like this: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_nothrow_1,indent=0] +---- + +The overload lets us use `ip_address` with <>. + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_nothrow_2,indent=0] +---- + +If <> is used with a type, for which there's no `tag_invoke` +overload of the form described in this section, but there is one of the form +intended for <>, then the library still tries to perform the +conversion. It uses the throwing overload, and attempts to convert any thrown +exception into an {ref_error_code}. Note, though, that such approach will +likely be slower then a dedicated overload. + +The opposite is also true: if there's a `tag_invoke` overload intended for +<>, but not for <>, then calling +<> will invoke the non-throwing overload, then construct +a {ref_system_error} from the {ref_error_code} and throw it. Due to these +fallbacks, it is recommended that users provide the overload from this section, +rather then the other one, if they ever intend to use <>. diff --git a/doc/pages/conversion/overview.adoc b/doc/pages/conversion/overview.adoc new file mode 100644 index 000000000..f1e3eff70 --- /dev/null +++ b/doc/pages/conversion/overview.adoc @@ -0,0 +1,147 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2021 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#conversion] += Value Conversion +While the <> container makes it easy to create ad-hoc structures, +often it is necessary to convert between JSON and user-defined types or types +from the standard library. + +The function template <> provides an interface to construct +a <> from a type `T`. The function template <> +converts in the opposite direction, from a type `T` to <>. Both +support a wide variety of different +https://en.cppreference.com/w/cpp/language/types[fundamental types], such as +`int` or `double`, standard library types, such as `std::string` or +`std::vector`, and can be extended to support user-defined types. + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_conv_1,indent=0] +---- + +For the type `T`, the appropriate conversion approach is chosen from the +following list of categories. The first matching category is selected. + +.Conversion categories +[%autowidth,cols=4] +|=== +|Category of T|Comment|`value_from` behavior|`value_to` behavior + +|Custom conversion. +| +|Custom behavior. +|Custom behavior. + +|Boost.JSON container. +| +|The result is equal to the input value. +|The result is equal to the input value. + +|`bool` +| +|The result is equal to the input value. +|The result is equal to the input value. + +|https://en.cppreference.com/w/cpp/types/is_arithmetic[Arithmetic type] +| +a| The result is a number equal to input and has the type + +* `std::int64_t`, if `T` is a signed integer'; or +* `std::uint64_t`, if `T` is an unsigned integer; or +* `double` otherwise. +|The result is created via <>. + +|Type satisfying <> +|Intended for types like {std_monostate}. +|The result is a null value. +|The result is default-constructed. + +|Type satisfying <>. +|A sequence of `char`s, e.g. `std::string`. +|The result is a <>. +|The result is constructed from a <>. + +|Type satisfying <>. +|A one-to-one mapping (e.g. `std::map`) with string-like keys. +|The result is an <>. +|The result is default-constructed, and elements are `insert`-ed at the end. + +|Type satisfying <>. +|A sequence of elements, e.g. `std::vector`. +|The result is an <>. +|The result is default-constructed, and elements are `insert`-ed at the end. + +|Type satisfying <>. +|A heterogenous sequence with fixed size, e.g. `std::tuple` and `std::pair`. +|The result is an <>. +|The result is constructed with the array elements as constructor arguments. + +|Type satisfying <> +| +|The result is an <> with described members' names as keys. +|The result is default-constructed and described members are assigned + corresponding values. + +|Type satisfying <> +| +|If the input value is equal to one of the described enumerators, the result is + a <>, containing its name. Otherwise it's equal to the input + value converted to its underlying type. +|The result is the described enumerator, corresponding to the input + <>. + +|Type satisfying <>. +|`std::variant` and similar types, e.g. `boost::variant2::variant`. +|The result is equal to the result of conversion of the active variant + alternative. +|The result holds the first alternative for which a conversion succeeds. + +|Type satisfying <> +| +|If the input value is empty, the result is a `null`. Otherwise it is + equivalent to conversion of the object stored inside of optional. +|The result is default constructed if the input value is `null`. Otherwise the + result is constructed from the result of conversion of the input to the + type stored in optional. + +|Type satisfying <>. +|`std::filesystem::path` and similar types, e.g. `boost::filesystem::path`. +|The result is equal to the result of `path::generic_string`. +|The result is constructed from two pointers to `const char`. +|=== + +For composite types (sequences, tuples, described classes, etc.) conversion of +contained objects is applied recursively. For example: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_conv_recursive,indent=0] +---- + +Here, the map is converted into an <>, since it matches +<>. Each of its keys is converted into a <>, as +`std::string` matches <>, and each of its values is +converted into an <>, as `std::pair` matches <>. +Finally, elements of pairs are converted into a `std::int64_t` number and +a `bool`. + +:leveloffset: +1 + +include::custom.adoc[] +include::nothrow.adoc[] +include::alloc.adoc[] +include::context.adoc[] +include::forward.adoc[] +include::direct.adoc[] +include::guidelines.adoc[] + +:leveloffset: -1 diff --git a/doc/pages/definitions.adoc b/doc/pages/definitions.adoc new file mode 100644 index 000000000..f815cb432 --- /dev/null +++ b/doc/pages/definitions.adoc @@ -0,0 +1,43 @@ +//// +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +:ref_memory_resource: pass:q[https://boost.org/doc/libs/release/doc/html/doxygen/boost_container_header_reference/classboost_1_1container_1_1pmr_1_1memory__resource.html[`memory_resource`]] +:ref_polymorphic_allocator: pass:q[https://boost.org/doc/libs/release/doc/html/doxygen/boost_container_header_reference/classboost_1_1container_1_1pmr_1_1polymorphic__allocator.html[`polymorphic_allocator`]] +:ref_error_category: pass:q[https://boost.org/doc/libs/release/libs/system/doc/html/system.html#ref_error_category[`error_category`]] +:ref_error_code: pass:q[https://boost.org/doc/libs/release/libs/system/doc/html/system.html#ref_error_code[`error_code`]] +:ref_error_condition: pass:q[https://boost.org/doc/libs/release/libs/system/doc/html/system.html#ref_error_condition[`error_condition`]] +:ref_result: pass:q[https://boost.org/doc/libs/release/libs/system/doc/html/system.html#ref_resultt_e[`result`]] +:ref_system_error: pass:q[https://boost.org/doc/libs/release/libs/system/doc/html/system.html#ref_system_error[`system_error`]] + +:req_Allocator: pass:q[https://en.cppreference.com/w/cpp/named_req/Allocator[__Allocator__]] +:req_CopyAssignable: pass:q[https://en.cppreference.com/w/cpp/named_req/CopyAssignable[__CopyAssignable__]] +:req_CopyConstructible: pass:q[https://en.cppreference.com/w/cpp/named_req/CopyConstructible[__CopyConstructible__]] +:req_Copyable: pass:q[https://en.cppreference.com/w/cpp/concepts/copyable[__Copyable__]] +:req_DefaultConstructible: pass:q[https://en.cppreference.com/w/cpp/named_req/DefaultConstructible[__DefaultConstructible__]] +:req_Hash: pass:q[https://en.cppreference.com/w/cpp/named_req/Hash[__Hash__]] +:req_InputIterator: pass:q[https://en.cppreference.com/w/cpp/named_req/InputIterator[__LegacyInputIterator__]] +:req_ForwardIterator: pass:q[https://en.cppreference.com/w/cpp/named_req/ForwardIterator[__LegacyForwardIterator__]] +:req_MoveAssignable: pass:q[https://en.cppreference.com/w/cpp/named_req/MoveAssignable[__MoveAssignable__]] +:req_MoveConstructible: pass:q[https://en.cppreference.com/w/cpp/named_req/MoveConstructible[__MoveConstructible__]] +:req_Regular: pass:q[https://en.cppreference.com/w/cpp/concepts/regular[__Regular__]] +:req_Swappable: pass:q[https://en.cppreference.com/w/cpp/named_req/Swappable[__Swappable__]] + +:std_array: pass:q[https://en.cppreference.com/w/cpp/container/array[`std::array`]] +:std_initializer_list: pass:q[https://en.cppreference.com/w/cpp/utility/initializer_list[`std::initializer_list`]] +:std_complex: pass:q[https://en.cppreference.com/w/cpp/numeric/complex[`std::complex`]] +:std_hash: pass:q[https://en.cppreference.com/w/cpp/utility/hash[`std::hash`]] +:std_memory_resource: pass:q[https://en.cppreference.com/w/cpp/memory/memory_resource[`std::pmr::memory_resource`]] +:std_monostate: pass:q[https://en.cppreference.com/w/cpp/utility/variant/monostate[`std::monostate`]] +:std_ostream: pass:q[https://en.cppreference.com/w/cpp/io/basic_ostream[`std::ostream`]] +:std_polymorphic_allocator: pass:q[https://en.cppreference.com/w/cpp/memory/polymorphic_allocator[`std::pmr::polymorphic_allocator`]] +:std_string: pass:q[https://en.cppreference.com/w/cpp/string/basic_string[`std::string`]] +:std_unordered_map: pass:q[https://en.cppreference.com/w/cpp/container/unordered_map[`std::unordered_map`]] +:std_uses_allocator: pass:q[https://en.cppreference.com/w/cpp/memory/uses_allocator[`std::uses_allocator`]] +:std_vector: pass:q[https://en.cppreference.com/w/cpp/container/vector[`std::vector`]] +:std_tuple: pass:q[https://en.cppreference.com/w/cpp/utility/tuple[`std::tuple`]] diff --git a/doc/pages/docinfo.html b/doc/pages/docinfo.html new file mode 100644 index 000000000..0b9ef0980 --- /dev/null +++ b/doc/pages/docinfo.html @@ -0,0 +1,96 @@ + diff --git a/doc/pages/dom/array.adoc b/doc/pages/dom/array.adoc new file mode 100644 index 000000000..2b94feb37 --- /dev/null +++ b/doc/pages/dom/array.adoc @@ -0,0 +1,59 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_array] += `array` +A <> stores an instance of <> as the underlying +representation for a JSON array. Instances of the __array__ type function +identically to a {std_vector} of <>. Additionally, all values +inserted into the container will use the same <> as the +container itself. + +An empty array may be constructed without incurring any memory allocations +using the <>. +A <> can also be explicitly specified: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_arrays_1,indent=0] +---- + +Initializer lists can be used to construct objects with initial contents. These +constructors may allocate memory and throw: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_arrays_2,indent=0] +---- + +Alternatively, elements may be inserted after construction: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_arrays_3,indent=0] +---- + +Similar to its standard library counterpart, elements may be accessed directly +by their 0-based index with bounds checking using <>, +or without bounds checking using <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_arrays_4,indent=0] +---- + +For the complete listing of all available member functions and nested +types, see the reference page for <>. + +== Formatted Output + +When an <> is formatted to a {std_ostream}, the result is a valid +JSON. That is, the array will be output with square brackets and the comma +separated list of values, as per the JSON specification. diff --git a/doc/pages/dom/init_lists.adoc b/doc/pages/dom/init_lists.adoc new file mode 100644 index 000000000..2d441e0d5 --- /dev/null +++ b/doc/pages/dom/init_lists.adoc @@ -0,0 +1,102 @@ +//// +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Initializer Lists +Initializer lists can be used to construct or assign a <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_1,indent=0] +---- + +Simple initializer lists produce an <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_2,indent=0] +---- + +Initializer lists can be nested. Here we construct an array as an element of an +array: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_3,indent=0] +---- + +When a two element initializer list is nested within an enclosing initializer +list, it is unclear whether it represents an <> or an +<>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_4,indent=0] +---- + +In such cases, if every element consists of a string followed by a single +value, then the enclosing initializer list is interpreted as an <>. +Otherwise, it is interpreted as an <>. + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_5,indent=0] +---- + +To resolve the ambiguity manually, use an explicit constructor: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_6,indent=0] +---- + +Initializer lists can be used to unambiguously construct or assign an +<> or <>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_7,indent=0] +---- + +Similarly, an initializer list for an <> is always interpreted as an +<>. In such cases, the initializer list must be a list of key-value +pairs. For example, the following code will not compile because `1` is not +convertible to a string: + +[source] +---- +object jo = { { 1, 0.39 }, { "venus", 0.72 }, { "earth", 1 } }; +---- + +The requirement for an initializer list to be interpreted as an <> +or <> when initializing such an entity only applies to the outermost +initializer list; subsequent nested elements will follow the usual ambiguity +resolution rules. + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_8,indent=0] +---- + +Elements that are rvalues will be moved upon initialization: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_init_list_9,indent=0] +---- + +[WARNING] +==== +Do not create variables of type {std_initializer_list}. This may result in +temporaries being destroyed before the initializer list is used. +==== + +In all cases, the <> owned by an <>, +<>, or <> constructed from an initializer list will be +propagated to each element, recursively. diff --git a/doc/pages/dom/nested_access.adoc b/doc/pages/dom/nested_access.adoc new file mode 100644 index 000000000..f2fa3b60e --- /dev/null +++ b/doc/pages/dom/nested_access.adoc @@ -0,0 +1,53 @@ +//// +Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#nested_access] += Accessing Deeply Nested Elements +In order to allow conveniently retrieving and changing deeply nested elements +of <> objects the library implements +https://datatracker.ietf.org/doc/html/rfc6901[RFC 6901 (JSON Pointer)]: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_1,indent=0] +---- + +This is particularly useful when throwing exceptions is undesirable. For +example, compare + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_2,indent=0] +---- + +with + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_3,indent=0] +---- + +The library also allows changing and adding deeply nested elements. The +function <> traverses the value similarly to +<>, but additionally, it can create missing elements in +certain cases: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_4,indent=0] +---- + +The specific behavior is controlled by an optional parameter of type +<>. For example, here's the same example with +a different option: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_pointer_5,indent=0] +---- diff --git a/doc/pages/dom/numbers.adoc b/doc/pages/dom/numbers.adoc new file mode 100644 index 000000000..e1dda426c --- /dev/null +++ b/doc/pages/dom/numbers.adoc @@ -0,0 +1,85 @@ +//// +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_numbers] += Numbers +JSON numbers are represented using `std::int64_t`, `std::uint64_t`, and +`double`. When a <> is constructed from an unsigned integer, its +<> will be `kind::uint64`. Likewise, a <> constructed +from a signed integer will have `kind::int64`, or `kind::double_` if +constructed from a floating-point type: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_1,indent=0] +---- + +When accessing a number contained within a <>, the function used +must match the value's <> exactly; no conversions will be performed. +For example if `as_double` is called on a <> that contains +a `std::uint64_t`, an exception is thrown. Similarly, the function `if_double` +will return `nullptr` and calling `get_double` will result in undefined +behavior: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_2,indent=0] +---- + +In cases where you know that a <> contains a number but don't know +its <>, `value::to_number` can be used to convert the +<> to an arithmetic type: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_3,indent=0] +---- + +If the <> does not contain a number, or if the conversion is to an +integer type `T` and the number cannot be represented exactly by `T`, the +conversion will fail. Otherwise, the result is the number converted to `T` +as-if by `static_cast`: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_4,indent=0] +---- + +In settings where exceptions cannot be used, an overload of `value::to_number` +accepting {ref_error_code} can be used instead with identical semantics to its +throwing counterpart: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_5,indent=0] +---- + +When parsing a JSON document, the type used to represent a number is not +explicitly specified and must be determined from its value. In general, the +parser will choose the best type which can accurately store the number as it +appears in the document. Integers (i.e. numbers without decimals or exponents) +that cannot be represented by `std::uint64_t` and `std::int64_t` will be +represented as `double` to preserve their magnitude: + +[source] +---- +include::../../../test/doc_using_numbers.cpp[tag=doc_using_numbers_6,indent=0] +---- + +More formally, if the number: + +* contains a decimal point, or +* contains an exponent, or +* is negative and its value is less than `INT64_MIN`, or +* is positive and its value is greater than `UINT64_MAX`, + +then its type is `double`. Otherwise, if the number is positive and its value +is greater than `INT64_MAX`, then its type is `std::uint64_t`. All other +numbers are parsed as `std::int64_t`. diff --git a/doc/pages/dom/object.adoc b/doc/pages/dom/object.adoc new file mode 100644 index 000000000..6f15b6926 --- /dev/null +++ b/doc/pages/dom/object.adoc @@ -0,0 +1,95 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_object] += `object` +A <> stores an instance of <> as the underlying +representation for a JSON object. Instances of the <> type are +associative containers holding key and value pairs, where the key is +a <> and the mapped type is a <>. These containers +are modelled after standard maps with these properties: + +* The elements are stored contiguously as instances of <>. + +* Iterators are ordinary pointers, and may become invalidated on insertions + and removals. + +* The order of insertions is preserved, as long as there are no removals. + +* All inserted values will use the same {ref_memory_resource} as the container + itself. + +An empty object may be constructed without incurring any memory allocations +using the <>. +A <> can also be explicitly specified: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_1,indent=0] +---- + +Initializer lists consisting of two-element key value pairs can be used to +construct objects with initial contents. These constructors may allocate memory +and throw: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_2,indent=0] +---- + +Alternatively, elements may be inserted after construction: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_3,indent=0] +---- + +Similar to the `std` counterpart, elements may be accessed directly by their +key with bounds checking using <>, or without bounds checking +using <> which creates a null element if the key +does not already exist: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_4,indent=0] +---- + +Internally, the container computes a hash table over the keys +so that the complexity of lookups is in constant time, on average. + +[WARNING] +==== +Unlike traditional node based containers like `std::set`, there is no +guarantee of reference stability or iterator stability on insertions +and erasures. + +For example: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_objects_5,indent=0] +---- + +Using `arr` after adding another value to `obj` results in undefined behavior. +==== + +For the complete listing of all available member functions and nested types, +see the reference page for <>. + +As with `std::pair`, the <> type can be used with +structured bindings in {cpp}17. Specializations of `std::tuple_size`, +`std::tuple_element`, and overloads of <> are all provided for this +purpose. + +== Formatted Output + +When an <> is formatted to a {std_ostream}, the result is a valid +JSON. That is, the object will be output with curly braces and a comma +separated list of key/value pairs, as per the JSON specification. diff --git a/doc/pages/dom/overview.adoc b/doc/pages/dom/overview.adoc new file mode 100644 index 000000000..7c7593a9d --- /dev/null +++ b/doc/pages/dom/overview.adoc @@ -0,0 +1,63 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom] += Document Model +In this library the following types implement containers used to represent JSON +data in memory: + +.Container Types +[%autowidth] +|=== +|Type|Description + +| <> +| A sequence container of JSON values supporting dynamic size and fast, random + access. The interface and performance characteristics are similar to + {std_vector}. + +| <> +| An associative container of key-value pairs with unique keys, where the key + is a string and the mapped type is a JSON value. Search, insertion, and + removal have average contant-time complexity. In addition, elements are + stored contiguously in memory allowing cache-friendly iteration. + +| <> +| A contiguous range of characters. The library assumes that the contents of + the string holds only valid UTF-8. + +| <> +| A special variant which can hold one of any of the six standard JSON data + types. +|=== + +These containers are explored in-depth in the sections that follow. + +[NOTE] +==== +Sample code and identifiers used throughout are written as if the following +declarations are in effect: + +[source] +---- +#include +using namespace boost::json; +---- +==== + +:leveloffset: +1 +include::value.adoc[] +include::string.adoc[] +include::array.adoc[] +include::object.adoc[] +include::numbers.adoc[] +include::init_lists.adoc[] +include::nested_access.adoc[] +:leveloffset: -1 diff --git a/doc/pages/dom/string.adoc b/doc/pages/dom/string.adoc new file mode 100644 index 000000000..63cdb70d5 --- /dev/null +++ b/doc/pages/dom/string.adoc @@ -0,0 +1,103 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_string] += `string` + +Modifiable sequences of characters are represented using objects of type +<>. + +The interface and functionality of <> is the same as {std_string} +except that: + +* <> is not a class template, +* <> uses `char` as its character type, +* redundant overloads for string operations have been replaced + with a <>-based interface, +* access to characters in the range `[size(), capacity())` is permitted, +* <> is used instead of {req_Allocator}, and +* small buffer optimisation is guaranteed, which avoids allocating memory for + small strings. + +With augmented interface, operations requiring an input string are implemented +as a single overload with a parameter of type <>, and can +accept most string-like objects. Objects such as null terminated character +pointers, `std::string`, <>, subranges of strings, and objects +convertible to <> can all be passed to these functions. + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_4,indent=0] +---- + +More formally, `std::string` member function overloads that accept any of the +following parameter combinations as an input string: + +* a `std::string` parameter, or +* a `std::string` parameter and two `size_type` parameters that specify a + substring, or +* a parameter of a type convertible to <>, or +* a parameter of a type convertible to <> and two + `size_type` parameters that specify a substring, or +* a `const_pointer` parameter, or +* a parameter of type `const_pointer` and a `size_type` parameter that + specifies the length of the string + +are replaced with an overload accepting a <> parameter. + +This design removes several redundant overloads from the interface. For +example, the 11 overloads of `std::string::insert` are reduced to just 3 in +<>, while still providing identical functionality. In addition +to these changes, overloads taking a `std::initializer_list` parameter +have been removed. Such overloads have little use, as they serve as little more +than wrappers for arrays with an inefficient syntax: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_3,indent=0] +---- + +With the removal of overloads that specify parameters for a substring, a member +function `subview` that returns a <> is provided to +facilitate cheap substring operations: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_2,indent=0] +---- + +A <> may be constructed using the <> without incurring any memory allocations. Alternatively, +a <> can be provided explicitly: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_1,indent=0] +---- + +== Formatted Output + +When a <> is formatted to a {std_ostream}, the result is a valid +JSON. That is, the result will be double quoted and the contents properly +escaped per the JSON specification. + +== Accessing Storage Beyond `size()` + +<> directly supports access to its storage in the range `[size(), +capacity())`. This can be used for efficient assembly of a string from several +parts. After the string is assembled, use the member function +<> to update the string's size and insert the null terminator. +For example: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_strings_5,indent=0] +---- diff --git a/doc/pages/dom/value.adoc b/doc/pages/dom/value.adoc new file mode 100644 index 000000000..58881fcad --- /dev/null +++ b/doc/pages/dom/value.adoc @@ -0,0 +1,272 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#dom_value] += `value` +JSON documents are represented in memory as instances of <>: a +{req_Regular} type which satisfies {req_DefaultConstructible}, +{req_CopyConstructible}, {req_CopyAssignable}, {req_MoveConstructible}, +{req_MoveAssignable}, and many of the requirements of allocator-aware +containers. It is implemented as a +https://en.wikipedia.org/wiki/Tagged_union[__variant__] internally, and can +dynamically store any of the six defined JSON value types: + +* **null**: A + https://en.cppreference.com/w/cpp/utility/variant/monostate[__monostate__] + value, equivalent to `nullptr`. + +* **boolean**: A boolean: either `true` or `false`. + +* **number**: An integral or floating point value. + +* **string**: A sequence of zero or more Unicode characters, + similar to {std_string}. + +* **array**: An ordered list of values, like {std_vector}. + +* **object**: A collection of name/value pairs, also known as an + https://en.wikipedia.org/wiki/Associative_array[__associative array__]. + +== Working With Values +A <> constructed from `nullptr` or default constructed represents +a null JSON element: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_1,indent=0] +---- + +The member function <> may be used to query the +kind stored in the value. Alternatively, member functions like +<> +<> may be used to check whether or not +the value is a particular kind: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_2,indent=0] +---- + +Functions like <> actually return +a pointer to the object if the value is an object, otherwise they return null. +This allows them to be used both in boolean contexts as above, and in +assignments or conditional expressions to capture the value of the pointer: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_3,indent=0] +---- + +After a <> is constructed, its kind can change depending on what is +assigned to it, or by calling functions such as +<> or +<>. If the assignment is +successful, in other words it completes without any exceptions then the value +is replaced. Otherwise, the value is unchanged. All operations which can fail +to modify a value offer the strong exception safety guarantee. + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_4,indent=0] +---- + +The following table shows all of the ways to determine and access the contents +of a `!value!`: + +.<> Accessors +[%autowidth,cols=8] +|=== +|Kind +|Representation +|Emplacement +|Kind Test +|Pointer Access +|`result` Access +|Checked Access +|Unchecked Access + +|<> +|<> +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|<> +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|<> +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/types/integer[`std::int64_t`] +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/types/integer[`std::uint64_t`] +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/language/types[`double`] +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/language/types[`bool`] +|<> +|<> +|<> +|<> +|<> +|<> + +|<> +|https://en.cppreference.com/w/cpp/language/nullptr[`std::nullptr_t`] +|<> +|<> +^|— +|<> +^|— +^|— + +|=== + +The emplace members of <> return a typed reference to the underlying +representation. For example, the call to +<> in the previous example +returns a <>. This table shows the underlying type for +each kind: + +|=== +|Kind|Type|Description + +| <> +| <> +| An associative array of string keys mapping to <> elements with an + interface similar to {std_unordered_map}, that remembers insertion order. + +| <> +| <> +| An ordered list of <> elements with an interface similar to + {std_vector}. + +| <> +| <> +| A https://en.wikipedia.org/wiki/UTF-8[__UTF-8__] encoded + https://en.wikipedia.org/wiki/Unicode[Unicode] + https://en.wikipedia.org/wiki/String_(computer_science)[string] of characters + with an interface similar to {std_string}. + +| <> +| `std::int64_t` +| A 64 bit signed integer. + +| <> +| `std::uint64_t` +| A 64 bit unsigned integer. + +| <> +| `double` +| A `double` holding a floating-point value. + +| <> +| https://en.cppreference.com/w/cpp/keyword/bool[`bool`] +| A `bool` holding `true` or `false`. + +| <> +^| — +| A monostate value representing null. +|=== + +The return value from emplace can be used to perform assignment or to capture a +reference to the underlying element for later inspection or modification: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_5,indent=0] +---- + +If the <> of a <> is known, functions such as +<> or +<> may be used to obtain a reference to +the underlying representation without changing the existing value: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_6,indent=0] +---- + +However, as shown above these functions throw an exception if the kind in the +<> does not match the kind denoted by the function signature. This +can be used as a concise form of validation: access values as if they were the +right type, but handle the resulting exception indicating if the schema of the +JSON is not valid. + +We can query a value for its underlying representation of a particular kind in +a way that does not throw exceptions by requesting a pointer which may be null, +instead of a reference. Here we use <> +to conditionally perform an assignment without using exceptions: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_7,indent=0] +---- + +[TIP] +==== +If the value's kind is known statically, a reference to the underlying +representation may be obtained by dereferencing the pointer without checking. +This avoids the code overhead of the possible exception when using, for example +<>: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_8,indent=0] +---- +==== + +Functions returning {ref_result} allow you to use both approaches: + +[source] +---- +include::../../../test/snippets.cpp[tag=snippet_value_9,indent=0] +---- + +=== Formatted Output +When a <> is formatted to a {std_ostream}, the result is serialized +JSON as if by calling <>. diff --git a/doc/pages/examples.adoc b/doc/pages/examples.adoc new file mode 100644 index 000000000..f4e661c5a --- /dev/null +++ b/doc/pages/examples.adoc @@ -0,0 +1,41 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[pagelevels=1] += Examples + +== Pretty + +[source] +---- +include::../../example/pretty.cpp[tag=example_pretty,indent=0] +---- + +[#examples_validate] +== Validate + +[source] +---- +include::../../example/validate.cpp[tag=example_validate,indent=0] +---- + +== Allocator-Aware Conversion + +[source] +---- +include::../../example/use_allocator.cpp[tag=example_use_allocator,indent=0] +---- + +== CBOR + +[source] +---- +include::../../example/cbor.cpp[tag=example_cbor,indent=0] +---- diff --git a/doc/pages/faq.adoc b/doc/pages/faq.adoc new file mode 100644 index 000000000..73c45231d --- /dev/null +++ b/doc/pages/faq.adoc @@ -0,0 +1,38 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Frequently Asked Questions + +"Isn't simdjson faster?":: These libraries are not comparable. The output of +the simdjson parser is a read-only structure. In other words, it can't be +changed, and the only way to create one is by parsing a JSON string. On the +other hand, Boost.JSON allows you to modify the container holding the parsed +JSON, or even build a JSON document from scratch through the container +interface. + +"Why not use a standard {req_Allocator}?:: Using standard allocators would +require that <> be declared as a class template, which would impose +an additional compilation burden. By avoiding the template, most of the +function definitions in the library can be excluded from the headers and +emitted in a separate static or dynamic library. + +"Why use <> over {ref_polymorphic_allocator}?:: +{ref_polymorphic_allocator} treats the memory resource as a reference with +respect to ownership. Boost.JSON uses a reference counted smart pointer +container to simplify the lifetime management of memory resources. In addition +to being reference counted, <> can function as an uncounted +reference wrapper around a {ref_memory_resource}. + + +"Why <> instead of {std_string}?":: The string provided by the +library uses the <> allocator model, has the same interface on +all C++ versions, and has an optimized class layout to keep the size of JSON +values small. <> also implements an improved interface that +replaces extraneous overloads with ones that use <>. diff --git a/doc/pages/io/overview.adoc b/doc/pages/io/overview.adoc new file mode 100644 index 000000000..b47311670 --- /dev/null +++ b/doc/pages/io/overview.adoc @@ -0,0 +1,22 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#input_output,pagelevels=2] += Input/Output +The library provides parsing and serialization algorithms to transform JSON to +and from the <> container as needed. This is accomplished through +free functions and classes, described as follows. + +:leveloffset: +1 + +include::parsing.adoc[] +include::serializing.adoc[] + +:leveloffset: -1 diff --git a/doc/pages/io/parsing.adoc b/doc/pages/io/parsing.adoc new file mode 100644 index 000000000..2ffedaf97 --- /dev/null +++ b/doc/pages/io/parsing.adoc @@ -0,0 +1,265 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[pagelevels=1] += Parsing +Parsing is the process where a serialized JSON text is validated and decomposed +into elements. The library provides these functions and types to assist with +parsing: + +.Parsing Functions and Types +|=== +|Name|Description + +| <> +| A SAX push parser implementation which converts a serialized JSON text into + a series of member function calls to a user provided handler. This allows + custom behaviors to be implemented for representing the document in memory. + +| <> +| A structure used to select which extensions are enabled during parsing. + +| <> +| Parse a string containing a complete serialized JSON text, and return + a <>. + +| <> +| A stateful DOM parser object which may be used to efficiently parse a series + of JSON texts each contained in a single contiguous character buffer, + returning each result as a <>. + +| <> +| A stateful DOM parser object which may be used to efficiently parse a series + of JSON texts incrementally, returning each result as a <>. + +| <> +| A low level building block used for efficiently building a <>. The + parsers use this internally, and users may use it to adapt foreign parsers to + produce this library's containers. +|=== + +The <> function offers a simple interface for converting +a serialized JSON text to a <> in a single function call. This +overload uses exceptions to indicate errors: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_1,indent=0] +---- + +Alternatively, an {ref_error_code} can be used: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_2,indent=0] +---- + +Even when using error codes, exceptions thrown from the underlying +{ref_memory_resource} are still possible: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_3,indent=0] +---- + +The <> returned in the preceding examples use the +<>. The following code uses +a <>, which results in faster parsing. `jv` is marked +`const` to prevent subsequent modification, because containers using +a monotonic resource waste memory when mutated. + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_4,indent=0] +---- + +== Non-Standard JSON +Unless otherwise specified, the parser in this library is strict. It recognizes +only valid, standard JSON. The parser can be configured to allow certain +non-standard extensions by filling in a <> structure and +passing it by value. By default all extensions are disabled: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_5,indent=0] +---- + +When building with {cpp}20 or later, the use of +https://en.cppreference.com/w/cpp/language/aggregate_initialization#Designated_initializers[designated +initializers] with <> is possible: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_6,indent=0] +---- + +When `allow_invalid_utf16` is enabled, the parser will not throw an error in +the case of illegal leading, trailing, or half a surrogate. Instead, it will +replace the invalid UTF-16 code point(s) with the Unicode replacement +character. + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_15,indent=0] +---- + +CAUTION: When enabling comment support take extra care not to drop whitespace +when reading the input. For example, `std::getline` removes the endline +characters from the string it produces. + +== Full Precision Number Parsing +The default algorithm that the library uses to parse numbers is fast, but may +result in slight precision loss. This may not be suitable for some +applications, so there is an option to enable an alternative algorithm that +doesn't have that flaw, but is somewhat slower. To do this, you also need to +use <> structure. + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_precise,indent=0] +---- + +Note that full precision number parsing requires the algorithm to see the full +number. This means, that when used with <>, additional +memory allocations may be necessary to store the number parts which were so far +accepted by the parser. The library does try its best to avoid such +allocations. + +== Parser +Instances of <> and <> offer functionality +beyond what is available when using the <> free functions: + +* More control over memory +* Streaming API, parse input JSON incrementally +* Improved performance when parsing multiple JSON texts +* Ignore non-JSON content after the end of a JSON text + +The parser implementation uses temporary storage space to accumulate values +during parsing. When using the <> free functions, this storage is +allocated and freed in each call. However, by declaring an instance of +<> or <>, this temporary storage can be reused +when parsing more than one JSON text, reducing the total number of dynamic +memory allocations. + +To use the <>, declare an instance. Then call <> +once with the buffer containing representing the input JSON. Finally, call +<> to take ownership of the resulting <> upon +success. This example persists the parser instance in a class member to reuse +across calls: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_7,indent=0] +---- + +Sometimes a protocol may have a JSON text followed by data that is in +a different format or specification. The JSON portion can still be parsed by +using the function <>. Upon success, the return value +will indicate the number of characters consumed from the input, which will +exclude the non-JSON characters: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_8,indent=0] +---- + +The parser instance may be constructed with parse options which allow some +non-standard JSON extensions to be recognized: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_9,indent=0] +---- + +== Streaming Parser +The <> implements +a https://en.wikipedia.org/wiki/Online_algorithm[__streaming algorithm__]; it +allows incremental processing of large JSON inputs using one or more contiguous +character buffers. The entire input JSON does not need to be loaded into memory +at once. A network server can use the streaming interface to process incoming +JSON in fixed-size amounts, providing these benefits: + +* CPU consumption per I/O cycle is bounded +* Memory consumption per I/O cycle is bounded +* Jitter, unfairness, and latency is reduced +* Less total memory is required to process the full input + +To use the <>, declare an instance. Then call +<> zero or more times with successive buffers +representing the input JSON. When there are no more buffers, call +<>. The function <> returns +`true` after a successful call to `write` or `finish` if parsing is complete. + +In the following example a JSON text is parsed from standard input a line at +a time. Error codes are used instead. The function <> +is used to indicate the end of the input: + +CAUTION: This example will break, if comments are enabled, because of +`std::getline` use (see the warning in <> section). + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_10,indent=0] +---- + +We can complicate the example further by extracting _several_ JSON values from +the sequence of lines. + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_14,indent=0] +---- + +== Controlling Memory +After default construction, or after <> is called with +no arguments, the <> produced after a successful parse operation +uses the default memory resource. To use a different memory resource, call +`reset` with the resource to use. Here we use a <>, +which is optimized for parsing but not subsequent modification: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_11,indent=0] +---- + +To achieve performance and memory efficiency, the parser uses a temporary +storage area to hold intermediate results. This storage is reused when parsing +more than one JSON text, reducing the total number of calls to allocate memory +and thus improving performance. Upon construction, the memory resource used to +perform allocations for this temporary storage area may be specified. +Otherwise, the default memory resource is used. In addition to a memory +resource, the parser can make use of a caller-owned buffer for temporary +storage. This can help avoid dynamic allocations for small inputs. The +following example uses a four kilobyte temporary buffer for the parser, and +falls back to the default memory resource if needed: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_12,indent=0] +---- + +== Avoiding Dynamic Allocations +Through careful specification of buffers and memory resources, it is possible +to eliminate all dynamic allocation completely when parsing JSON, for the case +where the entire JSON text is available in a single character buffer, as shown +here: + +[source] +---- +include::../../../test/doc_parsing.cpp[tag=doc_parsing_13,indent=0] +---- + +== Custom Parsers +Users who wish to implement custom parsing strategies may create their own +handler to use with an instance of <>. The handler implements +the function signatures required by SAX event interface. In +<> example we define the "null" parser, which throws out the +parsed results, to use in the implementation of a function that determines if +a JSON text is valid. diff --git a/doc/pages/io/serializing.adoc b/doc/pages/io/serializing.adoc new file mode 100644 index 000000000..8bacee21f --- /dev/null +++ b/doc/pages/io/serializing.adoc @@ -0,0 +1,67 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Serializing + +Serialization is the process where a JSON document represented in memory by +a <> is turned into a sequence of characters. The library provides +the following free functions and types for serialization: + +.Serialization Functions and Types +|=== +|Name|Description + +| <> +| Serialize a <>, <>, <>, or <> + to a {std_ostream}. + +| <> +| Return a {std_string} representing a serialized <>, <>, + <>, or <>. + +| <> +| A stateful object which may be used to efficiently serialize one or more + instances of <>, <>, <>, or <>. +|=== + +To facilitate debugging and ease of output, library container types may be +written to standard output streams using the stream operator: + +[source] +---- +include::../../../test/doc_serializing.cpp[tag=doc_serializing_1,indent=0] +---- + +The <> function converts a <> into a {std_string}: + +[source] +---- +include::../../../test/doc_serializing.cpp[tag=doc_serializing_2,indent=0] +---- + +In situations where serializing a <> in its entirety is inefficient +or even impossible, <> can be used to serialize a <> +incrementally. This may be done for a variety of reasons, such as to avoid +buffering the entire output, or to ensure that a fixed amount of work is +performed in each cycle. Instances of <> maintain an output +state using internal dynamically allocated structures, with an interface to +retrieve successive buffers of the serialized output into a caller provided +buffer. Here is an example, demonstrating how <> may be +implemented using a <>: + +[source] +---- +include::../../../include/boost/json/impl/serialize.ipp[tag=example_operator_lt_lt,indent=0] +---- + +As with the parser, the serializer may be reused by calling +<>. This sets up the object to serialize a new instance +and retains previously allocated memory. This can result in performance +improvements when multiple variables are serialized. diff --git a/doc/pages/main.adoc b/doc/pages/main.adoc new file mode 100644 index 000000000..bbd16efcf --- /dev/null +++ b/doc/pages/main.adoc @@ -0,0 +1,39 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + += Boost.JSON +(C) 2019--2020 Vinnie Falco; (C) 2020 Krystian Stasiowski; (C) 2022 Dmitry Arkhipov +:idprefix: +:sectanchors: +:toclevels: 2 +:toc: left +:pagelevels: 2 +:source-highlighter: rouge +:source-language: c++ + +include::definitions.adoc[] + +include::../../README.adoc[] + +:leveloffset: +1 + +include::quick_look.adoc[] +include::dom/overview.adoc[] +include::allocators/overview.adoc[] +include::io/overview.adoc[] +include::conversion/overview.adoc[] +include::examples.adoc[] +include::faq.adoc[] +include::benchmarks.adoc[] +include::comparison.adoc[] +include::reference.adoc[] +include::../../CHANGELOG.adoc[] + +:leveloffset: -1 diff --git a/doc/pages/quick_look.adoc b/doc/pages/quick_look.adoc new file mode 100644 index 000000000..ee12d4eb3 --- /dev/null +++ b/doc/pages/quick_look.adoc @@ -0,0 +1,277 @@ +//// +Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[pagelevels=1,toclevels=1] += Quick Look +Here we highlight important features through example code to help convey the +style of the interface. We begin by including the library header file which +brings all the symbols into scope. Alternatively, individual headers may be +included to obtain the declarations for specific types: + +[source] +---- +#include +---- + +In order to link your program you will need to link with a built library. +Alternatively you can use the header-only configuration simply by including +this header file in any __one__ of your new or existing source files: + +[source] +---- +#include +---- + +[NOTE] +==== +Sample code and identifiers used throughout are written as if the following +declarations are in effect: + +[source] +---- +#include +using namespace boost::json; +---- +==== + +[#quick_look_values] +== Values +Say you want to recreate this JSON object in a container: + +[source,json] +---- +{ + "pi": 3.141, + "happy": true, + "name": "Boost", + "nothing": null, + "answer": { + "everything": 42 + }, + "list": [1, 0, 2], + "object": { + "currency": "USD", + "value": 42.99 + } +} +---- + +In this library the types <>, <>, and <> +hold JSON arrays, objects, and strings respectively while the type +<> is a special variant which can hold any JSON element. Here we +construct an empty object and then insert the elements above: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_1] +---- + +While keys are strings, the mapped type of objects and the element type of +arrays is the aforementioned type <> which can hold any JSON +element, as seen in the previous assignments. Instead of building the JSON +document using a series of function calls, we can build it in one statement +using an initializer list: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_2] +---- + +When a <>, <>, or <> is assigned or +constructed from an initializer list, the creation of the new value happens +only once. This makes initializer lists equally efficient as using the other +ways to create a value. The types in this library are first-class, supporting +copy and move construction and assignment: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_3] +---- + +[#quick_look_allocators] +== Allocators +To permit custom memory allocation strategies, these containers all allow +construction with a <> which is a smart pointer to +a {ref_memory_resource}. The constructor signatures have the same ordering as +their `std` equivalents which use {req_Allocator} parameters. Once a container +is constructed its memory resource can never change. Here we create an array +without performing any dynamic allocations: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_4] +---- + +The containers in this library enforce the invariant that every element of the +container will use the same memory resource: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_5] +---- + +When a library type is used as the element type of a PMR container; that is, a +container which uses a {ref_polymorphic_allocator}, the memory resource will +automatically propagate to the type and all of its children: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_6] +---- + +Up until now we have shown how values may be constructed from a memory resource +pointer, where ownership is not transferred. In this case the caller is +responsible for ensuring that the lifetime of the resource is extended for the +life of the container. Sometimes you want the container to acquire shared +ownership of the resource. This is accomplished with +<>: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_7] +---- + +A counted memory resource will not be destroyed until every container with +shared ownership of the resource is destroyed. + +[#quick_look_parsing] +== Parsing +JSON can be parsed into the value container in one step using a free function. +In the following snippet, a parse error is indicated by a thrown exception: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_8] +---- + +Error codes are also possible: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_9] +---- + +By default, the parser is strict and only accepts JSON compliant with the +standard. However this behavior can be relaxed by filling out an options +structure enabling one or more extensions. Here we use a static buffer and +enable two non-standard extensions: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_10] +---- + +The parser in this library implements a +https://en.wikipedia.org/wiki/Online_algorithm[__streaming algorithm__]; it can +process JSON piece-by-piece, without the requirement that the entire input is +available from the start. The parser uses a temporary memory allocation to do +its work. If you plan on parsing multiple JSONs, for example in a network +server, keeping the same parser instance will allow re-use of this temporary +storage, improving performance. + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_11] +---- + +With strategic use of the right memory resources, parser instance, and +calculated upper limits on buffer sizes, it is possible to parse and examine +JSON without __any__ dynamic memory allocations. This is explored in more +detail in a later section. + +[#quick_look_serializing] +== Serializing +Simple free functions are provided for serializing a <> to +a {std_string} containing JSON: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_12] +---- + +The serializer in this library implements +a https://en.wikipedia.org/wiki/Online_algorithm[__streaming algorithm__]; it +can output JSON a piece at a time, without the requirement that the entire +output area is allocated at once: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_13] +---- + +[#quick_look_conversion] +== Value Conversion +Given a user-defined type: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_14] +---- + +We can define a conversion from the user-defined type to a <> by +defining an overload of `tag_invoke` in the same namespace. This maps +`customer` to a JSON object: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_15] +---- + +This allows us to use the library function <> to produce +a <> from our type: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_16] +---- + +The library knows what to do with standard containers. Here we convert an array +of customers to a value: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_17] +---- + +To go from JSON to a user-defined type we use <>, which uses +another overload of `tag_invoke`. This converts a JSON value to a `customer`. +It throws an exception if the contents of the value do not match what is +expected: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_18] +---- + +The code above defines the convenience function `extract`, which deduces the +types of the struct members. This works, but requires that the struct is +{req_DefaultConstructible}. An alternative is to construct the object directly, +which is a little more verbose but doesn't require default construction: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_19] +---- + +Now we can construct customers from JSON: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_20] +---- + +The library's generic algorithms recognize when you are converting +a <> to a container which resembles an array or object, so if you +wanted to turn a JSON array into a vector of customers you might write: + +[source] +---- +include::../../test/doc_quick_look.cpp[tag=doc_quick_look_21] +---- diff --git a/doc/pages/reference.adoc b/doc/pages/reference.adoc new file mode 100644 index 000000000..1cf4ad63c --- /dev/null +++ b/doc/pages/reference.adoc @@ -0,0 +1,97 @@ +//// +Copyright (c) 2025 Dmitry Arkhipov (grisumbras@yandex.ru) + +Distributed under the Boost Software License, Version 1.0. (See accompanying +file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +Official repository: https://github.com/boostorg/json +//// + +[#ref,pagelevels=3,toclevels=1] += Reference + +[cols=4*a,autowidth] +|=== +4+|JSON + +a| *Classes* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +a| *Aliases* + +<> + +<> + +*Functions* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +| *Operators* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + + +*Constants* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +| *Type Traits* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +*P0308R0* + +<> + +<> + +<> + +|=== + +include::{entities-file}[leveloffset=+1] diff --git a/doc/reference.adoc.jinja2 b/doc/reference.adoc.jinja2 new file mode 100644 index 000000000..6d28d4c5e --- /dev/null +++ b/doc/reference.adoc.jinja2 @@ -0,0 +1,938 @@ +{%- macro write_entity(entity) -%} + {% if entity.access != Access.private or Config.include_private -%} + {%- if entity is Namespace -%} + {{ write_namespace(entity) }} + {%- elif entity is Type -%} + {{ write_type(entity) }} + {%- elif entity is OverloadSet -%} + {{ write_overload_set(entity) }} + {%- elif entity is Variable -%} + {{ write_variable(entity) }} + {%- endif -%} + {%- endif -%} +{%- endmacro -%} + + +{%- macro write_namespace(entity) -%} + {%- for m in entity.members.values() | select("Type") | sort -%} + {{ write_type(m) }} + {%- endfor -%} + + {%- for m in entity.members.values() | select("OverloadSet") | sort -%} + {{ write_overload_set(m) }} + {%- endfor -%} + + {%- for m in entity.members.values() | select("Variable") | sort -%} + {{ write_variable(m) }} + {%- endfor -%} +{%- endmacro -%} + + +{%- macro write_type(entity) %} +{% call(segment) section(entity) %} +{%- if segment == "summary" -%} + +{%- if entity is Scope -%} +{#- public members -#} +{{ simple_summary_table( + entity.members.values() + | select("Type") + | selectattr("access", "eq", Access.public), + 'Types') }} +{{ function_summary_table( + entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.public) + | selectattr("kind", "eq", FunctionKind.nonstatic), + 'Member Functions') }} {#- -#} +{{ function_summary_table( + entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.public) + | selectattr("kind", "eq", FunctionKind.static), + 'Static Member Functions') }} +{{ simple_summary_table( + entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.public) + | rejectattr("is_static"), + 'Data Members') }} +{{ simple_summary_table( + entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.public) + | selectattr("is_static") + | reject("in", entity.objects), + 'Static Members') }} +{{ function_summary_table( + entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.public) + | selectattr("kind", "eq", FunctionKind.friend), + 'Friends') }} +{{ function_summary_table( + entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.public) + | selectattr("kind", "eq", FunctionKind.free), + 'Related Non-member Functions') }} +{#- protected members -#} +{{ simple_summary_table( + entity.members.values() + | select("Type") + | selectattr("access", "eq", Access.protected), + 'Protected Types') }} {#- -#} +{{ function_summary_table( + entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.protected) + | selectattr("kind", "eq", FunctionKind.nonstatic), + 'Protected Member Functions') }} {#- -#} +{{ function_summary_table( + entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.protected) + | selectattr("kind", "eq", FunctionKind.static), + 'Protected Static Member Functions') }} {#- -#} +{{ simple_summary_table( + entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.protected) + | rejectattr("is_static"), + 'Protected Data Members') }} {#- -#} +{{ simple_summary_table( + entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.protected) + | selectattr("is_static") + | reject("in", entity.objects), + 'Protected Static Members') }} {#- -#} +{#- private members -#} +{%- if Config.get('include_private') %} +{{ simple_summary_table( + entity.members.values() + | select("Type") + | selectattr("access", "eq", Access.private), + 'Private Types') }} {#- -#} +{{ function_summary_table( + entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.private) + | selectattr("kind", "eq", FunctionKind.nonetatic), + 'Private Member Functions') }} {#- -#} +{{ function_summary_table( + entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.private) + | selectattr("kind", "eq", FunctionKind.static), + 'Private Static Member Functions') }} {#- -#} +{{ simple_summary_table( + entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.private) + | rejectattr("is_static"), + 'Private Data Members') }} {#- -#} +{{ simple_summary_table( + entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.private) + | selectattr("is_static") + | reject("in", entity.objects), + 'Private Static Members') }} {#- -#} +{%- endif -%} +{%- endif -%} + +{% if entity is Enum -%} +{%- call(member) summary_table(entity.objects, "Values") -%} + |``{{ escape(member.name) }}`` + |{{ description(member.brief) | trim }} +{%- endcall -%} +{%- else -%} +{{ simple_summary_table(entity.objects, 'Values') }} +{%- endif -%} + +{%- elif segment == "members" -%} + +{%- if entity is Scope -%} +{#- public member subsections #} +:leveloffset: +1 + +{% for member in entity.members.values() + | select("Type") + | selectattr("access", "eq", Access.public) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.public) + | selectattr("kind", "eq", FunctionKind.nonstatic) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.public) + | selectattr("kind", "eq", FunctionKind.static) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.public) + | rejectattr("is_static") -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.public) + | selectattr("is_static") + | reject("in", entity.objects) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.public) + | selectattr("kind", "eq", FunctionKind.friend) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.public) + | selectattr("kind", "eq", FunctionKind.free) -%} +{{ write_entity(member) }} +{% endfor %} +{#- protected member subsections -#} +{%- for member in entity.members.values() + | select("Type") + | selectattr("access", "eq", Access.protected) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.protected) + | selectattr("kind", "eq", FunctionKind.nonstatic) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.protected) + | selectattr("kind", "eq", FunctionKind.static) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.protected) + | rejectattr("is_static") -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.protected) + | selectattr("is_static") + | reject("in", entity.objects) -%} +{{ write_entity(member) }} +{% endfor %} +{#- private members -#} +{%- if Config.get('include_private') %} +{%- for member in entity.members.values() + | select("Type") + | selectattr("access", "eq", Access.private) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.private) + | selectattr("kind", "eq", FunctionKind.nonstatic) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("OverloadSet") + | selectattr("access", "eq", Access.private) + | selectattr("kind", "eq", FunctionKind.static) -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.private) + | rejectattr("is_static") -%} +{{ write_entity(member) }} +{% endfor %} +{%- for member in entity.members.values() + | select("Variable") + | selectattr("access", "eq", Access.private) + | selectattr("is_static") + | reject("in", entity.objects) -%} +{{ write_entity(member) }} +{% endfor %} +{% endif -%} + +:leveloffset: -1 +{%- endif -%} + +{%- endif -%} + +{% endcall %} +{% endmacro -%} + + +{%- macro write_overload_set(oset) -%} +[id={{ anchor(oset[0]) }} +{%- if oset.location -%} + ,source-location="include/{{oset.location.file}}" +{%- endif -%} +] += ``{{ abridged_fqn(oset[0]) }}`` +{{ description(oset[0].brief) }} + +{{ heading('Synopsis') }} +{% if oset.location and (oset.scope is Namespace or oset.is_friend) -%} + Defined in header {{ source_header(oset.location.file) }}. +{%- endif %} + +[source,cpp,subs="+macros,+attributes"] +---- +{{ overload_set_declaration(oset) }} +---- + +{% if oset[0].description -%} + {{ oset_description(oset, title='Description') }} +{%- endif %} + +{% if Config.get('convenience_header') + and oset.location + and (oset.scope is Namespace + or (oset.kind in (FunctionKind.free, FunctionKind.friend))) -%} +Convenience header {{ source_header(Config.convenience_header) }} +{%- endif %} + +{% endmacro -%} + + +{%- macro write_variable(entity) -%} +{%- call(segment) section(entity) %}{% endcall -%} +{%- endmacro -%} + + +{%- macro section(entity) %} +[id={{ anchor(entity) }} +{%- if entity.location -%} + ,source-location="include/{{entity.location.file}}" +{%- endif -%} +] += ``{{ abridged_fqn(entity) }}`` +{{ description(entity.brief) }} + +{{ heading('Synopsis') }} +{% if entity.location + and (entity.scope is Namespace + or (entity is Function and entity.is_friend)) -%} + Defined in header {{ source_header(entity.location.file) }}. +{%- endif %} + +[source,cpp,subs="+macros,+attributes"] +---- +{{ entity_declaration(entity) }} +---- + +{{ caller("summary") }} + +{% if entity.description -%} + {{ description(entity.description, title='Description') }} +{%- endif %} + +{% if entity.location + and (entity.scope is Namespace + or (entity is Function + and entity.kind in (FunctionKind.free, FunctionKind.friend))) -%} +Convenience header {{ source_header(Config.convenience_header) }}. +{%- endif %} +{{ caller("members") }} +{% endmacro -%} + + +{% macro description(parts, nesting=1, title=None) -%} +{%- for part in parts -%} +{%- if loop.first and title + and part is not Section and part is not ParameterList + -%} +{{ heading(title) }} +{% endif -%} + +{%- if part is Paragraph -%} + {{ phrase(part) }}{{ "\n\n" }} +{%- elif part is List -%} +{{ itemised(part, nesting) }} +{%- elif part is Section -%} +{{ subsection(part) }} +{%- elif part is CodeBlock %} +[source] +---- +{% for line in part -%} +{{line}} +{% endfor -%} +---- +{% elif part is ParameterList -%} +{{ parameter_list(part) }} +{% elif part is Table -%} +{{ table(part) }} +{%- else -%} +{{ part.unhandled_type() }} +{%- endif -%} +{%- endfor -%} +{%- endmacro -%} + + +{%- macro oset_description(oset, nesting=1, title=None) -%} +{%- set ns = namespace(done_parameters=False) -%} +{%- for part in oset[0].description -%} +{%- if loop.first and title + and part is not Section and part is not ParameterList + -%} +{{ heading(title) }} +{% endif -%} + + +{%- if part is Paragraph -%} + {{ phrase(part) }}{{ "\n\n" }} +{%- elif part is List -%} +{{ itemised(part, nesting) }} +{%- elif part is Section -%} + {%- if part.kind == 'see' -%} + {%- if not ns.done_parameters -%} + {%- set ns.done_parameters = true -%} + {{ oset_all_parameter_lists(oset) }} + {%- endif -%} + {%- endif -%} + {%- if not part.kind == 'return' %}{{ subsection(part) }}{% endif -%} +{%- elif part is CodeBlock %} +[source] +---- +{% for line in part -%} +{{line}} +{% endfor -%} +---- +{% elif part is ParameterList -%} +{%- if not ns.done_parameters -%} +{%- set ns.done_parameters = true -%} +{{ oset_all_parameter_lists(oset) }} +{%- endif -%} +{%- elif part is Table -%} +{{ table(part) }} +{%- else -%} +{{ part.unhandled_type() }} +{%- endif -%} +{%- endfor -%} +{%- if not ns.done_parameters -%} + {{ oset_all_parameter_lists(oset) }} +{%- endif -%} +{%- endmacro -%} + + +{%- macro oset_all_parameter_lists(oset) -%} +{%- for kind in [ + ParameterList.TemplateParameters, + ParameterList.ReturnValues, + ParameterList.Parameters, + ParameterList.Exceptions] -%} +{{ oset_parameter_list(oset, kind) }} +{% endfor -%} +{%- endmacro -%} + + +{%- macro itemised(lst, nesting) -%} + {%- for item in lst -%} + {%- for mark in range(0, nesting) -%} + {%- if lst.is_ordered -%} + . + {%- else -%} + * + {%- endif %} + {%- endfor %} {{ description(item, nesting + 1) }} + {%- endfor -%} +{%- endmacro -%} + + +{%- macro subsection(sub) -%} +{%- if sub.kind in ['note', 'warning', 'attention'] -%} +{{ admonition(sub) }} +{% else -%} +{{ heading('') }} +{%- if sub.title %}{{ phrase(sub.title) }} +{%- elif sub.kind -%} + {%- if sub.kind == 'see' %}See Also + {%- elif sub.kind == 'return' %}Return Value + {%- elif sub.kind == 'author' %}Author + {%- elif sub.kind == 'authors' %}Authors + {%- elif sub.kind == 'version' %}Version + {%- elif sub.kind == 'since' %}Since + {%- elif sub.kind == 'date' %}Date + {%- elif sub.kind == 'pre' %}Preconditions + {%- elif sub.kind == 'post' %}Postconditions + {%- elif sub.kind == 'copyright' %}Copyright + {%- elif sub.kind == 'invariant' %}Invariants + {%- elif sub.kind == 'remark' %}Remarks + {%- elif sub.kind == 'par' %}Paragraph + {%- elif sub.kind == 'rcs'%}RCS + {%- else %}Unknown + {%- endif -%} +{%- endif %} +{{ description(sub) }} +{%- endif -%} +{%- endmacro -%} + + +{%- macro admonition(sub) -%} +[ +{%- if sub.kind == 'warning' %}WARNING +{%- elif sub.kind == 'note' %}NOTE +{%- elif sub.kind == 'attention' %}IMPORTANT +{%- else %}{{ sub.kind.upper() }} +{%- endif -%} +] +==== +{{ description(sub) }} +==== +{% endmacro -%} + + +{%- macro table(part) -%} +{% if part.caption %}.{{ phrase(part.caption) }}{% endif %} +|=== +{%- for row in part %} +{% for cell in row -%} + a|{{ description(cell) }} +{%- endfor %} +{% endfor %} +|=== +{% endmacro -%} + + +{%- macro parameter_list(part) -%} +{%- set ns = namespace(title="", col="") -%} +{%- if part.kind == ParameterList.Parameters -%} + {%- set ns.title = "Parameters" -%} + {%- set ns.col1 = "Name" -%} + {%- set ns.col2 = "Description" -%} +{%- elif part.kind == ParameterList.TemplateParameters -%} + {%- set ns.title = "Template Parameters" -%} + {%- set ns.col1 = "Type" -%} + {%- set ns.col2 = "Description" -%} +{%- elif part.kind == ParameterList.Exceptions -%} + {%- set ns.title = "Exceptions" -%} + {%- set ns.col1 = "Type" -%} + {%- set ns.col2 = "Thrown On" -%} +{%- elif part.kind == ParameterList.ReturnValues -%} + {%- set ns.title = "Return Values" -%} + {%- set ns.col1 = "Type" -%} + {%- set ns.col2 = "Description" -%} +{%- endif -%} +{% call(param_block) summary_table(part, ns.title, cols=[ns.col1, ns.col2]) -%} +{%- set sep = joiner(", ") -%} +| +{%- for item in param_block -%} +{{ sep() }}`` +{%- if item.type %}{{ phrase(item.type) }} {% endif -%} +{{ phrase(item.name) }}`` +{%- endfor %} +|{{ description(param_block.description) }} +{% endcall -%} +{%- endmacro -%} + + +{%- macro oset_parameter_list(oset, kind) -%} +{%- set ns = namespace(title="", col="") -%} +{%- if kind == ParameterList.Parameters -%} + {%- set ns.title = "Parameters" -%} + {%- set ns.col1 = "Name" -%} + {%- set ns.col2 = "Description" -%} +{%- elif kind == ParameterList.TemplateParameters -%} + {%- set ns.title = "Template Parameters" -%} + {%- set ns.col1 = "Type" -%} + {%- set ns.col2 = "Description" -%} +{%- elif kind == ParameterList.Exceptions -%} + {%- set ns.title = "Exceptions" -%} + {%- set ns.col1 = "Type" -%} + {%- set ns.col2 = "Thrown On" -%} +{%- elif kind == ParameterList.ReturnValues -%} + {%- set ns.title = "Return Values" -%} + {%- set ns.col1 = "Type" -%} + {%- set ns.col2 = "Description" -%} +{%- endif -%} + +{%- set params = [] -%} +{%- for func in oset -%} + {%- for part in func.description -%} + {%- if part is ParameterList and part.kind == kind -%} + {%- for param in part -%} + {%- set name = param | map(attribute="name") + | map(attribute="text") | join -%} + {%- set type = param | map(attribute="type") + | map(attribute="text") | join -%} + {%- set names = params | map("map", attribute="name") + | map("map", attribute="text") | map("join") -%} + {%- set types = params | map("map", attribute="type") + | map("map", attribute="text") | map("join") -%} + {%- if (name not in names) or (type not in types) -%} + {%- set ___ = params.append(param) -%} + {%- endif -%} + {%- endfor -%} + {%- endif -%} + {%- endfor -%} +{%- endfor -%} + +{%- if kind == ParameterList.ReturnValues -%} +{%- for func in oset -%} + {%- for part in func.description -%} + {%- if part is Section and part.kind == 'return' -%} + {{ subsection(part) }} + {%- endif -%} + {%- endfor -%} +{%- endfor -%} +{%- endif -%} + +{% call(param_block) summary_table(params, ns.title, cols=[ns.col1, ns.col2]) -%} +{%- set sep = joiner(", ") -%} +| +{%- for item in param_block -%} +{{ sep() }}`` +{%- if item.type %}{{ phrase(item.type) }} {% endif -%} +{{ phrase(item.name) }}`` +{%- endfor %} +|{{ description(param_block.description) }} +{% endcall -%} +{%- endmacro -%} + + +{%- macro phrase(para, in_code=False) -%} +{%- for part in para -%}{{ phrase_part(part, in_code=in_code) }}{%- endfor -%} +{%- endmacro -%} + + +{%- macro phrase_part(part, in_code=False) -%} + {%- if part is string -%} + {{ text_helper(part, in_code=in_code) }} + {%- elif part is EmDash -%} + -- + {%- elif part is EnDash -%} + {{ part.text }} + {%- elif part is Monospaced -%} + ``{{ phrase(part, in_code=True) }}`` + {%- elif part is Emphasised -%} + __{{ phrase(part, in_code=in_code) }}__ + {%- elif part is Strong -%} + **{{ phrase(part, in_code=in_code) }}** + {%- elif part is EntityRef -%} + {%- set is_external = Config.get('external_marker') + and (part.entity.brief | map(attribute="text") | join | trim) + == Config.get('external_marker') -%} + pass:q,a,m[ + {%- if is_external -%} + link: + {%- for part in part.entity.description -%} + {%- if part is Section and part.kind == Section.See -%} + {{ part | map(attribute="text") | join | trim }} + {%- endif -%} + {%- endfor -%} + {%- else -%} + xref:{{ link(part.entity) }} + {%- endif -%} + [ + {%- if in_code -%} + {{ abridged_fqn(part.entity) }} + {%- else -%} + ``{{ phrase(part, in_code=True) }}`` + {%- endif -%} + \]] + {%- elif part is UrlLink -%} + link:pass:a[{{ escape(part.url) }}][{{ phrase(part, in_code=in_code) }}] + {%- elif part is Linebreak -%}{{ "\n\n" }} + {%- elif part is Phrase -%} + {{ phrase(part, in_code=in_code) }} + {%- else -%} + {{ part.unhandled_type() }} + {%- endif -%} +{%- endmacro -%} + + +{%- macro link(entity) -%} + {%- if entity is Enumerator -%} + {{ link(entity.enum) }} + {%- else -%} + {{ anchor(entity) }} + {%- endif -%} +{%- endmacro -%} + + +{%- macro anchor(entity) -%} + {%- set ns = namespace(path=entity.path) -%} + {%- if entity.fully_qualified_name.startswith(Config.default_namespace) -%} + {%- set ns.path = ns.path[2:] -%} + {%- endif -%} + {{ Config.link_prefix }} + {%- set sep = joiner("_") -%} + {%- for segment in ns.path -%} + {{ sep() }}{{ sanitize_path_segment(segment.name) }} + {%- endfor -%} + {%- if entity.scope and not entity.scope is Namespace -%} + {%- if (entity is Function or entity is OverloadSet) and entity.is_friend -%}_fr{%- endif -%} + {%- if (entity is Function or entity is OverloadSet) and entity.is_free -%}_fe{%- endif -%} + {%- endif -%} +{%- endmacro -%} + + +{%- macro sanitize_path_segment(segment) -%} + {{ segment.replace("[", "_lb") + .replace("]", "_rb") + .replace("(", "_lp") + .replace(")", "_rp") + .replace("<=>", "_spshp") + .replace("operator~", "operator_bnot") + .replace("->", "_arrow") + .replace("=", "_eq") + .replace("!", "_not") + .replace("+", "_plus") + .replace("-", "_minus") + .replace("&", "_and") + .replace("|", "_or") + .replace("^", "_xor") + .replace("*", "_star") + .replace("/", "_slash") + .replace("%", "_mod") + .replace("<", "_lt") + .replace(">", "_gt") + .replace("~", "_dtor") + .replace(",", "_comma") + .replace("::", "_") + .replace(":", "_") + .replace(" ", "_") }} +{%- endmacro -%} + + +{%- macro abridged_fqn(entity) -%} + {%- set prefix = Config.default_namespace + '::' -%} + {%- set s = entity.fully_qualified_name -%} + {%- if s.startswith(prefix) -%} + {{ escape(s[prefix | length:]) }} + {%- else -%} + {{ escape(s) }} + {%- endif -%} +{%- endmacro -%} + + +{%- macro entity_declaration(entity) -%} +{{ template_parameters(entity) }} +{%- if entity is Variable -%} +{%- set sp = joiner(' ') -%} +{%- if entity.is_static %}{{ sp() }}static{% endif -%} +{%- if entity.is_constexpr %}{{ sp() }}constexpr{% endif %} +{%- if sp() %} +{% endif -%} +{{ phrase(entity.type, in_code=True) }} +{%- if entity.args %}(*{% else %} {% endif -%} +{%- endif -%} +{%- if entity is Type %}{{ entity.declarator }} {% endif -%} +{{ entity.name }} +{%- if entity is Variable and entity.args -%} +){{ phrase(entity.args, in_code=True) }} +{%- endif -%} +{%- if entity is Class -%} +{% for entry in entity.bases %} + {% if loop.first %}:{% else %},{% endif -%} + {{ ' ' ~ entry.access ~ ' ' }} + {%- if entry.is_virtual %}virtual {% endif -%} + {{ phrase(entry.base, in_code=True) }} +{%- endfor -%} +{%- endif -%} +{%- if entity is Enum and entity.underlying_type %} + : {{ phrase(entity.underlying_type, in_code=True) }} +{%- endif -%} +{%- if entity is TypeAlias %} = {{ phrase(entity.aliased, in_code=True) }}{% endif -%} +{%- if entity is Variable and entity.value %} {{ phrase(entity.value, in_code=True) }}{% endif -%} +; +{%- endmacro -%} + + +{%- macro overload_set_declaration(oset) -%} +{%- set sep = joiner("\n") -%} +{% for func in oset -%} +{{ sep() }}{{ function_declaration(func) }} +{%- if (oset | length) > 1 %} // <{{ loop.index }}>{% endif %} +{% endfor -%} +{% endmacro -%} + + +{%- macro function_declaration(entity) -%} +{{ template_parameters(entity) }} +{%- set sp = joiner(' ') -%} +{%- if entity.is_explicit %}{{ sp() }}explicit{% endif -%} +{%- if entity.is_static %}{{ sp() }}static{% endif -%} +{%- if entity.is_constexpr %}{{ sp() }}constexpr{% endif %} +{%- set ret = phrase(entity.return_type, in_code=True) -%} +{%- if ret %}{{ sp() }}{{ ret }}{% endif -%} +{%- if sp() %} +{% endif -%} +{{ escape(entity.name) }}( +{%- for param in entity.parameters %} + {{ phrase(param.type, in_code=True) }} + {%- if param.array %} (&{% endif -%} + {%- if param.name -%} + {%- if not param.array %} {% endif -%} + {{ param.name }} + {%- endif -%} + {%- if param.array %}) {{ phrase(param.array, in_code=True) }}{% endif -%} + {%- if param.default_value %} = {{ phrase(param.default_value, in_code=True) }}{% endif -%} + + {%- if not loop.last %},{% endif -%} +{%- endfor -%} +) +{%- if entity.refqual or entity.is_const -%} + {{ " " }} + {%- if entity.is_const %}const{% endif -%} + {%- if "lvalue" == entity.refqual %}& + {%- elif "rvalue" == entity.refqual %}&& + {%- endif -%} +{%- endif -%} +{%- if entity.is_noexcept and not entity.is_destructor %} noexcept +{%- if entity.noexcept_condition -%} + ({{ entity.noexcept_condition }}) +{%- endif -%} +{%- endif -%} +{%- if not entity.is_noexcept and entity.is_destructor %} noexcept(false){% endif -%} +{%- if entity.is_deleted %} = delete +{%- elif entity.is_defaulted and Config.get('show_defaulted') %} = default +{%- endif -%} +; +{%- endmacro %} + + +{%- macro template_parameters(entity) -%} +{%- if entity.template_parameters or entity.is_specialization -%}template<{%- endif -%} +{%- for tparam in entity.template_parameters %} + {{ phrase(tparam.type, in_code=True) }} + {%- if tparam.array %} (&{% endif -%} + {%- if tparam.name -%} + {%- if not tparam.array %} {% endif -%} + {{ phrase_part(tparam.name, in_code=True) }} + {%- endif -%} + {%- if tparam.array %}) {{ phrase(tparam.array, in_code=True) }}{% endif -%} + {%- if tparam.default_value %} = {{ phrase(tparam.default_value, in_code=True) }}{% endif -%} + +{%- if loop.last -%} +> +{% else -%} +, +{%- endif -%} + +{%- else -%} +{%- if entity.is_specialization -%} +> +{% endif -%} +{%- endfor %} +{%- endmacro -%} + + +{%- macro simple_summary_table(sequence, title) -%} +{%- call(member) summary_table(sequence | sort, title) -%} +|<<{{ link(member) }},``{{ escape(member.name) }}``>> +|{{ description(member.brief) | trim }} +{%- endcall -%} +{%- endmacro -%} + + +{%- macro function_summary_table(sequence, title) -%} +{%- call(member) summary_table( + sequence | sort, title, cols=['Name', 'Description']) -%} + |<<{{ link(member) }},``{{ escape(member.name) }}``>> + {%- if member.is_constructor %}{nbsp}[.silver]##[constructor]##{% endif -%} + {%- if member.is_destructor %}{nbsp}[.silver]##[destructor]##{% endif -%} + |{{ description(member[0].brief) | trim }} +{% endcall -%} +{%- endmacro -%} + + + +{%- macro summary_table(sequence, title, cols=['Name', 'Description']) -%} +{%- for row in sequence -%} +{% if loop.first -%} +{{ heading(title) }} +|=== +{% for col in cols %}|{{col}}{% endfor %} + +{% endif -%} + {{ caller(row) }} +{% if loop.last %} +|=== +{% endif %} +{%- endfor -%} +{%- endmacro -%} + + +{% macro heading(title) -%} +[discrete] +=== {{ escape(title) }} +{%- endmacro -%} + + +{%- macro source_header(path) -%} +https://github.com/boostorg/json/blob/master/include/{{path}}[<{{path}}>] +{%- endmacro -%} + + +{%- macro text_helper(s, in_code=False) -%} + {%- if s -%} + {%- set replacements=Config.get('replace_strings', {}) if in_code else {} -%} + + {%- set ns = namespace(s=s) -%} + {%- if in_code -%} + {%- set ns.s = re.sub("\\s+", " ", ns.s, re.U) -%} + {%- if ns.s.endswith(" &") -%} + {%- set ns.s = ns.s[:-2] + "&" -%} + {%- elif ns.s.endswith(" *") -%} + {%- set ns.s = ns.s[:-2] + "*" -%} + {%- elif ns.s.endswith(" &&") -%} + {%- set ns.s = ns.s[:-3] + "&&" -%} + {%- elif ns.s.endswith(" &...") -%} + {%- set ns.s = ns.s[:-5] + "&..." -%} + {%- elif ns.s.endswith(" *...") -%} + {%- set ns.s = ns.s[:-5] + "*..." -%} + {%- elif ns.s.endswith(" &&...") -%} + {%- set ns.s = ns.s[:-6] + "&&..." -%} + {%- endif -%} + {%- else -%} + {%- set ns.s = s -%} + {%- endif -%} + {%- for src, tgt in replacements.items() -%} + {%- set ns.s = re.sub(src, tgt, ns.s, flags=re.U) -%} + {%- endfor -%} + {%- if in_code -%} + {{ code_escape(ns.s) }} + {%- else -%} + {{ escape(ns.s) }} + {%- endif -%} + {%- else -%} + {{ s }} + {%- endif -%} +{%- endmacro -%} + + +{%- macro escape(s) -%} +{{ s.replace("++", "{pp}") }} +{%- endmacro -%} + + +{%- macro code_escape(s) -%} +{{ s.replace("<=", "++<=++") + .replace("->", "++->++") + .replace("...", "++...++") }} +{%- endmacro -%} + + +{% for entity in entities.values() -%} + {% if entity is not Namespace -%} + {% continue %} + {%- endif -%} + {% if Config.get('default_namespace') + and entity.fully_qualified_name != Config.default_namespace -%} + {% continue %} + {%- endif -%} + + {{ write_namespace(entity) }} +{%- endfor %} diff --git a/example/cbor.cpp b/example/cbor.cpp index 815290d4c..69915da36 100644 --- a/example/cbor.cpp +++ b/example/cbor.cpp @@ -7,7 +7,7 @@ // Official repository: https://github.com/boostorg/json // -//[example_cbor +// tag::example_cbor[] /* This example implements simple parsing and serialization of a @@ -470,4 +470,4 @@ main(int argc, const char** argv) return EXIT_SUCCESS; } -//] +// end::example_cbor[] diff --git a/example/pretty.cpp b/example/pretty.cpp index 1de936d52..45027edd2 100644 --- a/example/pretty.cpp +++ b/example/pretty.cpp @@ -7,7 +7,7 @@ // Official repository: https://github.com/boostorg/json // -//[example_pretty +// tag::example_pretty[] /* This example parses a JSON file and pretty-prints @@ -155,4 +155,4 @@ main(int argc, char** argv) return EXIT_SUCCESS; } -//] +// end::example_pretty[] diff --git a/example/use_allocator.cpp b/example/use_allocator.cpp index 5cb2826ae..c3bc8f9a1 100644 --- a/example/use_allocator.cpp +++ b/example/use_allocator.cpp @@ -7,7 +7,7 @@ // Official repository: https://github.com/boostorg/json // -//[example_use_allocator +// tag::example_use_allocator[] /* This example uses a context that stores an allocator to create sequences during conversions @@ -86,4 +86,4 @@ main(int, char**) return EXIT_SUCCESS; } -//] +// end::example_use_allocator[] diff --git a/example/validate.cpp b/example/validate.cpp index 420fe1738..e368ce38a 100644 --- a/example/validate.cpp +++ b/example/validate.cpp @@ -7,7 +7,7 @@ // Official repository: https://github.com/boostorg/json // -//[example_validate +// tag::example_validate[] /* This example verifies that a file contains valid JSON. @@ -131,4 +131,4 @@ main(int argc, char** argv) return EXIT_SUCCESS; } -//] +// end::example_validate[] diff --git a/include/boost/json/array.hpp b/include/boost/json/array.hpp index ad3a48db8..f59e21ee3 100644 --- a/include/boost/json/array.hpp +++ b/include/boost/json/array.hpp @@ -30,56 +30,50 @@ class value_ref; /** A dynamically sized array of JSON values - This is the type used to represent a JSON array as - a modifiable container. The interface and performance - characteristics are modeled after `std::vector`. -\n - Elements are stored contiguously, which means that - they can be accessed not only through iterators, but - also using offsets to regular pointers to elements. A - pointer to an element of an @ref array may be passed to - any function that expects a pointer to @ref value. -\n - The storage of the array is handled automatically, being - expanded and contracted as needed. Arrays usually occupy - more space than array language constructs, because more - memory is allocated to handle future growth. This way an - array does not need to reallocate each time an element - is inserted, but only when the additional memory is used - up. The total amount of allocated memory can be queried - using the @ref capacity function. Extra memory can be - relinquished by calling @ref shrink_to_fit. - \n - - Reallocations are usually costly operations in terms of - performance. The @ref reserve function can be used to - eliminate reallocations if the number of elements is - known beforehand. -\n - The complexity (efficiency) of common operations on - arrays is as follows: - - @li Random access - constant *O(1)*. - @li Insertion or removal of elements at the - end - amortized constant *O(1)*. - @li Insertion or removal of elements - linear in - the distance to the end of the array *O(n)*. + This is the type used to represent a JSON array as a modifiable container. + The interface and performance characteristics are modeled after + `std::vector`. + + Elements are stored contiguously, which means that they can be accessed not + only through iterators, but also using offsets to regular pointers to + elements. A pointer to an element of an `array` may be passed to any + function that expects a pointer to @ref value. + + The storage of the array is handled automatically, being expanded and + contracted as needed. Arrays usually occupy more space than array language + constructs, because more memory is allocated to handle future growth. This + way an array does not need to reallocate each time an element is inserted, + but only when the additional memory is used up. The total amount of + allocated memory can be queried using the @ref capacity function. Extra + memory can be relinquished by calling @ref shrink_to_fit. + + + Reallocations are usually costly operations in terms of performance. The + @ref reserve function can be used to eliminate reallocations if the number + of elements is known beforehand. + + The complexity (efficiency) of common operations on arrays is as follows: + + @li Random access---constant *O(1)*. + @li Insertion or removal of elements at the end - amortized + constant *O(1)*. + @li Insertion or removal of elements---linear in the distance to the end of + the array *O(n)*. @par Allocators - All elements stored in the container, and their - children if any, will use the same memory resource - that was used to construct the container. + All elements stored in the container, and their children if any, will use + the same memory resource that was used to construct the container. @par Thread Safety - Non-const member functions may not be called - concurrently with any other member functions. + Non-const member functions may not be called concurrently with any other + member functions. @par Satisfies - ContiguousContainer, - ReversibleContainer, and - SequenceContainer. + [*ContiguousContainer*](https://en.cppreference.com/w/cpp/named_req/ContiguousContainer), + [*ReversibleContainer*](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer), and + [*SequenceContainer*](https://en.cppreference.com/w/cpp/named_req/SequenceContainer). */ class array { @@ -160,12 +154,12 @@ class array /** Destructor. - The destructor for each element is called if needed, - any used memory is deallocated, and shared ownership - of the `boost::container::pmr::memory_resource` is released. + The destructor for each element is called if needed, any used memory is + deallocated, and shared ownership of the + @ref boost::container::pmr::memory_resource is released. @par Complexity - Constant, or linear in @ref size(). + Linear in @ref size(). @par Exception Safety No-throw guarantee. @@ -175,38 +169,84 @@ class array //------------------------------------------------------ - /** Constructor. + /** Constructors. + + Constructs an array. + + @li **(1)**, **(2)** the array is empty and has zero capacity. + + @li **(3)** the array is filled with `count` copies of `jv`. + + @li **(4)** the array is filled with `count` null values. + + @li **(5)** the array is filled with values in the range + `[first, last)`, preserving order. + + @li **(6)** the array is filled with copies of the values in `init, + preserving order. - The constructed array is empty with zero - capacity, using the [default memory resource]. + @li **(7)**, **(8)** the array is filled with copies of the elements of + `other`, preserving order. + + @li **(9)** the array acquires ownership of the contents of `other` and + shared ownership of `other`'s memory resource. + + @li **(10)** equivalent to **(9)** if `*sp == *other.storage()`; + otherwise equivalent to **(8)**. + + @li **(11)** the array acquires ownership of the contents of `other` + using pilfer semantics. This is more efficient than move + construction, when it is known that the moved-from object will be + immediately destroyed afterwards. + + With **(2)**, **(4)**, **(5)**, **(6)**, **(8)**, **(10)** the + constructed array uses memory resource of `sp`. With **(7)**, **(9)**, + and **(11)** it uses `other`'s memory resource. With **(1)** and **(3)** + it uses the \<\\>. + + After **(9)** `other` behaves as if newly constructed with its + current storage pointer. + + After **(11)** `other` is not in a usable state and may only be + destroyed. + + @par Constraints + + @code + std::is_constructible_v::reference> + @endcode @par Complexity - Constant. + @li **(1)**, **(2)**, **(9)**, **(11)** constant. + @li **(3)**, **(4)** linear in `count`. + @li **(5)** linear in `std::distance(first, last)` + @li **(6)** linear in `init.size()`. + @li **(7)**, **(8)** linear in `other.size()`. + @li **(10)** constant if `*sp == *other.storage()`; otherwise linear in + `other.size()`. @par Exception Safety - No-throw guarantee. + @li **(1)**, **(2)**, **(9)**, **(11)** no-throw guarantee. + @li **(3)**, **(4)**, **(6)**, **(7)**, **(8)**, **(10)** strong + guarantee. + @li **(5)** strong guarantee if `InputIt` satisfies + {req_ForwardIterator}, basic guarantee otherwise. + + Calls to `memory_resource::allocate` may throw. - [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource + @see @ref pilfer, + [Valueless Variants Considered Harmful](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0308r0.html). + @{ */ array() noexcept : t_(&empty_) { } - /** Constructor. - - The constructed array is empty with zero - capacity, using the specified memory resource. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - - @param sp A pointer to the `boost::container::pmr::memory_resource` - to use. The container will acquire shared - ownership of the memory resource. + /** Overload + @param sp A pointer to the @ref boost::container::pmr::memory_resource + to use. The container will acquire shared ownership of the memory + resource. */ explicit array(storage_ptr sp) noexcept @@ -216,88 +256,31 @@ class array { } - /** Constructor. - - The array is constructed with `count` - copies of the value `v`, using the - specified memory resource. - - @par Complexity - Linear in `count` - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - + /** Overload @param count The number of copies to insert. - - @param v The value to be inserted. - - @param sp A pointer to the `boost::container::pmr::memory_resource` - to use. The container will acquire shared - ownership of the memory resource. + @param jv The value to be inserted. + @param sp */ BOOST_JSON_DECL array( std::size_t count, - value const& v, + value const& jv, storage_ptr sp = {}); - /** Constructor. - - The array is constructed with `count` null values, - using the specified memory resource. - - @par Complexity - Linear in `count` - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param count The number of nulls to insert. - - @param sp A pointer to the `boost::container::pmr::memory_resource` - to use. The container will acquire shared - ownership of the memory resource. - */ + /// Overload BOOST_JSON_DECL array( std::size_t count, storage_ptr sp = {}); - /** Constructor. - - The array is constructed with the elements - in the range `{first, last)`, preserving order, - using the specified memory resource. + /** Overload - @par Constraints + @param first An input iterator pointing to the first element to insert, + or pointing to the end of the range. + @param last An input iterator pointing to the end of the range. + @param sp - @code - std::is_constructible_v::reference> - @endcode - - @par Complexity - Linear in `std::distance(first, last)` - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param first An input iterator pointing to the - first element to insert, or pointing to the end - of the range. - - @param last An input iterator pointing to the end - of the range. - - @param sp A pointer to the `boost::container::pmr::memory_resource` - to use. The container will acquire shared - ownership of the memory resource. - - @tparam InputIt a type satisfying the requirements - of __InputIterator__. + @tparam InputIt a type satisfying the {req_InputIterator} requirement. */ template< class InputIt @@ -312,68 +295,28 @@ class array InputIt first, InputIt last, storage_ptr sp = {}); - /** Copy constructor. - - The array is constructed with a copy of the - contents of `other`, using `other`'s memory resource. - - @par Complexity - Linear in `other.size()`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. + /** Overload + @param init The initializer list with elements to insert. + @param sp + */ + BOOST_JSON_DECL + array( + std::initializer_list init, + storage_ptr sp = {}); - @param other The array to copy + /** Overload + @param other Another array. */ BOOST_JSON_DECL array(array const& other); - /** Copy constructor. - - The array is constructed with a copy of the - contents of `other`, using the specified memory resource. - - @par Complexity - Linear in `other.size()`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param other The array to copy - - @param sp A pointer to the `boost::container::pmr::memory_resource` - to use. The container will acquire shared - ownership of the memory resource. - */ + /// Overload BOOST_JSON_DECL array( array const& other, storage_ptr sp); - /** Pilfer constructor. - - The array is constructed by acquiring ownership - of the contents of `other` using pilfer semantics. - This is more efficient than move construction, when - it is known that the moved-from object will be - immediately destroyed afterwards. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - - @param other The value to pilfer. After pilfer - construction, `other` is not in a usable state - and may only be destroyed. - - @see @ref pilfer, - - Valueless Variants Considered Harmful - */ + /// Overload array(pilfered other) noexcept : sp_(std::move(other.get().sp_)) , t_(detail::exchange( @@ -381,26 +324,7 @@ class array { } - /** Move constructor. - - The array is constructed by acquiring ownership of - the contents of `other` and shared ownership of - `other`'s memory resource. - - @note - - After construction, the moved-from array behaves - as if newly constructed with its current storage - pointer. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - - @param other The container to move - */ + /// Overload array(array&& other) noexcept : sp_(other.sp_) , t_(detail::exchange( @@ -408,97 +332,48 @@ class array { } - /** Move constructor. - - The array is constructed with the contents of - `other` by move semantics, using the specified - memory resource: - - @li If `*other.storage() == *sp`, ownership of - the underlying memory is transferred in constant - time, with no possibility of exceptions. - After construction, the moved-from array behaves - as if newly constructed with its current storage - pointer. - - @li If `*other.storage() != *sp`, an - element-wise copy is performed, which may throw. - In this case, the moved-from array is not - changed. - - @par Complexity - At most, linear in `other.size()`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param other The container to move - - @param sp A pointer to the `boost::container::pmr::memory_resource` - to use. The container will acquire shared - ownership of the memory resource. - */ + /// Overload BOOST_JSON_DECL array( array&& other, storage_ptr sp); + /// @} - /** Constructor. + /** Assignment operators. - The array is constructed with a copy of the values - in the initializer-list in order, using the - specified memory resource. + Replaces the contents of the array. + @li **(1)** the contents are replaced with an element-wise copy of + `other`. + @li **(2)** takes ownership of `other`'s element storage if + `*storage() == *other.storage()`; otherwise equivalent to **(1)**. + @li **(3)** the contents are replaced with a copy of the values in + `init`. - @par Complexity - Linear in `init.size()`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param init The initializer list to insert - - @param sp A pointer to the `boost::container::pmr::memory_resource` - to use. The container will acquire shared - ownership of the memory resource. - */ - BOOST_JSON_DECL - array( - std::initializer_list init, - storage_ptr sp = {}); - - //------------------------------------------------------ - - /** Copy assignment. - - The contents of the array are replaced with an - element-wise copy of `other`. + After **(2)**, the moved-from array behaves as if newly constructed + with its current storage pointer. @par Complexity - Linear in @ref size() plus `other.size()`. + @li **(1)** linear in `size() + other.size()`. + @li **(2)** constant if `*storage() == *other.storage()`; otherwise + linear in `size() + other.size()`. @par Exception Safety - Strong guarantee. + {sp} **(2)** provides strong guarantee if + `*storage() != *other.storage()` and no-throw guarantee otherwise. + Other overloads provide strong guarantee. Calls to `memory_resource::allocate` may throw. @param other The array to copy. + + @{ */ BOOST_JSON_DECL array& operator=(array const& other); - /** Move assignment. + /** Overload - The contents of the array are replaced with the - contents of `other` using move semantics: - @li If `*other.storage() == *sp`, ownership of - the underlying memory is transferred in constant - time, with no possibility of exceptions. - After assignment, the moved-from array behaves - as if newly constructed with its current storage - pointer. @li If `*other.storage() != *sp`, an element-wise copy is performed, which may throw. @@ -519,10 +394,7 @@ class array array& operator=(array&& other); - /** Assignment. - - The contents of the array are replaced with a - copy of the values in the initializer-list. + /** Overload @par Complexity Linear in `this->size()` plus `init.size()`. @@ -537,13 +409,12 @@ class array array& operator=( std::initializer_list init); - - //------------------------------------------------------ + /// @} /** Return the associated memory resource. - This function returns the `boost::container::pmr::memory_resource` used - by the container. + This function returns the @ref boost::container::pmr::memory_resource + used by the container. @par Complexity Constant. @@ -560,7 +431,7 @@ class array /** Return the associated allocator. This function returns an instance of @ref allocator_type constructed - from the associated `boost::container::pmr::memory_resource`. + from the associated @ref boost::container::pmr::memory_resource. @par Complexity Constant. @@ -582,9 +453,9 @@ class array /** Access an element, with bounds checking. - Returns `boost::system::result` containing a reference to the element - specified at location `pos`, if `pos` is within the range of the - container. Otherwise the result contains an `error_code`. + Returns @ref boost::system::result containing a reference to the + element specified at location `pos`, if `pos` is within the range of + the container. Otherwise the result contains an `error_code`. @par Exception Safety No-throw guarantee. @@ -593,8 +464,9 @@ class array @par Complexity Constant. + + @{ */ - /** @{ */ BOOST_JSON_DECL system::result try_at(std::size_t pos) noexcept; @@ -602,14 +474,13 @@ class array BOOST_JSON_DECL system::result try_at(std::size_t pos) const noexcept; - /** @} */ + /// @} /** Access an element, with bounds checking. - Returns a reference to the element specified at - location `pos`, with bounds checking. If `pos` is - not within the range of the container, an exception - of type `boost::system::system_error` is thrown. + Returns a reference to the element specified at location `pos`, with + bounds checking. If `pos` is not within the range of the container, an + exception of type @ref boost::system::system_error is thrown. @par Complexity Constant. @@ -620,8 +491,9 @@ class array location of the call site by default. @throw `boost::system::system_error` `pos >= size()`. + + @{ */ - /** @{ */ inline value& at( @@ -639,22 +511,22 @@ class array at( std::size_t pos, source_location const& loc = BOOST_CURRENT_LOCATION) const&; - /** @} */ + /// @} /** Access an element. Returns a reference to the element specified at location `pos`. No bounds checking is performed. - @par Precondition - `pos < size()` + @pre `pos < size()` @par Complexity Constant. @param pos A zero-based index + + @{ */ - /** @{ */ inline value& operator[](std::size_t pos) & noexcept; @@ -666,19 +538,19 @@ class array inline value const& operator[](std::size_t pos) const& noexcept; - /** @} */ + /// @} /** Access the first element. Returns a reference to the first element. - @par Precondition - `not empty()` + @pre `! empty()` @par Complexity Constant. + + @{ */ - /** @{ */ inline value& front() & noexcept; @@ -690,19 +562,19 @@ class array inline value const& front() const& noexcept; - /** @} */ + /// @} /** Access the last element. Returns a reference to the last element. - @par Precondition - `not empty()` + @pre `!empty()` @par Complexity Constant. + + @{ */ - /** @{ */ inline value& back() & noexcept; @@ -714,36 +586,17 @@ class array inline value const& back() const& noexcept; - /** @} */ + /// @} /** Access the underlying array directly. - Returns a pointer to the underlying array serving - as element storage. The value returned is such that - the range `{data(), data() + size())` is always a - valid range, even if the container is empty. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. + Returns a pointer to the underlying array serving as element storage. + The value returned is such that the range `[data(), data() + size())` + is always a valid range, even if the container is empty. @note - If `size() == 0`, the function may or may not return a null pointer. - */ - inline - value* - data() noexcept; - - /** Access the underlying array directly. - - Returns a pointer to the underlying array serving - as element storage. The value returned is such that - the range `{data(), data() + size())` is always a - valid range, even if the container is empty. @par Complexity Constant. @@ -751,20 +604,22 @@ class array @par Exception Safety No-throw guarantee. - @note - - If `size() == 0`, the function may or may not return - a null pointer. + @{ */ + inline + value* + data() noexcept; + inline value const* data() const noexcept; + /// @} - /** Return a pointer to an element, or nullptr if the index is invalid + /** Return a pointer to an element if it exists. - This function returns a pointer to the element - at index `pos` when the index is less then the size - of the container. Otherwise it returns null. + This function returns a pointer to the element at index `pos` when the + index is less then the size of the container. Otherwise it returns + null. @par Example @code @@ -779,34 +634,17 @@ class array No-throw guarantee. @param pos The index of the element to return. + + @{ */ inline value const* if_contains(std::size_t pos) const noexcept; - /** Return a pointer to an element, or nullptr if the index is invalid - - This function returns a pointer to the element - at index `pos` when the index is less then the size - of the container. Otherwise it returns null. - - @par Example - @code - if( auto p = arr.if_contains( 1 ) ) - std::cout << *p; - @endcode - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - - @param pos The index of the element to return. - */ inline value* if_contains(std::size_t pos) noexcept; + /// @} //------------------------------------------------------ // @@ -823,24 +661,17 @@ class array @par Exception Safety No-throw guarantee. + + @{ */ inline iterator begin() noexcept; - /** Return a const iterator to the first element. - - If the container is empty, @ref end() is returned. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ inline const_iterator begin() const noexcept; + /// @} /** Return a const iterator to the first element. @@ -856,40 +687,32 @@ class array const_iterator cbegin() const noexcept; - /** Return an iterator to the element following the last element. + /** Return a const iterator past the last element. - The element acts as a placeholder; attempting - to access it results in undefined behavior. + The returned iterator only acts as a sentinel. Dereferencing it results + in undefined behavior. @par Complexity Constant. @par Exception Safety No-throw guarantee. + + @{ */ inline iterator end() noexcept; - /** Return a const iterator to the element following the last element. - - The element acts as a placeholder; attempting - to access it results in undefined behavior. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ inline const_iterator end() const noexcept; + /// @} - /** Return a const iterator to the element following the last element. + /** Return a const iterator past the last element. - The element acts as a placeholder; attempting - to access it results in undefined behavior. + The returned iterator only acts as a sentinel. Dereferencing it results + in undefined behavior. @par Complexity Constant. @@ -912,26 +735,17 @@ class array @par Exception Safety No-throw guarantee. + + @{ */ inline reverse_iterator rbegin() noexcept; - /** Return a const reverse iterator to the first element of the reversed container. - - The pointed-to element corresponds to the - last element of the non-reversed container. - If the container is empty, @ref rend() is returned. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ inline const_reverse_iterator rbegin() const noexcept; + /// @} /** Return a const reverse iterator to the first element of the reversed container. @@ -953,42 +767,31 @@ class array The pointed-to element corresponds to the element preceding the first element of the non-reversed container. - The element acts as a placeholder; attempting - to access it results in undefined behavior. + The returned iterator only acts as a sentinel. Dereferencing it results + in undefined behavior. @par Complexity Constant. @par Exception Safety No-throw guarantee. + + @{ */ inline reverse_iterator rend() noexcept; - /** Return a const reverse iterator to the element following the last element of the reversed container. - - The pointed-to element corresponds to the element - preceding the first element of the non-reversed container. - The element acts as a placeholder; attempting - to access it results in undefined behavior. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ inline const_reverse_iterator rend() const noexcept; + /// @} /** Return a const reverse iterator to the element following the last element of the reversed container. - The pointed-to element corresponds to the element - preceding the first element of the non-reversed container. - The element acts as a placeholder; attempting - to access it results in undefined behavior. + The pointed-to element corresponds to the element preceding the first + element of the non-reversed container. The returned iterator only acts + as a sentinel. Dereferencing it results in undefined behavior. @par Complexity Constant. @@ -1043,8 +846,9 @@ class array /** Return the number of elements that can be held in currently allocated memory. - This number may be larger than the value returned - by @ref size(). + Returns the number of elements that the container has currently + allocated space for. This number is never smaller than the value + returned by @ref size(). @par Complexity Constant. @@ -1073,18 +877,14 @@ class array /** Increase the capacity to at least a certain amount. - This increases the @ref capacity() to a value - that is greater than or equal to `new_capacity`. - If `new_capacity > capacity()`, new memory is - allocated. Otherwise, the call has no effect. - The number of elements and therefore the - @ref size() of the container is not changed. - \n - If new memory is allocated, all iterators - including any past-the-end iterators, and all - references to the elements are invalidated. - Otherwise, no iterators or references are - invalidated. + This increases the @ref capacity() to a value that is greater than or + equal to `new_capacity`. If `new_capacity > capacity()`, new memory is + allocated. Otherwise, the call has no effect. The number of elements + and therefore the @ref size() of the container is not changed. + + If new memory is allocated, all iterators including any past-the-end + iterators, and all references to the elements are invalidated. + Otherwise, no iterators or references are invalidated. @par Complexity At most, linear in @ref size(). @@ -1095,7 +895,7 @@ class array @param new_capacity The new capacity of the array. - @throw `boost::system::system_error` `new_capacity > max_size()`. + @throw boost::system::system_error `new_capacity > max_size()`. */ inline void @@ -1129,11 +929,10 @@ class array /** Clear the contents. - Erases all elements from the container. After this - call, @ref size() returns zero but @ref capacity() - is unchanged. All references, pointers, or iterators - referring to contained elements are invalidated. Any - past-the-end iterators are also invalidated. + Erases all elements from the container. After this call, @ref size() + returns zero but @ref capacity() is unchanged. All references, + pointers, or iterators referring to contained elements are invalidated. + Any past-the-end iterators are also invalidated. @par Complexity Linear in @ref size(). @@ -1147,149 +946,96 @@ class array /** Insert elements before the specified location. - This inserts a copy of `v` before `pos`. - If `capacity() < size() + 1`, a reallocation - occurs first, and all iterators and references - are invalidated. - Otherwise, only the iterators and references from - the insertion point forward are invalidated. All + @li **(1)** and **(2)** insert a single new element before + `pos`. **(1)** copy-constructs and **(2)** move-constructs the new + element from `jv`. + @li **(3)** inserts `count` copies of `jv` before `pos`. + @li **(4)** the elements in the range `[first, last)` are inserted in + order. + @li **(5)** the elements of the initializer list `init` are inserted in + order. + + Inserted values will be constructed using the container's + associated @ref boost::container::pmr::memory_resource. + + @note Overload **(2)** is equivalent to **(1)** if + `*jv.storage() != *this->storage()`. + + If the size of the array after insertion would have exceeded + @ref capacity(), a reallocation occurs first, and all iterators and + references are invalidated. Otherwise, only the iterators and + references from the insertion point forward are invalidated. All past-the-end iterators are also invalidated. + @pre + `first` and `last` are not iterators into `*this`. + + @par Constraints + @code + ! std::is_convertible_v + std::is_constructible_v::reference> + @endcode + @par Complexity - Constant plus linear in `std::distance(pos, end())`. + @li **(1)**, **(2)** linear in `std::distance(pos, end())`. + @li **(3)** linear in `count + std::distance(pos, end())`. + @li **(4)** linear in `std::distance(first, last) + + std::distance(pos, end())`. + @li **(5)** linear in `init.size() + std::distance(pos, end())`. @par Exception Safety - Strong guarantee. + {sp}**(4)** provides strong guarantee if `InputIt` satisfies + {req_ForwardIterator}, and basic guarantee otherwise. Other overloads + provide strong guarantee. Calls to `memory_resource::allocate` may throw. - @param pos Iterator before which the content will + @param pos Iterator before which the new element will be inserted. This may be the @ref end() iterator. - @param v The value to insert. A copy will be made - using container's associated `boost::container::pmr::memory_resource`. + @param jv The value to insert. A copy will be made + using container's associated + @ref boost::container::pmr::memory_resource. - @return An iterator to the inserted value + @return An iterator to the first inserted value, or `pos` if no values + were inserted. + + @{ */ BOOST_JSON_DECL iterator insert( const_iterator pos, - value const& v); - - /** Insert elements before the specified location. + value const& jv); - This inserts `v` before `pos` via move-construction. - If `capacity() < size() + 1`, a reallocation occurs - first, and all iterators and references are - invalidated. - Otherwise, only the iterators and references from - the insertion point forward are invalidated. All - past-the-end iterators are also invalidated. - - @par Complexity - Constant plus linear in `std::distance(pos, end())`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param pos Iterator before which the content will - be inserted. This may be the @ref end() iterator. - - @param v The value to insert. Ownership of the - value will be transferred via move construction, - using the container's - associated `boost::container::pmr::memory_resource`. - - @return An iterator to the inserted value - */ + // Overload BOOST_JSON_DECL iterator insert( const_iterator pos, - value&& v); + value&& jv); - /** Insert elements before the specified location. - - This inserts `count` copies of `v` before `pos`. - If `capacity() < size() + count`, a reallocation - occurs first, and all iterators and references are - invalidated. - Otherwise, only the iterators and references from - the insertion point forward are invalidated. All - past-the-end iterators are also invalidated. - - @par Complexity - Linear in `count + std::distance(pos, end())`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param pos Iterator before which the content will - be inserted. This may be the @ref end() iterator. + /** Overload @param count The number of copies to insert. - - @param v The value to insert. Copies will be made - using the container's - associated `boost::container::pmr::memory_resource`. - - @return An iterator to the first inserted value, - or `pos` if `count == 0`. + @param pos + @param jv */ BOOST_JSON_DECL iterator insert( const_iterator pos, std::size_t count, - value const& v); + value const& jv); - /** Insert elements before the specified location. + /** Overload - The elements in the range `{first, last)` are - inserted in order. - If `capacity() < size() + std::distance(first, last)`, - a reallocation occurs first, and all iterators and - references are invalidated. - Otherwise, only the iterators and references from - the insertion point forward are invalidated. All - past-the-end iterators are also invalidated. - - @par Precondition - `first` and `last` are not iterators into `*this`. - - @par Constraints - @code - not std::is_convertible_v - @endcode - - @par Mandates - @code - std::is_constructible_v::reference> - @endcode - - @par Complexity - Linear in `std::distance(first, last) + std::distance(pos, end())`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @return An iterator to the first inserted value, or - `pos` if `first == last`. - - @param pos Iterator before which the content will - be inserted. This may be the @ref end() iterator. - - @param first An input iterator pointing to the first - element to insert, or pointing to the end of the range. - - @param last An input iterator pointing to the end - of the range. + @param first An input iterator pointing to the first element to insert, + or pointing to the end of the range. + @param last An input iterator pointing to the end of the range. + @param pos @tparam InputIt a type satisfying the requirements - of __InputIterator__. + of {req_InputIterator}. */ template< class InputIt @@ -1305,37 +1051,16 @@ class array const_iterator pos, InputIt first, InputIt last); - /** Insert elements before the specified location. - - The elements in the initializer list `init` are - inserted in order. - If `capacity() < size() + init.size()`, - a reallocation occurs first, and all iterators and - references are invalidated. - Otherwise, only the iterators and references from - the insertion point forward are invalidated. All - past-the-end iterators are also invalidated. - - @par Complexity - Linear in `init.size() + std::distance(pos, end())`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param pos Iterator before which the content will - be inserted. This may be the @ref end() iterator. - + /** Overload @param init The initializer list to insert - - @return An iterator to the first inserted value, or - `pos` if `init.size() == 0`. + @param pos */ BOOST_JSON_DECL iterator insert( const_iterator pos, std::initializer_list init); + /// @} /** Insert a constructed element in-place. @@ -1350,7 +1075,7 @@ class array past-the-end iterators are also invalidated. @par Complexity - Constant plus linear in `std::distance(pos, end())`. + Linear in `std::distance(pos, end())`. @par Exception Safety Strong guarantee. @@ -1370,61 +1095,54 @@ class array const_iterator pos, Arg&& arg); - /** Erase elements from the container. + /** Remove elements from the array. - The element at `pos` is removed. + @li **(1)** the element at `pos` is removed. + @li **(2)** the elements in the range `[first, last)` are removed. @par Complexity - Constant plus linear in `std::distance(pos, end())` + @li **(1)** linear in `std::distance(pos, end())`. + @li **(2)** linear in `std::distance(first, end())`. @par Exception Safety No-throw guarantee. @param pos Iterator to the element to remove - @return Iterator following the last removed element. - If the iterator `pos` refers to the last element, - the @ref end() iterator is returned. + @return Iterator following the last removed element. If that was the + last element of the array, the @ref end() iterator is returned. + + @{ */ BOOST_JSON_DECL iterator erase(const_iterator pos) noexcept; - /** Erase elements from the container. - - The elements in the range `{first, last)` are removed. - - @par Complexity - Linear in `std::distance(first, end())` - - @par Exception Safety - No-throw guarantee. - - @param first An iterator pointing to the first - element to erase, or pointing to the end of the range. - - @param last An iterator pointing to one past the - last element to erase, or pointing to the end of the - range. - - @return Iterator following the last removed element. - If the iterator `last` refers to the last element, - the @ref end() iterator is returned. + /** Overload + @param first An iterator pointing to the first element to erase, or + pointing to the end of the range. + @param last An iterator pointing to one past the last element to erase, + or pointing to the end of the range. */ BOOST_JSON_DECL iterator erase( const_iterator first, const_iterator last) noexcept; + /// @} /** Add an element to the end. - This appends a copy of `v` to the container's - elements. - If `capacity() < size() + 1`, a reallocation - occurs first, and all iterators and references - are invalidated. Any past-the-end iterators are - always invalidated. + Insert a new element at the end of the container. **(1)** + copy-constructs the new element from `jv`, **(2)** move-constructs from + `jv`. + + If `capacity() < size() + 1`, a reallocation occurs first, and all + iterators and references are invalidated. Any past-the-end iterators + are always invalidated. + + The new element will be constructed using the container's associated + @ref boost::container::pmr::memory_resource. @par Complexity Amortized constant. @@ -1433,36 +1151,18 @@ class array Strong guarantee. Calls to `memory_resource::allocate` may throw. - @param v The value to insert. A copy will be made using the container's - associated `boost::container::pmr::memory_resource`. + @param jv The value to insert. + + @{ */ BOOST_JSON_DECL void - push_back(value const& v); + push_back(value const& jv); - /** Add an element to the end. - - This appends `v` to the container's elements via - move-construction. - If `capacity() < size() + 1`, a reallocation - occurs first, and all iterators and references - are invalidated. Any past-the-end iterators are - always invalidated. - - @par Complexity - Amortized constant. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param v The value to insert. Ownership of the value will be - transferred via move construction, using the container's - associated `boost::container::pmr::memory_resource`. - */ BOOST_JSON_DECL void - push_back(value&& v); + push_back(value&& jv); + /// @} /** Append a constructed element in-place. @@ -1497,8 +1197,8 @@ class array The last element of the container is erased. - @par Precondition - `not empty()` + @pre + `! empty()` @par Exception Safety No-throw guarantee. @@ -1510,88 +1210,65 @@ class array /** Change the number of elements stored. Resizes the container to contain `count` elements. - If `capacity() < size() + count`, a reallocation - occurs first, and all iterators and references - are invalidated. Any past-the-end iterators are - always invalidated. - @li If `size() > count`, the container is reduced - to its first `count` elements. + @li If `size() > count`, the container is reduced to its first `count` + elements. + @li If `size() < count`, additional null values (**(1)**) or copies + of `jv` (**(2)**) are appended. - @li If `size() < count`, additional null values - are appended. + If `capacity() < count`, a reallocation occurs first, and all iterators + and references are invalidated. Any past-the-end iterators are always + invalidated. @par Complexity - Linear in `abs(size() - count)`, plus the cost of - reallocation if @ref capacity() is less than `count`. + Linear in `size() + count`. @par Exception Safety Strong guarantee. Calls to `memory_resource::allocate` may throw. @param count The new size of the container. + + @{ */ BOOST_JSON_DECL void resize(std::size_t count); - /** Change the number of elements stored. - - Resizes the container to contain `count` elements. - If `capacity() < size() + count`, a reallocation - occurs first, and all iterators and references - are invalidated. Any past-the-end iterators are - always invalidated. - - @li If `size() > count`, the container is reduced - to its first `count` elements. - - @li If `size() < count`, additional copies of `v` - are appended. - - @par Complexity - Linear in `abs(size() - count)`, plus the cost of - reallocation if @ref capacity() is less than `count`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @param count The new size of the container. - - @param v The @ref value to copy into the new elements. + /** Overload + @param jv The @ref value to copy into the new elements. + @param count */ BOOST_JSON_DECL void resize( std::size_t count, - value const& v); + value const& jv); + /// @} /** Swap the contents. Exchanges the contents of this array with another array. Ownership of the respective - `boost::container::pmr::memory_resource` objects is not transferred. - - @li If `*other.storage() == *this->storage()`, - ownership of the underlying memory is swapped in - constant time, with no possibility of exceptions. - All iterators and references remain valid. + @ref boost::container::pmr::memory_resource objects is not transferred. + If `this == &other`, this function call has no effect. - @li If `*other.storage() != *this->storage()`, - the contents are logically swapped by making copies, - which can throw. In this case all iterators and - references are invalidated. + @li If `*other.storage() == *this->storage()` all iterators and + references remain valid. + @li Otherwise, the contents are logically swapped by making copies, + which can throw. In this case all iterators and references are + invalidated. @par Complexity - Constant or linear in @ref size() plus `other.size()`. + If `*storage() == *other.storage()`, then constant; otherwise linear in + `size() + other.size()`. @par Exception Safety - Strong guarantee. + No-throw guarantee if `*storage() == *other.storage()`. Otherwise + strong guarantee. Calls to `memory_resource::allocate` may throw. @param other The value to swap with. - If `this == &other`, this function call has no effect. */ BOOST_JSON_DECL void diff --git a/include/boost/json/basic_parser.hpp b/include/boost/json/basic_parser.hpp index dfecf57b5..8e34c5542 100644 --- a/include/boost/json/basic_parser.hpp +++ b/include/boost/json/basic_parser.hpp @@ -26,68 +26,50 @@ namespace json { /** An incremental SAX parser for serialized JSON. - This implements a SAX-style parser, invoking a - caller-supplied handler with each parsing event. - To use, first declare a variable of type - `basic_parser` where `T` meets the handler - requirements specified below. Then call - @ref write_some one or more times with the input, - setting `more = false` on the final buffer. - The parsing events are realized through member - function calls on the handler, which exists - as a data member of the parser. -\n - The parser may dynamically allocate intermediate - storage as needed to accommodate the nesting level - of the input JSON. On subsequent invocations, the - parser can cheaply re-use this memory, improving - performance. This storage is freed when the - parser is destroyed + This implements a SAX-style parser, invoking a caller-supplied handler with + each parsing event. To use, first declare a variable of type + `basic_parser` where `T` meets the handler requirements specified below. + Then call @ref write_some one or more times with the input, setting + `more = false` on the final buffer. The parsing events are realized through + member function calls on the handler, which exists as a data member of the + parser. + + The parser may dynamically allocate intermediate storage as needed to + accommodate the nesting level of the input JSON. On subsequent invocations, + the parser can cheaply re-use this memory, improving performance. This + storage is freed when the parser is destroyed @par Usage - - To get the declaration and function definitions - for this class it is necessary to include this - file instead: + To get the declaration and function definitions for this class it is + necessary to include this file instead: @code #include @endcode - Users who wish to parse JSON into the DOM container - @ref value will not use this class directly; instead - they will create an instance of @ref parser or - @ref stream_parser and use that instead. Alternatively, - they may call the function @ref parse. This class is - designed for users who wish to perform custom actions - instead of building a @ref value. For example, to - produce a DOM from an external library. -\n - @note + Users who wish to parse JSON into the DOM container @ref value will not use + this class directly; instead they will create an instance of @ref parser or + @ref stream_parser and use that instead. Alternatively, they may call the + function @ref parse. This class is designed for users who wish to perform + custom actions instead of building a @ref value. For example, to produce a + DOM from an external library. - By default, only conforming JSON using UTF-8 - encoding is accepted. However, select non-compliant - syntax can be allowed by construction using a + @note + By default, only conforming JSON using UTF-8 encoding is accepted. However, + select non-compliant syntax can be allowed by construction using a @ref parse_options set to desired values. @par Handler + The handler provided must be implemented as an object of class type which + defines each of the required event member functions below. The event + functions return a `bool` where `true` indicates success, and `false` + indicates failure. If the member function returns `false`, it must set the + error code to a suitable value. This error code will be returned by the + write function to the caller. - The handler provided must be implemented as an - object of class type which defines each of the - required event member functions below. The event - functions return a `bool` where `true` indicates - success, and `false` indicates failure. If the - member function returns `false`, it must set - the error code to a suitable value. This error - code will be returned by the write function to - the caller. -\n - Handlers are required to declare the maximum - limits on various elements. If these limits - are exceeded during parsing, then parsing - fails with an error. -\n - The following declaration meets the parser's - handler requirements: + Handlers are required to declare the maximum limits on various elements. If + these limits are exceeded during parsing, then parsing fails with an error. + + The following declaration meets the parser's handler requirements: @code struct handler @@ -256,9 +238,7 @@ namespace json { @see @ref parse, @ref stream_parser, - [Validating parser example](../../doc/html/json/examples.html#json.examples.validate). - - @headerfile + \<\\>. */ template class basic_parser @@ -468,14 +448,6 @@ class basic_parser } public: - /// Copy constructor (deleted) - basic_parser( - basic_parser const&) = delete; - - /// Copy assignment (deleted) - basic_parser& operator=( - basic_parser const&) = delete; - /** Destructor. All dynamically allocated internal memory is freed. @@ -493,11 +465,13 @@ class basic_parser */ ~basic_parser() = default; - /** Constructor. + /** Constructors. + + Overload **(1)** constructs the parser with the specified options, with + any additional arguments forwarded to the handler's constructor. - This function constructs the parser with - the specified options, with any additional - arguments forwarded to the handler's constructor. + `basic_parser` is not copyable or movable, so the copy constructor is + deleted. @par Complexity Same as `Handler( std::forward< Args >( args )... )`. @@ -505,12 +479,12 @@ class basic_parser @par Exception Safety Same as `Handler( std::forward< Args >( args )... )`. - @param opt Configuration settings for the parser. - If this structure is default constructed, the - parser will accept only standard JSON. + @param opt Configuration settings for the parser. If this structure is + default constructed, the parser will accept only standard JSON. + @param args Optional additional arguments forwarded to the handler's + constructor. - @param args Optional additional arguments - forwarded to the handler's constructor. + @{ */ template explicit @@ -518,6 +492,18 @@ class basic_parser parse_options const& opt, Args&&... args); + /// Overload + basic_parser( + basic_parser const&) = delete; + /// @} + + /** Assignment. + + This type cannot be copied or moved. The copy assignment is deleted. + */ + basic_parser& operator=( + basic_parser const&) = delete; + /** Return a reference to the handler. This function provides access to the constructed @@ -528,6 +514,8 @@ class basic_parser @par Exception Safety No-throw guarantee. + + @{ */ Handler& handler() noexcept @@ -535,22 +523,12 @@ class basic_parser return h_; } - /** Return a reference to the handler. - - This function provides access to the constructed - instance of the handler owned by the parser. - - @par Complexity - Constant. - - @par Exception Safety - No-throw guarantee. - */ Handler const& handler() const noexcept { return h_; } + /// @} /** Return the last error. @@ -570,17 +548,14 @@ class basic_parser return ec_; } - /** Return true if a complete JSON has been parsed. - - This function returns `true` when all of these - conditions are met: + /** Check if a complete JSON text has been parsed. - @li A complete serialized JSON has been - presented to the parser, and + This function returns `true` when all of these conditions are met: - @li No error or exception has occurred since the - parser was constructed, or since the last call - to @ref reset, + @li A complete serialized JSON text has been presented to the parser, + and + @li No error or exception has occurred since the parser was + constructed, or since the last call to @ref reset. @par Complexity Constant. @@ -612,14 +587,13 @@ class basic_parser /** Indicate a parsing failure. - This changes the state of the parser to indicate - that the parse has failed. A parser implementation - can use this to fail the parser if needed due to - external inputs. + This changes the state of the parser to indicate that the parse has + failed. A parser implementation can use this to fail the parser if + needed due to external inputs. - @note - - If `!ec`, the stored error code is unspecified. + @attention + If `! ec.failed()`, an implementation-defined error code that indicates + failure will be stored instead. @par Complexity Constant. @@ -627,65 +601,54 @@ class basic_parser @par Exception Safety No-throw guarantee. - @param ec The error code to set. If the code does - not indicate failure, an implementation-defined - error code that indicates failure will be stored - instead. + @param ec The error code to set. */ void fail(system::error_code ec) noexcept; - /** Parse some of an input string as JSON, incrementally. - - This function parses the JSON in the specified - buffer, calling the handler to emit each SAX - parsing event. The parse proceeds from the - current state, which is at the beginning of a - new JSON or in the middle of the current JSON - if any characters were already parsed. - \n - The characters in the buffer are processed - starting from the beginning, until one of the - following conditions is met: + /** Parse some of input characters as JSON, incrementally. - @li All of the characters in the buffer - have been parsed, or + This function parses the JSON text in the specified buffer, calling the + handler to emit each SAX parsing event. The parse proceeds from the + current state, which is at the beginning of a new JSON or in the middle + of the current JSON if any characters were already parsed. - @li Some of the characters in the buffer - have been parsed and the JSON is complete, or + The characters in the buffer are processed starting from the beginning, + until one of the following conditions is met: + @li All of the characters in the buffer have been parsed, or + @li Some of the characters in the buffer have been parsed and the JSON + is complete, or @li A parsing error occurs. - The supplied buffer does not need to contain the - entire JSON. Subsequent calls can provide more - serialized data, allowing JSON to be processed - incrementally. The end of the serialized JSON - can be indicated by passing `more = false`. + The supplied buffer does not need to contain the entire JSON. + Subsequent calls can provide more serialized data, allowing JSON to be + processed incrementally. The end of the serialized JSON can be + indicated by passing `more = false`. @par Complexity Linear in `size`. @par Exception Safety - Basic guarantee. - Calls to the handler may throw. - Upon error or exception, subsequent calls will - fail until @ref reset is called to parse a new JSON. + Basic guarantee. Calls to the handler may throw. + + Upon error or exception, subsequent calls will fail until @ref reset + is called to parse a new JSON. @return The number of characters successfully parsed, which may be smaller than `size`. - @param more `true` if there are possibly more - buffers in the current JSON, otherwise `false`. + @param more `true` if there are possibly more buffers in the current + JSON, otherwise `false`. - @param data A pointer to a buffer of `size` - characters to parse. + @param data A pointer to a buffer of `size` characters to parse. - @param size The number of characters pointed to - by `data`. + @param size The number of characters pointed to by `data`. @param ec Set to the error, if any occurred. + + @{ */ - /** @{ */ std::size_t write_some( bool more, @@ -699,7 +662,7 @@ class basic_parser char const* data, std::size_t size, std::error_code& ec); - /** @} */ + /// @} }; } // namespace json diff --git a/include/boost/json/impl/serialize.ipp b/include/boost/json/impl/serialize.ipp index f4ec35d6e..66747e7df 100644 --- a/include/boost/json/impl/serialize.ipp +++ b/include/boost/json/impl/serialize.ipp @@ -176,7 +176,7 @@ serialize( //---------------------------------------------------------- -//[example_operator_lt__lt_ +// tag::example_operator_lt_lt[] // Serialize a value into an output stream std::ostream& @@ -200,7 +200,7 @@ operator<<( std::ostream& os, value const& jv ) return os; } -//] +// end::example_operator_lt_lt[] static void diff --git a/include/boost/json/parse.hpp b/include/boost/json/parse.hpp index 538d5c85c..5c5dd7914 100644 --- a/include/boost/json/parse.hpp +++ b/include/boost/json/parse.hpp @@ -23,41 +23,43 @@ namespace json { /** Return parsed JSON as a @ref value. - This function parses an entire string in one - step to produce a complete JSON object, returned - as a @ref value. If the buffer does not contain a - complete serialized JSON, an error occurs. In this - case the returned value will be null, using the - [default memory resource]. + This function parses input in one step to produce a complete JSON + @ref value. If the input does not contain a complete serialized JSON, an + error occurs. In this case **(1)**, **(2)**, **(4)**, and **(5)** return a null value that + uses the [default memory resource], and set `ec` to the corresponding error + value. **(3)** and **(6)** throw an exception. @par Complexity - Linear in `s.size()`. + @li **(1)**, **(2)**, **(3)** linear in `s.size()`. + @li **(4)**, **(5)**, **(6)** linear in the size of consumed input. @par Exception Safety - Strong guarantee. + @li **(1)**, **(2)**, **(3)** strong guarantee. + @li **(4)**, **(5)**, **(6)** basic guarantee. + + __(3)__, **(6)** throw `boost::system::system_error` on failed parse. Calls to `memory_resource::allocate` may throw. + The stream `is` may throw as described by [`std::ios::exceptions`]. - @return A value representing the parsed JSON, - or a null if any error occurred. + @return A value representing the parsed JSON. @param s The string to parse. @param ec Set to the error, if any occurred. - @param sp The memory resource that the new value and all - of its elements will use. If this parameter is omitted, - the [default memory resource] is used. + @param sp The memory resource that the new value and all of its elements + will use. If this parameter is omitted, the [default memory resource] is + used. - @param opt The options for the parser. If this parameter - is omitted, the parser will accept only standard JSON. + @param opt The options for the parser. If this parameter is omitted, the + parser will accept only standard JSON. - @see - @ref parse_options, - @ref stream_parser. + @see @ref parse_options, @ref stream_parser, @ref value::operator>>. [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource + [`std::ios::exceptions`]: https://en.cppreference.com/w/cpp/io/basic_ios/exceptions */ -/** @{ */ +/// @{ BOOST_JSON_DECL value parse( @@ -66,6 +68,7 @@ parse( storage_ptr sp = {}, parse_options const& opt = {}); +/// Overload BOOST_JSON_DECL value parse( @@ -73,42 +76,8 @@ parse( std::error_code& ec, storage_ptr sp = {}, parse_options const& opt = {}); -/** @} */ - -/** Return parsed JSON as a @ref value. - - This function parses an entire string in one - step to produce a complete JSON object, returned - as a @ref value. If the buffer does not contain a - complete serialized JSON, an exception is thrown. - - @par Complexity - Linear in `s.size()`. - - @par Exception Safety - Strong guarantee. - Calls to `memory_resource::allocate` may throw. - - @return A value representing the parsed - JSON upon success. - - @param s The string to parse. - @param sp The memory resource that the new value and all - of its elements will use. If this parameter is omitted, - the [default memory resource] is used. - - @param opt The options for the parser. If this parameter - is omitted, the parser will accept only standard JSON. - - @throw boost::system::system_error Thrown on failure. - - @see - @ref parse_options, - @ref stream_parser. - - [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource -*/ +/// Overload BOOST_JSON_DECL value parse( @@ -116,42 +85,12 @@ parse( storage_ptr sp = {}, parse_options const& opt = {}); -/** Return parsed JSON as a @ref value. - - This function reads data from an input stream and parses it to produce a - complete JSON entity, returned as a @ref value. If the stream does not - contain a complete serialized JSON, or contains extra non-whitespace data, - an error occurs. In this case the returned value will be `null`, using the - [default memory resource]. - - @par Complexity - Linear in the size of consumed input. - - @par Exception Safety - Basic guarantee. - Calls to `memory_resource::allocate` may throw. - The stream may throw as described by - [`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions). - - @return A value representing the parsed JSON, - or a `null` if any error occurred. - +/** Overload @param is The stream to read from. - - @param ec Set to the error, if any occurred. - - @param sp The memory resource that the new value and all of its elements - will use. If this parameter is omitted, the [default memory resource] - is used. - - @param opt The options for the parser. If this parameter is omitted, the - parser will accept only standard JSON. - - @see @ref parse_options, @ref stream_parser, @ref value::operator>>. - - [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource + @param ec + @param sp + @param opt */ -/** @{ */ BOOST_JSON_DECL value parse( @@ -160,6 +99,7 @@ parse( storage_ptr sp = {}, parse_options const& opt = {}); +/// Overload BOOST_JSON_DECL value parse( @@ -167,46 +107,15 @@ parse( std::error_code& ec, storage_ptr sp = {}, parse_options const& opt = {}); -/** @} */ - -/** Return parsed JSON as a @ref value. - - This function reads data from an input stream and parses it to produce a - complete JSON entity, returned as a @ref value. If the stream does not - contain a complete serialized JSON, or contains extra non-whitespace data, - an exception is thrown. - - @par Complexity - Linear in the size of consumed input. - @par Exception Safety - Basic guarantee. - Throws `boost::system::system_error` on failed parse. - Calls to `memory_resource::allocate` may throw. - The stream may throw as described by - [`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions). - - @return A value representing the parsed JSON upon success. - - @param is The stream to read from. - - @param sp The memory resource that the new value and all of its elements - will use. If this parameter is omitted, the [default memory resource] - is used. - - @param opt The options for the parser. If this parameter is omitted, the - parser will accept only standard JSON. - - @see @ref parse_options, @ref stream_parser, @ref value::operator>>. - - [default memory resource]: json/allocators/storage_ptr.html#json.allocators.storage_ptr.default_memory_resource -*/ +/// Overload BOOST_JSON_DECL value parse( std::istream& is, storage_ptr sp = {}, parse_options const& opt = {}); +/// @} } // namespace json } // namespace boost diff --git a/include/boost/json/value_from.hpp b/include/boost/json/value_from.hpp index 21669878e..c3c010823 100644 --- a/include/boost/json/value_from.hpp +++ b/include/boost/json/value_from.hpp @@ -38,13 +38,13 @@ namespace json { @code template< class FullContext > - void tag_invoke( value_from_tag, value&, T, const Context& , const FullContext& ); + void tag_invoke( value_from_tag, value&, T, const Context&, const FullContext& ); @endcode or @code - void tag_invoke( value_from_tag, value&, T, const Context& ); + void tag_invoke( value_from_tag, value&, T, const Context& ); @endcode or @@ -59,6 +59,10 @@ namespace json { The `ctx` argument can be used either as a tag type to provide conversions for third-party types, or to pass extra data to the conversion function. + Overloads **(2)** and **(4)** construct their return value using the + @ref storage_ptr `sp`, which ensures that the memory resource is correctly + propagated. + @par Exception Safety Strong guarantee. @@ -76,6 +80,7 @@ namespace json { tag_invoke: A general pattern for supporting customisable functions */ +/// @{ template< class T, class Context > void value_from( @@ -90,69 +95,15 @@ value_from( detail::value_from_impl( cat(), jv, std::forward(t), ctx ); } -/** Convert an object of type `T` to @ref value. - - This function attempts to convert an object - of type `T` to @ref value using - - @li one of @ref value's constructors, - - @li a library-provided generic conversion, or - - @li a user-provided overload of `tag_invoke`. - - Out of the box the function supports types satisfying - SequenceContainer, - arrays, arithmetic types, `bool`, `std::tuple`, `std::pair`, - `std::variant`, `std::optional`, `std::monostate`, and `std::nullopt_t`. - - Conversion of other types is done by calling an overload of `tag_invoke` - found by argument-dependent lookup. Its signature should be similar to: - - @code - template< class FullContext > - void tag_invoke( value_from_tag, value&, T, const Context& , const FullContext& ); - @endcode - - or - - @code - void tag_invoke( value_from_tag, value&, T, const Context& ); - @endcode - - or - - @code - void tag_invoke( value_from_tag, value&, T ); - @endcode - - The overloads are checked for existence in that order and the first that - matches will be selected.
    - - A @ref value constructed with the @ref storage_ptr passed to - @ref value_from is passed as the second argument to ensure that the memory - resource is correctly propagated. - - @par Exception Safety - Strong guarantee. - - @tparam T The type of the object to convert. - - @tparam Context The type of context passed to the conversion function. - - @returns `t` converted to @ref value. - - @param t The object to convert. - - @param ctx Context passed to the conversion function. - - @param sp A storage pointer referring to the memory resource - to use for the returned @ref value. The default argument for this - parameter is `{}`. +/** Overload + @param t + @param ctx + @param sp A storage pointer referring to the memory resource to use for the + returned @ref value. - @see @ref value_from_tag, @ref value_to, - - tag_invoke: A general pattern for supporting customisable functions + @return Overloads **(2)** and **(4)** return `t` converted to @ref value. + Overloads **(1)** and **3** return `void` instead and pass their result via + the out parameter `jv`. */ template< class T, class Context > #ifndef BOOST_JSON_DOCS @@ -173,42 +124,7 @@ value_from( return jv; } -/** Convert an object of type `T` to @ref value. - - This function attempts to convert an object - of type `T` to @ref value using - - @li one of @ref value's constructors, - - @li a library-provided generic conversion, or - - @li a user-provided overload of `tag_invoke`. - - Out of the box the function supports types satisfying - SequenceContainer, - arrays, arithmetic types, `bool`, `std::tuple`, `std::pair`, - `std::variant`, `std::optional`, `std::monostate`, and `std::nullopt_t`. - - Conversion of other types is done by calling an overload of `tag_invoke` - found by argument-dependent lookup. Its signature should be similar to: - - @code - void tag_invoke( value_from_tag, value&, T ); - @endcode - - @par Exception Safety - Strong guarantee. - - @tparam T The type of the object to convert. - - @param t The object to convert. - - @param jv @ref value out parameter. - - @see @ref value_from_tag, @ref value_to, - - tag_invoke: A general pattern for supporting customisable functions -*/ +/// Overload template void value_from( @@ -218,51 +134,7 @@ value_from( value_from( static_cast(t), detail::no_context(), jv ); } -/** Convert an object of type `T` to @ref value. - - This function attempts to convert an object - of type `T` to @ref value using - - @li one of @ref value's constructors, - - @li a library-provided generic conversion, or - - @li a user-provided overload of `tag_invoke`. - - Out of the box the function supports types satisfying - SequenceContainer, - arrays, arithmetic types, `bool`, `std::tuple`, `std::pair`, - `std::variant`, `std::optional`, `std::monostate`, and `std::nullopt_t`. - - Conversion of other types is done by calling an overload of `tag_invoke` - found by argument-dependent lookup. Its signature should be similar to: - - @code - void tag_invoke( value_from_tag, value&, T ); - @endcode - - A @ref value constructed - with the @ref storage_ptr passed to @ref value_from is - passed as the second argument to ensure that the memory - resource is correctly propagated. - - @par Exception Safety - Strong guarantee. - - @tparam T The type of the object to convert. - - @returns `t` converted to @ref value. - - @param t The object to convert. - - @param sp A storage pointer referring to the memory resource - to use for the returned @ref value. The default argument for this - parameter is `{}`. - - @see @ref value_from_tag, @ref value_to, - - tag_invoke: A general pattern for supporting customisable functions -*/ +/// Overload template value value_from( @@ -272,6 +144,7 @@ value_from( return value_from( static_cast(t), detail::no_context(), std::move(sp) ); } +/// @} /** Determine if `T` can be converted to @ref value. diff --git a/include/boost/json/value_to.hpp b/include/boost/json/value_to.hpp index 0006a7dbb..ffde1f752 100644 --- a/include/boost/json/value_to.hpp +++ b/include/boost/json/value_to.hpp @@ -54,14 +54,22 @@ namespace json { @endcode The overloads are checked for existence in that order and the first that - matches will be selected.
    + matches will be selected. The object returned by the function call is returned by @ref value_to as - the result of the conversion.
    + the result of the conversion. The `ctx` argument can be used either as a tag type to provide conversions for third-party types, or to pass extra data to the conversion function. + Overload **(3)** is **deleted** and participates in overload resolution + only when `U` is not @ref value. The overload exists to prevent unintented + creation of temporary @ref value instances, e.g. + + @code + auto flag = value_to(true); + @endcode + @par Constraints @code ! std::is_reference< T >::value @@ -83,6 +91,7 @@ namespace json { @see @ref value_to_tag, @ref value_from, tag_invoke: A general pattern for supporting customisable functions +@{ */ template< class T, class Context > T @@ -96,51 +105,7 @@ value_to( value const& jv, Context const& ctx ) return detail::value_to_impl( cat(), value_to_tag(), jv, ctx ); } -/** Convert a @ref value to an object of type `T`. - - This function attempts to convert a @ref value - to `T` using - - @li one of @ref value's accessors, or - - @li a library-provided generic conversion, or - - @li a user-provided overload of `tag_invoke`. - - Out of the box the function supports types satisfying - SequenceContainer, - arrays, arithmetic types, `bool`, `std::tuple`, `std::pair`, - `std::variant`, `std::optional`, `std::monostate`, and `std::nullopt_t`. - - Conversion of other types is done by calling an overload of `tag_invoke` - found by argument-dependent lookup. Its signature should be similar to: - - @code - T tag_invoke( value_to_tag, const value& ); - @endcode - - The object returned by the function call is - returned by @ref value_to as the result of the - conversion. - - @par Constraints - @code - ! std::is_reference< T >::value - @endcode - - @par Exception Safety - Strong guarantee. - - @tparam T The type to convert to. - - @returns `jv` converted to `T`. - - @param jv The @ref value to convert. - - @see @ref value_to_tag, @ref value_from, - - tag_invoke: A general pattern for supporting customisable functions -*/ +/// Overload template T value_to(const value& jv) @@ -148,7 +113,17 @@ value_to(const value& jv) return value_to( jv, detail::no_context() ); } -/** Convert a @ref value to a `boost::system::result`. +/// Overload +template::value>::type +#endif +> +T +value_to(U const& jv) = delete; +/// @} + +/* Convert a @ref value to a `boost::system::result`. This function attempts to convert a @ref value to `result` using @@ -285,25 +260,7 @@ try_value_to(const value& jv) return try_value_to( jv, detail::no_context() ); } -/** Convert a @ref value to an object of type `T`. - - This overload is **deleted** and participates in overload resolution only - when `U` is not @ref value. The overload exists to prevent unintented - creation of temporary @ref value instances, e.g. - - @code - auto flag = value_to(true); - @endcode -*/ -template::value>::type -#endif -> -T -value_to(U const& jv) = delete; - -/** Determine a @ref value can be converted to `T`. +/** Determine if a @ref value can be converted to `T`. If @ref value can be converted to `T` via a call to @ref value_to, the static data member `value` diff --git a/test/doc_background.cpp b/test/doc_background.cpp index 1bbba7a1a..8c474acf0 100644 --- a/test/doc_background.cpp +++ b/test/doc_background.cpp @@ -37,18 +37,18 @@ template using allocator = ::std::allocator; } // std -//[doc_background_1 +// tag::doc_background_1[] namespace std { template< class T, class Allocator = std::allocator< T > > class vector; } // namespace std -//] +// end::doc_background_1[] //---------------------------------------------------------- -//[doc_background_2 +// tag::doc_background_2[] namespace std { template< class T, class Allocator > @@ -58,7 +58,7 @@ class vector explicit vector( Allocator const& alloc ); //... -//] +// end::doc_background_2[] }; template @@ -67,7 +67,7 @@ vector::vector(A const&){} //---------------------------------------------------------- -//[doc_background_3 +// tag::doc_background_3[] namespace std { namespace pmr { @@ -88,11 +88,11 @@ class memory_resource } // namespace pmr } // namespace std -//] +// end::doc_background_3[] //---------------------------------------------------------- -//[doc_background_4 +// tag::doc_background_4[] namespace std { namespace pmr { @@ -103,7 +103,7 @@ using vector = std::vector< T, boost::container::pmr::polymorphic_allocator< T > } // namespace pmr } // namespace std -//] +// end::doc_background_4[] //---------------------------------------------------------- @@ -113,7 +113,7 @@ using namespace background; //---------------------------------------------------------- { struct T {}; -//[doc_background_5 +// tag::doc_background_5[] // A type of memory resource monotonic_resource mr; @@ -122,11 +122,11 @@ vector< T > v1(( boost::container::pmr::polymorphic_allocator< T >(&mr) )); // Or this way, since construction from memory_resource* is implicit: vector< T > v2( &mr ); -//] +// end::doc_background_5[] } //---------------------------------------------------------- { -//[doc_background_6 +// tag::doc_background_6[] { // A type of memory resource which uses a stack buffer unsigned char temp[4096]; @@ -137,7 +137,7 @@ vector< T > v2( &mr ); // The vector will allocate from `temp` first, and then the heap. } -//] +// end::doc_background_6[] } //---------------------------------------------------------- @@ -151,7 +151,7 @@ struct my_resource : container::pmr::memory_resource void do_deallocate( void*, size_t, size_t ) override {} bool do_is_equal ( memory_resource const& ) const noexcept override { return true; } }; -//[doc_background_7 +// tag::doc_background_7[] namespace my_library { std::pmr::vector get_chars1() @@ -163,11 +163,11 @@ std::pmr::vector get_chars1() } } // my_library -//] +// end::doc_background_7[] //---------------------------------------------------------- -//[doc_background_8 +// tag::doc_background_8[] namespace my_library { std::pmr::vector get_chars2() @@ -183,7 +183,7 @@ std::pmr::vector get_chars2() } } // my_library -//] +// end::doc_background_8[] //---------------------------------------------------------- diff --git a/test/doc_forward_conversion.cpp b/test/doc_forward_conversion.cpp index a1fa4807b..a2c7f2f7b 100644 --- a/test/doc_forward_conversion.cpp +++ b/test/doc_forward_conversion.cpp @@ -7,7 +7,7 @@ // Official repository: https://github.com/boostorg/json // -//[doc_forward_conversion_1 +// tag::doc_forward_conversion_1[] namespace boost { namespace json { @@ -30,9 +30,9 @@ try_value_to( const value& jv ); } } -//] +// end::doc_forward_conversion_1[] -//[doc_forward_conversion_3 +// tag::doc_forward_conversion_3[] namespace boost { namespace json { @@ -55,13 +55,13 @@ try_value_to( const value& jv, const Context& ctx ); } } -//] +// end::doc_forward_conversion_3[] #include "doc_types.hpp" #include -//[doc_forward_conversion_2 +// tag::doc_forward_conversion_2[] namespace user_ns { @@ -108,9 +108,10 @@ tag_invoke( } } -//] +// end::doc_forward_conversion_2[] + +// tag::doc_forward_conversion_3[] -//[doc_forward_conversion_4 namespace user_ns { @@ -153,7 +154,7 @@ tag_invoke( } } -//] +// end::doc_forward_conversion_3[] #include #include diff --git a/test/doc_parsing.cpp b/test/doc_parsing.cpp index 94ebb9726..914e6a292 100644 --- a/test/doc_parsing.cpp +++ b/test/doc_parsing.cpp @@ -28,22 +28,22 @@ static void set1() { //---------------------------------------------------------- { -//[doc_parsing_1 +// tag::doc_parsing_1[] value jv = parse( "[1,2,3,4,5]" ); -//] +// end::doc_parsing_1[] } //---------------------------------------------------------- { -//[doc_parsing_2 +// tag::doc_parsing_2[] boost::system::error_code ec; value jv = parse( "[1,2,3,4,5]", ec ); if( ec ) std::cout << "Parsing failed: " << ec.message() << "\n"; -//] +// end::doc_parsing_2[] } //---------------------------------------------------------- { -//[doc_parsing_3 +// tag::doc_parsing_3[] try { boost::system::error_code ec; @@ -55,50 +55,53 @@ catch( std::bad_alloc const& e) { std::cout << "Parsing failed: " << e.what() << "\n"; } -//] +// end::doc_parsing_3[] } //---------------------------------------------------------- { -//[doc_parsing_4 -{ +// tag::doc_parsing_4[] monotonic_resource mr; - value const jv = parse( "[1,2,3,4,5]", &mr ); -} -//] +// end::doc_parsing_4[] } //---------------------------------------------------------- { -//[doc_parsing_5 -parse_options opt; // all extensions default to off -opt.allow_comments = true; // permit C and C++ style comments to appear in whitespace -opt.allow_trailing_commas = true; // allow an additional trailing comma in object and array element lists -opt.allow_invalid_utf8 = true; // skip utf-8 validation of keys and strings -opt.allow_invalid_utf16 = true; // replace invalid surrogate pair UTF-16 code point(s) with the Unicode replacement character +// tag::doc_parsing_5[] +parse_options opt; // all extensions default to off +opt.allow_comments = true; // permit C and C++ style comments + // to appear in whitespace +opt.allow_trailing_commas = true; // allow an additional trailing comma in + // object and array element lists +opt.allow_invalid_utf8 = true; // skip utf-8 validation of keys and strings +opt.allow_invalid_utf16 = true; // replace invalid surrogate pair UTF-16 code point(s) + // with the Unicode replacement character value jv = parse( "[1,2,3,] // comment ", storage_ptr(), opt ); -//] +// end::doc_parsing_5[] } //---------------------------------------------------------- { #if __cpp_designated_initializers >= 201707L { -//[doc_parsing_6 -value jv = parse( "[1,2,3,] // comment ", storage_ptr(), +// tag::doc_parsing_6[] +value jv = parse( + "[1,2,3,] // comment ", + storage_ptr(), { - .allow_comments = true, // permit C and C++ style comments to appear in whitespace + .allow_comments = true, // permit C and C++ style comments + // to appear in whitespace .allow_trailing_commas = true, // allow a trailing comma in object and array lists .allow_invalid_utf8 = true // skip utf-8 validation of keys and strings }); -//] +// end::doc_parsing_6[] } { -//[doc_parsing_15 +// tag::doc_parsing_15[] value jv = parse( "{\"command\":\"\\uDF3E\\uDEC2\"}", storage_ptr(), { .allow_invalid_utf16 = true // replace illegal leading surrogate pair with �� }); -//] +// end::doc_parsing_15[] } #endif } @@ -107,22 +110,22 @@ value jv = parse( "{\"command\":\"\\uDF3E\\uDEC2\"}", storage_ptr(), } // set1 //---------------------------------------------------------- -//[doc_parsing_7 +// tag::doc_parsing_7[] class connection { - parser p_; // persistent data member + parser p_; // persistent data member public: - void do_read( string_view s ) // called for each complete message from the network + void do_read( string_view s ) // called for each complete message from the network { - p_.reset(); // start parsing a new JSON using the default resource - p_.write( s ); // parse the buffer, using exceptions to indicate error - do_rpc( p_.release() ); // process the command + p_.reset(); // start parsing a new JSON using the default resource + p_.write( s ); // parse the buffer, using exceptions to indicate error + do_rpc( p_.release() ); // process the command } void do_rpc( value jv ); }; -//] +// end::doc_parsing_7[] //---------------------------------------------------------- @@ -130,7 +133,7 @@ static void set2() { //---------------------------------------------------------- { -//[doc_parsing_8 +// tag::doc_parsing_8[] stream_parser p; boost::system::error_code ec; string_view s = "[1,2,3] %HOME%"; @@ -139,24 +142,25 @@ assert( ! ec && p.done() && n == 8 ); s = s.substr( n ); value jv = p.release(); assert( s == "%HOME%" ); -//] +// end::doc_parsing_8[] } //---------------------------------------------------------- { -//[doc_parsing_9 -parse_options opt; // All extensions default to off -opt.allow_comments = true; // Permit C and C++ style comments to appear in whitespace -opt.allow_trailing_commas = true; // Allow an additional trailing comma in object and array element lists -opt.allow_invalid_utf8 = true; // Skip utf-8 validation of keys and strings -stream_parser p( storage_ptr(), opt ); // The stream_parser will use the options -//] +// tag::doc_parsing_9[] +parse_options opt; // All extensions default to off +opt.allow_comments = true; // Permit C and C++ style comments to appear in whitespace +opt.allow_trailing_commas = true; // Allow an additional trailing comma in + // object and array element lists +opt.allow_invalid_utf8 = true; // Skip utf-8 validation of keys and strings +stream_parser p( storage_ptr(), opt ); // The stream_parser will use the options +// end::doc_parsing_9[] } //---------------------------------------------------------- } // set2 //---------------------------------------------------------- -//[doc_parsing_10 +// tag::doc_parsing_10[] value read_json( std::istream& is, boost::system::error_code& ec ) { stream_parser p; @@ -172,9 +176,9 @@ value read_json( std::istream& is, boost::system::error_code& ec ) return nullptr; return p.release(); } -//] +// end::doc_parsing_10[] -//[doc_parsing_14 +// tag::doc_parsing_14[] std::vector read_jsons( std::istream& is, boost::system::error_code& ec ) { std::vector< value > jvs; @@ -208,7 +212,7 @@ std::vector read_jsons( std::istream& is, boost::system::error_code& ec ) return jvs; } -//] +// end::doc_parsing_14[] //---------------------------------------------------------- @@ -216,27 +220,25 @@ static void set3() { //---------------------------------------------------------- { -//[doc_parsing_11 -{ +// tag::doc_parsing_11[] monotonic_resource mr; stream_parser p; - p.reset( &mr ); // Use mr for the resulting value - p.write( "[1,2,3,4,5]" ); // Parse the input JSON - value const jv = p.release(); // Retrieve the result - assert( *jv.storage() == mr ); // Same memory resource -} -//] + p.reset( &mr ); // Use mr for the resulting value + p.write( "[1,2,3,4,5]" ); // Parse the input JSON + value const jv = p.release(); // Retrieve the result + assert( *jv.storage() == mr ); // Same memory resource +// end::doc_parsing_11[] } //---------------------------------------------------------- { -//[doc_parsing_12 -unsigned char temp[ 4096 ]; // Declare our buffer +// tag::doc_parsing_12[] +unsigned char temp[ 4096 ]; // Declare our buffer stream_parser p( - storage_ptr(), // Default memory resource - parse_options{}, // Default parse options (strict parsing) - temp); // Use our buffer for temporary storage -//] + storage_ptr(), // Default memory resource + parse_options{}, // Default parse options (strict parsing) + temp); // Use our buffer for temporary storage +// end::doc_parsing_12[] } //---------------------------------------------------------- @@ -244,7 +246,7 @@ stream_parser p( //---------------------------------------------------------- -//[doc_parsing_13 +// tag::doc_parsing_13[] /* Parse JSON and invoke the handler This function parses the JSON specified in `s` @@ -264,16 +266,18 @@ stream_parser p( template< class Handler > void do_rpc( string_view s, Handler&& handler ) { - unsigned char temp[ 4096 ]; // The parser will use this storage for its temporary needs - parser p( // Construct a strict parser using the temp buffer and no dynamic memory - get_null_resource(), // The null resource never dynamically allocates memory - parse_options(), // Default constructed parse options allow only standard JSON + unsigned char temp[ 4096 ]; // The parser will use this storage for its temporary needs + parser p( // Construct a strict parser using + // the temp buffer and no dynamic memory + get_null_resource(), // The null resource never dynamically allocates memory + parse_options(), // Default constructed parse options allow only standard JSON temp ); - unsigned char buf[ 16384 ]; // Now we need a buffer to hold the actual JSON values - static_resource mr2( buf ); // The static resource is monotonic, using only a caller-provided buffer - p.reset( &mr2 ); // Use the static resource for producing the value - p.write( s ); // Parse the entire string we received from the network client + unsigned char buf[ 16384 ]; // Now we need a buffer to hold the actual JSON values + static_resource mr2( buf ); // The static resource is monotonic, + // using only a caller-provided buffer + p.reset( &mr2 ); // Use the static resource for producing the value + p.write( s ); // Parse the entire string we received from the network client // Retrieve the value and invoke the handler with it. // The value will use `buf` for storage. The handler @@ -281,18 +285,18 @@ void do_rpc( string_view s, Handler&& handler ) // are inefficient with mutation. handler( p.release() ); } -//] +// end::doc_parsing_13[] //---------------------------------------------------------- void testPrecise() { - //[doc_parsing_precise + // tag::doc_parsing_precise[] parse_options opt; opt.numbers = number_precision::precise; value jv = parse( "1002.9111801605201", storage_ptr(), opt ); - //] + // end::doc_parsing_precise[] (void)jv; assert( jv == 1002.9111801605201 ); } diff --git a/test/doc_quick_look.cpp b/test/doc_quick_look.cpp index d4859adf5..ce8aca19d 100644 --- a/test/doc_quick_look.cpp +++ b/test/doc_quick_look.cpp @@ -20,7 +20,7 @@ static void set1() { //---------------------------------------------------------- { -//[doc_quick_look_1 +// tag::doc_quick_look_1[] object obj; // construct an empty object obj[ "pi" ] = 3.141; // insert a double obj[ "happy" ] = true; // insert a bool @@ -29,11 +29,11 @@ obj[ "nothing" ] = nullptr; // insert a null obj[ "answer" ].emplace_object()["everything"] = 42; // insert an object with 1 element obj[ "list" ] = { 1, 0, 2 }; // insert an array with 3 elements obj[ "object" ] = { {"currency", "USD"}, {"value", 42.99} }; // insert an object with 2 elements -//] +// end::doc_quick_look_1[] } //---------------------------------------------------------- { -//[doc_quick_look_2 +// tag::doc_quick_look_2[] value jv = { { "pi", 3.141 }, { "happy", true }, @@ -47,11 +47,11 @@ value jv = { { "value", 42.99 } } } }; -//] +// end::doc_quick_look_2[] } //---------------------------------------------------------- { -//[doc_quick_look_3 +// tag::doc_quick_look_3[] array arr; // construct an empty array arr = { 1, 2, 3 }; // replace the contents with 3 elements value jv1( arr ); // this makes a copy of the array @@ -59,22 +59,22 @@ value jv2( std::move(arr) ); // this performs a move-cons assert( arr.empty() ); // moved-from arrays become empty arr = { nullptr, true, "boost" }; // fill in the array again -//] +// end::doc_quick_look_3[] } //---------------------------------------------------------- { -//[doc_quick_look_4 +// tag::doc_quick_look_4[] { unsigned char buf[ 4096 ]; // storage for our array static_resource mr( buf ); // memory resource which uses buf array arr( &mr ); // construct using the memory resource arr = { 1, 2, 3 }; // all allocated memory comes from `buf` } -//] +// end::doc_quick_look_4[] } //---------------------------------------------------------- { -//[doc_quick_look_5 +// tag::doc_quick_look_5[] { monotonic_resource mr; // memory resource optimized for insertion array arr( &mr ); // construct using the memory resource @@ -82,11 +82,11 @@ arr = { nullptr, true, "boost" }; // fill in the array again arr[ 0 ] = { 1, 2, 3 }; // assign an array to element 0 assert( *arr[0].storage() == *arr.storage() ); // same memory resource } -//] +// end::doc_quick_look_5[] } //---------------------------------------------------------- { -//[doc_quick_look_6 +// tag::doc_quick_look_6[] { monotonic_resource mr; boost::container::pmr::vector< value > vv( &mr ); @@ -97,7 +97,7 @@ arr = { nullptr, true, "boost" }; // fill in the array again assert( *vv.get_allocator().resource() == *vv[1].storage() ); assert( *vv.get_allocator().resource() == *vv[2].storage() ); } -//] +// end::doc_quick_look_6[] } //---------------------------------------------------------- @@ -105,7 +105,7 @@ arr = { nullptr, true, "boost" }; // fill in the array again //---------------------------------------------------------- -//[doc_quick_look_7 +// tag::doc_quick_look_7[] value f() { // create a reference-counted memory resource @@ -120,38 +120,38 @@ value f() // The caller receives the value, which still owns the resource return jv; } -//] +// end::doc_quick_look_7[] //---------------------------------------------------------- static void set2() { //---------------------------------------------------------- { -//[doc_quick_look_8 +// tag::doc_quick_look_8[] value jv = parse( "[1, 2, 3]" ); -//] +// end::doc_quick_look_8[] } //---------------------------------------------------------- { -//[doc_quick_look_9 +// tag::doc_quick_look_9[] boost::system::error_code ec; value jv = parse( R"( "Hello, world!" )", ec ); -//] +// end::doc_quick_look_9[] } //---------------------------------------------------------- { -//[doc_quick_look_10 +// tag::doc_quick_look_10[] unsigned char buf[ 4096 ]; static_resource mr( buf ); parse_options opt; opt.allow_comments = true; opt.allow_trailing_commas = true; value jv = parse( "[1, 2, 3, ] // array ", &mr, opt ); -//] +// end::doc_quick_look_10[] } //---------------------------------------------------------- { -//[doc_quick_look_11 +// tag::doc_quick_look_11[] stream_parser p; boost::system::error_code ec; p.reset(); @@ -163,19 +163,19 @@ if( ! ec ) if( ec ) return; value jv = p.release(); -//] +// end::doc_quick_look_11[] } //---------------------------------------------------------- { -//[doc_quick_look_12 +// tag::doc_quick_look_12[] value jv = { 1, 2, 3 }; std::string s = serialize( jv ); // produces "[1,2,3]" -//] +// end::doc_quick_look_12[] } //---------------------------------------------------------- { value jv; -//[doc_quick_look_13 +// tag::doc_quick_look_13[] serializer sr; sr.reset( &jv ); // prepare to output `jv` do @@ -184,7 +184,7 @@ do std::cout << sr.read( buf ); } while( ! sr.done() ); -//] +// end::doc_quick_look_13[] } //---------------------------------------------------------- @@ -192,7 +192,7 @@ while( ! sr.done() ); //---------------------------------------------------------- -//[doc_quick_look_14 +// tag::doc_quick_look_14[] namespace my_app { struct customer @@ -203,11 +203,11 @@ struct customer }; } // namespace my_app -//] +// end::doc_quick_look_14[] //---------------------------------------------------------- -//[doc_quick_look_15 +// tag::doc_quick_look_15[] namespace my_app { void tag_invoke( value_from_tag, value& jv, customer const& c ) @@ -220,24 +220,24 @@ void tag_invoke( value_from_tag, value& jv, customer const& c ) } } // namespace my_app -//] +// end::doc_quick_look_15[] static void set3() { //---------------------------------------------------------- { -//[doc_quick_look_16 +// tag::doc_quick_look_16[] my_app::customer c{ 1001, "Boost", true }; std::cout << serialize( value_from( c ) ); -//] +// end::doc_quick_look_16[] } //---------------------------------------------------------- { -//[doc_quick_look_17 +// tag::doc_quick_look_17[] std::vector< my_app::customer > vc; //... value jv = value_from( vc ); -//] +// end::doc_quick_look_17[] } //---------------------------------------------------------- @@ -245,7 +245,7 @@ value jv = value_from( vc ); //---------------------------------------------------------- -//[doc_quick_look_18 +// tag::doc_quick_look_18[] namespace my_app { // This helper function deduces the type and assigns the value with the matching key @@ -266,13 +266,13 @@ customer tag_invoke( value_to_tag< customer >, value const& jv ) } } // namespace my_app -//] +// end::doc_quick_look_18[] //---------------------------------------------------------- namespace my_app_2 { namespace my_app { using boost::json::my_app::customer; } -//[doc_quick_look_19 +// tag::doc_quick_look_19[] namespace my_app { customer tag_invoke( value_to_tag< customer >, value const& jv ) @@ -286,7 +286,7 @@ customer tag_invoke( value_to_tag< customer >, value const& jv ) } } // namespace my_app -//] +// end::doc_quick_look_19[] } // my_app_2 //---------------------------------------------------------- @@ -296,18 +296,18 @@ using namespace my_app; //---------------------------------------------------------- { -//[doc_quick_look_20 +// tag::doc_quick_look_20[] json::value jv; //... customer c( value_to(jv) ); -//] +// end::doc_quick_look_20[] } //---------------------------------------------------------- { json::value jv; -//[doc_quick_look_21 +// tag::doc_quick_look_21[] std::vector< customer > vc = value_to< std::vector< customer > >( jv ); -//] +// end::doc_quick_look_21[] } //---------------------------------------------------------- diff --git a/test/doc_serializing.cpp b/test/doc_serializing.cpp index 47b43e4d4..421c9a9b7 100644 --- a/test/doc_serializing.cpp +++ b/test/doc_serializing.cpp @@ -23,41 +23,25 @@ static void set1() { //---------------------------------------------------------- { -//[doc_serializing_1 +// tag::doc_serializing_1[] value jv = { 1, 2, 3, 4, 5 }; std::cout << jv << "\n"; -//] +// end::doc_serializing_1[] } //---------------------------------------------------------- { -//[doc_serializing_2 +// tag::doc_serializing_2[] value jv = { 1, 2, 3, 4, 5 }; std::string s = serialize( jv ); -//] -} -//---------------------------------------------------------- -{ -//[doc_serializing_3 -//] -} -//---------------------------------------------------------- -{ -//[doc_serializing_4 -//] +// end::doc_serializing_2[] } //---------------------------------------------------------- { //[doc_serializing_5 //] } -//---------------------------------------------------------- -{ -//[doc_serializing_6 -//] -} -//---------------------------------------------------------- } // set1 diff --git a/test/doc_storage_ptr.cpp b/test/doc_storage_ptr.cpp index 8d9fb16e3..7540b527a 100644 --- a/test/doc_storage_ptr.cpp +++ b/test/doc_storage_ptr.cpp @@ -26,18 +26,18 @@ static void set1() { //---------------------------------------------------------- { -//[doc_storage_ptr_1 +// tag::doc_storage_ptr_1[] storage_ptr sp1; storage_ptr sp2; assert( sp1.get() != nullptr ); // always points to a valid resource assert( sp1.get() == sp2.get() ); // both point to the default resource assert( *sp1.get() == *sp2.get() ); // the default resource compares equal -//] +// end::doc_storage_ptr_1[] } //---------------------------------------------------------- { -//[doc_storage_ptr_2 +// tag::doc_storage_ptr_2[] array arr; // default construction object obj; string str; @@ -46,15 +46,15 @@ value jv; assert( jv.storage().get() == storage_ptr().get() ); // uses the default memory resource assert( jv.storage().get() == arr.storage().get() ); // both point to the default resource assert( *arr.storage() == *obj.storage() ); // containers use equivalent resources -//] +// end::doc_storage_ptr_2[] } //---------------------------------------------------------- { -//[doc_storage_ptr_3 +// tag::doc_storage_ptr_3[] monotonic_resource mr; value const jv = parse( "[1,2,3]", &mr ); -//] +// end::doc_storage_ptr_3[] } //---------------------------------------------------------- @@ -62,16 +62,16 @@ value const jv = parse( "[1,2,3]", &mr ); //---------------------------------------------------------- -//[doc_storage_ptr_4 +// tag::doc_storage_ptr_4[] value parse_value( string_view s) { return parse( s, make_shared_resource< monotonic_resource >() ); } -//] +// end::doc_storage_ptr_4[] //---------------------------------------------------------- -//[doc_storage_ptr_5 +// tag::doc_storage_ptr_5[] template< class Handler > void do_rpc( string_view s, Handler&& h ) { @@ -80,7 +80,7 @@ void do_rpc( string_view s, Handler&& h ) value const jv = parse( s, &mr ); // Parse the input string into a value that uses our resource h( jv ); // Call the handler to perform the RPC command } -//] +// end::doc_storage_ptr_5[] //---------------------------------------------------------- @@ -88,23 +88,23 @@ void set2() { //---------------------------------------------------------- { -//[doc_storage_ptr_6 +// tag::doc_storage_ptr_6[] unsigned char buffer[ 8192 ]; static_resource mr( buffer ); // The resource will use our local buffer -//] +// end::doc_storage_ptr_6[] } //---------------------------------------------------------- { -//[doc_storage_ptr_7 +// tag::doc_storage_ptr_7[] monotonic_resource mr; array arr( &mr ); // construct an array using our resource arr.emplace_back( "boost" ); // insert a string assert( *arr[0].as_string().storage() == mr ); // the resource is propagated to the string -//] +// end::doc_storage_ptr_7[] } //---------------------------------------------------------- { -//[doc_storage_ptr_8 +// tag::doc_storage_ptr_8[] { monotonic_resource mr; @@ -112,25 +112,25 @@ assert( *arr[0].as_string().storage() == mr ); // the resource is propa assert( ! arr.storage().is_shared() ); // no shared ownership } -//] +// end::doc_storage_ptr_8[] } //---------------------------------------------------------- { -//[doc_storage_ptr_9 +// tag::doc_storage_ptr_9[] storage_ptr sp = make_shared_resource< monotonic_resource >(); string str( sp ); assert( sp.is_shared() ); // shared ownership assert( str.storage().is_shared() ); // shared ownership -//] +// end::doc_storage_ptr_9[] } //---------------------------------------------------------- } // set2 //---------------------------------------------------------- -//[doc_storage_ptr_10 +// tag::doc_storage_ptr_10[] class logging_resource : public boost::container::pmr::memory_resource { private: @@ -157,7 +157,7 @@ class logging_resource : public boost::container::pmr::memory_resource return dynamic_cast< logging_resource const* >( &other ) != nullptr; } }; -//] +// end::doc_storage_ptr_10[] //---------------------------------------------------------- diff --git a/test/doc_types.hpp b/test/doc_types.hpp index 1dbb908a4..ca902fe52 100644 --- a/test/doc_types.hpp +++ b/test/doc_types.hpp @@ -18,7 +18,7 @@ #include -//[snippet_conv_spec_trait1 +// tag::snippet_conv_spec_trait1[] namespace user_ns { @@ -62,7 +62,7 @@ struct tuple_element< N, user_ns::ip_address > }; } // namespace std -//] +// end::snippet_conv_spec_trait1[] namespace user_ns { diff --git a/test/doc_uses_allocator.cpp b/test/doc_uses_allocator.cpp index 9061f16f2..cad5fd30d 100644 --- a/test/doc_uses_allocator.cpp +++ b/test/doc_uses_allocator.cpp @@ -23,7 +23,7 @@ static void set1() { //---------------------------------------------------------- { -//[doc_uses_allocator_1 +// tag::doc_uses_allocator_1[] // We want to use this resource for all the containers monotonic_resource mr; @@ -38,11 +38,11 @@ v.emplace_back( "boost" ); // The vector propagates the memory resource to the string assert( v[0].storage().get() == &mr ); -//] +// end::doc_uses_allocator_1[] } //---------------------------------------------------------- { -//[doc_uses_allocator_2 +// tag::doc_uses_allocator_2[] // This vector will use the default memory resource std::vector< value, boost::container::pmr::polymorphic_allocator < value > > v; @@ -54,7 +54,7 @@ assert( ! jv.storage().is_shared() ); // and deallocate is never null assert( ! jv.storage().is_deallocate_trivial() ); -//] +// end::doc_uses_allocator_2[] } //---------------------------------------------------------- diff --git a/test/doc_using_numbers.cpp b/test/doc_using_numbers.cpp index 09dd681e7..ff72384bd 100644 --- a/test/doc_using_numbers.cpp +++ b/test/doc_using_numbers.cpp @@ -36,7 +36,7 @@ static void set1() { //---------------------------------------------------------- { -//[doc_using_numbers_1 +// tag::doc_using_numbers_1[] // construction from int value jv1 = 1; @@ -51,11 +51,11 @@ assert( jv2.is_uint64() ); value jv3 = 3.0; assert( jv3.is_double() ); -//] +// end::doc_using_numbers_1[] } //---------------------------------------------------------- { -//[doc_using_numbers_2 +// tag::doc_using_numbers_2[] value jv = 1; @@ -71,19 +71,19 @@ std::uint64_t r2 = jv.get_uint64(); if(double* d = jv.if_double()) assert( false ); -//] +// end::doc_using_numbers_2[] }; //---------------------------------------------------------- { -//[doc_using_numbers_3 +// tag::doc_using_numbers_3[] value jv = 1; assert( jv.to_number< int >() == 1 ); -//] +// end::doc_using_numbers_3[] } //---------------------------------------------------------- try { -//[doc_using_numbers_4 +// tag::doc_using_numbers_4[] value jv1 = 404; assert( jv1.is_int64() ); @@ -113,14 +113,14 @@ try // not a number, throws system_error int r6 = jv3.to_number< int >(); -//] +// end::doc_using_numbers_4[] } catch(...) { } //---------------------------------------------------------- { -//[doc_using_numbers_5 +// tag::doc_using_numbers_5[] value jv = 10.5; @@ -136,11 +136,11 @@ int r2 = jv.to_number< int >( ec ); assert( ec == error::not_exact ); -//] +// end::doc_using_numbers_5[] } //---------------------------------------------------------- { -//[doc_using_numbers_6 +// tag::doc_using_numbers_6[] value jv = parse("[-42, 100, 10.25, -299999999999999999998, 2e32]"); array ja = jv.as_array(); @@ -159,14 +159,8 @@ assert( ja[3].is_double() ); // contains exponent, represented as double assert( ja[4].is_double() ); -//] +// end::doc_using_numbers_6[] } -//---------------------------------------------------------- -{ -//[doc_using_numbers_7 -//] -} -//---------------------------------------------------------- } // set1 diff --git a/test/snippets.cpp b/test/snippets.cpp index 2abb2c96d..5d1c529c7 100644 --- a/test/snippets.cpp +++ b/test/snippets.cpp @@ -24,14 +24,13 @@ #include #include #include -#include #include #include #include "test_suite.hpp" #include "doc_types.hpp" -//[snippet_conv_spec_trait2 +// tag::snippet_conv_spec_trait2[] namespace boost { namespace json @@ -44,7 +43,7 @@ struct is_sequence_like< user_ns::ip_address > } // namespace json } // namespace boost -//] +// end::snippet_conv_spec_trait2[] namespace user_ns2 { @@ -56,7 +55,7 @@ class ip_address : public user_ns::ip_address using namespace boost::json; -//[snippet_tag_invoke_1 +// tag::snippet_tag_invoke_1[] void tag_invoke( const value_from_tag&, value& jv, ip_address const& addr ) { @@ -75,9 +74,9 @@ tag_invoke( const value_to_tag< ip_address >&, value const& jv ) arr.at(2).to_number< unsigned char >(), arr.at(3).to_number< unsigned char >() ); } -//] +// end::snippet_tag_invoke_1[] -//[snippet_nothrow_1 +// tag::snippet_nothrow_1[] result_for< ip_address, value >::type tag_invoke( const try_value_to_tag< ip_address >&, value const& jv ) { @@ -110,11 +109,11 @@ tag_invoke( const try_value_to_tag< ip_address >&, value const& jv ) return ip_address{ *oct1, *oct2, *oct3, *oct4 }; } -//] +// end::snippet_nothrow_1[] } // namespace user_ns -//[doc_context_conversion_1 +// tag::doc_context_conversion_1[] namespace user_ns { @@ -149,9 +148,9 @@ tag_invoke( } } -//] +// end::doc_context_conversion_1[] -//[doc_context_conversion_4 +// tag::doc_context_conversion_4[] namespace user_ns { @@ -173,9 +172,9 @@ tag_invoke( } } -//] +// end::doc_context_conversion_4[] -//[doc_context_conversion_6 +// tag::doc_context_conversion_6[] namespace user_ns { @@ -199,9 +198,9 @@ tag_invoke( } } -//] +// end::doc_context_conversion_6[] -//[doc_context_conversion_10 +// tag::doc_context_conversion_10[] namespace user_ns { @@ -253,14 +252,13 @@ tag_invoke( } } -//] +// end::doc_context_conversion_10[] namespace boost { namespace json { - namespace { -//[snippet_strings_5 +// tag::snippet_strings_5[] string greeting( string_view first_name, string_view last_name ) { const char hello[] = "Hello, "; @@ -278,22 +276,22 @@ string greeting( string_view first_name, string_view last_name ) js.grow( sz ); return js; } -//] +// end::snippet_strings_5[] void usingStrings() { { - //[snippet_strings_1 + // tag::snippet_strings_1[] string str1; // empty string, uses the default memory resource string str2( make_shared_resource() ); // empty string, uses a counted monotonic resource - //] + // end::snippet_strings_1[] } { - //[snippet_strings_2 + // tag::snippet_strings_2[] std::string sstr1 = "helloworld"; std::string sstr2 = "world"; @@ -306,10 +304,10 @@ usingStrings() // this is equivalent to assert( sstr2.insert(0, sstr1, 0, 5) == "helloworld" ); - //] + // end::snippet_strings_2[] } { - //[snippet_strings_3 + // tag::snippet_strings_3[] std::string sstr = "hello"; @@ -322,11 +320,11 @@ usingStrings() assert(jstr.append("world") == "helloworld"); - //] + // end::snippet_strings_3[] } { - //[snippet_strings_4 + // tag::snippet_strings_4[] json::string str = "Boost.JSON"; json::string_view sv = str; @@ -340,7 +338,7 @@ usingStrings() str.compare("Boost"); - //] + // end::snippet_strings_4[] } { @@ -356,7 +354,7 @@ void usingValues() { { - //[snippet_value_1 + // tag::snippet_value_1[] value jv1; value jv2( nullptr ); @@ -364,10 +362,10 @@ usingValues() assert( jv1.is_null() ); assert( jv2.is_null() ); - //] + // end::snippet_value_1[] } { - //[snippet_value_2 + // tag::snippet_value_2[] value jv( object_kind ); @@ -375,24 +373,24 @@ usingValues() assert( jv.is_object() ); assert( ! jv.is_number() ); - //] + // end::snippet_value_2[] } { auto f = []{ - //[snippet_value_3 + // tag::snippet_value_3[] value jv( object_kind ); if( auto p = jv.if_object() ) return p->size(); - //] + // end::snippet_value_3[] return std::size_t(0); }; (void)f; } { - //[snippet_value_4 + // tag::snippet_value_4[] value jv; jv = value( array_kind ); @@ -403,10 +401,10 @@ usingValues() assert( jv.is_string() ); - //] + // end::snippet_value_4[] } { - //[snippet_value_5 + // tag::snippet_value_5[] value jv; jv.emplace_string() = "Hello, world!"; @@ -416,35 +414,35 @@ usingValues() assert( jv.is_int64() ); - //] + // end::snippet_value_5[] } { try { - //[snippet_value_6 + // tag::snippet_value_6[] value jv( true ); jv.as_bool() = true; jv.as_string() = "Hello, world!"; // throws an exception - //] + // end::snippet_value_6[] } catch(...) { } } { - //[snippet_value_7 + // tag::snippet_value_7[] value jv( string_kind ); if( string* str = jv.if_string() ) *str = "Hello, world!"; - //] + // end::snippet_value_7[] } { - //[snippet_value_8 + // tag::snippet_value_8[] value jv( string_kind ); @@ -452,10 +450,10 @@ usingValues() // a null pointer is never dereferenced. *jv.if_string() = "Hello, world!"; - //] + // end::snippet_value_8[] } { - //[snippet_value_9 + // tag::snippet_value_9[] value jv( string_kind ); if( boost::system::result str = jv.try_as_string() ) @@ -469,7 +467,7 @@ usingValues() { } - //] + // end::snippet_value_9[] } } @@ -479,7 +477,7 @@ void usingInitLists() { { - //[snippet_init_list_1 + // tag::snippet_init_list_1[] value jv = { { "name", "John Doe" }, @@ -488,11 +486,11 @@ usingInitLists() { "total-balance", 330.00 }, { "account-balances", { 84, 120, 126 } } }; - //] + // end::snippet_init_list_1[] } { - //[snippet_init_list_2 + // tag::snippet_init_list_2[] value jv = { true, 2, "hello", nullptr }; @@ -502,11 +500,11 @@ usingInitLists() assert( serialize(jv) == R"([true,2,"hello",null])" ); - //] + // end::snippet_init_list_2[] } { - //[snippet_init_list_3 + // tag::snippet_init_list_3[] value jv = { true, 2, "hello", { "bye", nullptr, false } }; @@ -516,20 +514,20 @@ usingInitLists() assert( serialize(jv) == R"([true,2,"hello",["bye",null,false]])" ); - //] + // end::snippet_init_list_3[] } { - //[snippet_init_list_4 + // tag::snippet_init_list_4[] // Should this be an array or an object? value jv = { { "hello", 42 }, { "world", 43 } }; - //] + // end::snippet_init_list_4[] } { - //[snippet_init_list_5 + // tag::snippet_init_list_5[] value jv1 = { { "hello", 42 }, { "world", 43 } }; @@ -549,11 +547,11 @@ usingInitLists() assert( jv2.is_array() && jv3.is_array() && jv4.is_array() ); - //] + // end::snippet_init_list_5[] } { - //[snippet_init_list_6 + // tag::snippet_init_list_6[] value jv = { { "hello", 42 }, array{ "world", 43 } }; @@ -565,13 +563,13 @@ usingInitLists() assert ( serialize(jv) == R"([["hello",42],["world",43]])" ); - //] + // end::snippet_init_list_6[] (void)ja; } { - //[snippet_init_list_7 + // tag::snippet_init_list_7[] value jv = { { "mercury", 36 }, { "venus", 67 }, { "earth", 93 } }; @@ -583,13 +581,13 @@ usingInitLists() assert( serialize(ja) == R"([["mercury",36],["venus",67],["earth",93]])" ); - //] + // end::snippet_init_list_7[] (void)ja; } { - //[snippet_init_list_8 + // tag::snippet_init_list_8[] object jo = { { "mercury", { { "distance", 36 } } }, { "venus", { 67, "million miles" } }, { "earth", 93 } }; @@ -597,11 +595,11 @@ usingInitLists() assert( jo["venus"].is_array() ); - //] + // end::snippet_init_list_8[] } { - //[snippet_init_list_9 + // tag::snippet_init_list_9[] object jo1 = { { "john", 100 }, { "dave", 500 }, { "joe", 300 } }; @@ -613,7 +611,7 @@ usingInitLists() assert( serialize(jv) == R"({"clients":{"john":100,"dave":500,"joe":300}})" ); - //] + // end::snippet_init_list_9[] (void)jo2; } @@ -625,24 +623,24 @@ void usingArrays() { { - //[snippet_arrays_1 + // tag::snippet_arrays_1[] array arr1; // empty array, uses the default memory resource array arr2( make_shared_resource() ); // empty array, uses a counted monotonic resource - //] + // end::snippet_arrays_1[] } { - //[snippet_arrays_2 + // tag::snippet_arrays_2[] array arr( { "Hello", 42, true } ); - //] + // end::snippet_arrays_2[] } try { - //[snippet_arrays_3 + // tag::snippet_arrays_3[] array arr; @@ -650,16 +648,16 @@ usingArrays() arr.emplace_back( 42 ); arr.emplace_back( true ); - //] + // end::snippet_arrays_3[] - //[snippet_arrays_4 + // tag::snippet_arrays_4[] assert( arr[0].as_string() == "Hello" ); // The following line throws system_error, since the index is out of range arr.at( 3 ) = nullptr; - //] + // end::snippet_arrays_4[] } catch (...) { @@ -672,23 +670,23 @@ void usingObjects() { { - //[snippet_objects_1 + // tag::snippet_objects_1[] object obj1; // empty object, uses the default memory resource object obj2( make_shared_resource() ); // empty object, uses a counted monotonic resource - //] + // end::snippet_objects_1[] } { - //[snippet_objects_2 + // tag::snippet_objects_2[] object obj( {{"key1", "value1" }, { "key2", 42 }, { "key3", false }} ); - //] + // end::snippet_objects_2[] } { - //[snippet_objects_3 + // tag::snippet_objects_3[] object obj; @@ -696,11 +694,11 @@ usingObjects() obj.emplace( "key2", 42 ); obj.emplace( "key3", false ); - //] + // end::snippet_objects_3[] } try { - //[snippet_objects_4 + // tag::snippet_objects_4[] object obj; @@ -711,19 +709,19 @@ usingObjects() // The following line throws system_error, since the key does not exist obj.at( "key4" ); - //] + // end::snippet_objects_4[] } catch (...) { } { - //[snippet_objects_5 + // tag::snippet_objects_5[] object obj{{"arr", {1, 11}}}; value& arr = obj.at("arr"); obj.emplace("added", "value"); // invalidates arr - //] + // end::snippet_objects_5[] (void)arr; } @@ -804,7 +802,7 @@ void usingExchange() { { - //[snippet_conv_1 + // tag::snippet_conv_1[] std::vector< int > v1{ 1, 2, 3, 4 }; @@ -816,14 +814,14 @@ usingExchange() std::vector< int > v2 = value_to< std::vector< int > >( jv ); assert( v1 == v2 ); - //] + // end::snippet_conv_1[] (void)v2; } { using namespace user_ns2; - //[snippet_tag_invoke_3 + // tag::snippet_tag_invoke_3[] std::map< std::string, ip_address > computers = { { "Alex", { 192, 168, 1, 1 } }, { "Blake", { 192, 168, 1, 2 } }, @@ -844,14 +842,14 @@ usingExchange() } )"); assert( jv == serialized ); - //] + // end::snippet_tag_invoke_3[] (void)jv; } { using namespace user_ns2; - //[snippet_tag_invoke_2 + // tag::snippet_tag_invoke_2[] ip_address addr = { 127, 0, 0, 12 }; value jv = value_from( addr ); assert( serialize( jv ) == R"([127,0,0,12])" ); @@ -860,14 +858,14 @@ usingExchange() ip_address addr2 = value_to< ip_address >( jv ); assert(std::equal( addr.begin(), addr.end(), addr2.begin() )); - //] + // end::snippet_tag_invoke_2[] (void)addr2; } { using namespace user_ns2; - //[snippet_nothrow_2 + // tag::snippet_nothrow_2[] value jv = parse( R"([127,0,0,12])" ); boost::system::result< ip_address > addr @@ -881,13 +879,13 @@ usingExchange() // this fails without exception addr = try_value_to< ip_address >( value() ); assert( addr.has_error() ); - //] + // end::snippet_nothrow_2[] (void)addr; (void)addr2; } { - //[snippet_conv_recursive + // tag::snippet_conv_recursive[] std::map< std::string, std::pair > m = { {"a", {1, false}}, {"b", {4, true}}, @@ -901,14 +899,14 @@ usingExchange() {"b", array{4, true}}, {"c", array{5, false}}, })); - //] + // end::snippet_conv_recursive[] } } void usingPointer() { - //[snippet_pointer_1 + // tag::snippet_pointer_1[] value jv = { {"one", 1}, {"two", 2} }; assert( jv.at("one") == jv.at_pointer("/one") ); @@ -917,11 +915,11 @@ usingPointer() jv.at_pointer("/one/foo") = {true, 4, "qwerty"}; assert( jv.at("one").at("foo").at(1) == jv.at_pointer("/one/foo/1") ); - //] + // end::snippet_pointer_1[] value* elem1 = [&]() -> value* { - //[snippet_pointer_2 + // tag::snippet_pointer_2[] object* obj = jv.if_object(); if( !obj ) return nullptr; @@ -943,15 +941,15 @@ usingPointer() return nullptr; return arr->if_contains(1); - //] + // end::snippet_pointer_2[] }(); value* elem2 = [&]() -> value* { - //[snippet_pointer_3 + // tag::snippet_pointer_3[] boost::system::error_code ec; return jv.find_pointer("/one/foo/1", ec); - //] + // end::snippet_pointer_3[] }(); (void)elem1; @@ -963,17 +961,17 @@ usingPointer() void usingSetAtPointer() { - //[snippet_pointer_4 + // tag::snippet_pointer_4[] value jv; jv.set_at_pointer("/two/bar/0", 12); assert( jv.is_object() ); assert( jv.at_pointer("/two").is_object() ); assert( jv.at_pointer("/two/bar").is_array() ); assert( jv.at_pointer("/two/bar/0") == 12 ); - //] + // end::snippet_pointer_4[] jv = nullptr; - //[snippet_pointer_5 + // tag::snippet_pointer_5[] set_pointer_options opts; opts.create_arrays = false; @@ -982,7 +980,7 @@ usingSetAtPointer() assert( jv.at_pointer("/two").is_object() ); assert( jv.at_pointer("/two/bar").is_object() ); // object, not array assert( jv.at_pointer("/two/bar/0") == 12 ); - //] + // end::snippet_pointer_5[] } BOOST_STATIC_ASSERT( @@ -993,40 +991,6 @@ BOOST_STATIC_ASSERT( BOOST_STATIC_ASSERT( has_value_to::value); -} // (anon) - -} // json -} // boost - -//---------------------------------------------------------- - - -namespace { - -class my_non_deallocating_resource { }; - -} // (anon) - -//[snippet_allocators_14 -namespace boost { -namespace json { - -template<> -struct is_deallocate_trivial< my_non_deallocating_resource > -{ - static constexpr bool value = true; -}; - -} // json -} // boost - -//] -namespace boost { -namespace json { - -namespace -{ - void usingSpecializedTrait() { @@ -1042,7 +1006,7 @@ usingContextualConversions() using namespace user_ns; using namespace boost::json; { -//[doc_context_conversion_2 +// tag::doc_context_conversion_2[] ip_address addr( 192, 168, 10, 11 ); value jv = value_from( addr, as_string() ); @@ -1051,12 +1015,12 @@ usingContextualConversions() ip_address addr2 = value_to< ip_address >( jv, as_string() ); assert(std::equal( addr.begin(), addr.end(), addr2.begin() )); -//] +// end::doc_context_conversion_2[] (void)addr2; } { -//[doc_context_conversion_3 +// tag::doc_context_conversion_3[] std::map< std::string, ip_address > computers = { { "Alex", { 192, 168, 1, 1 } }, { "Blake", { 192, 168, 1, 2 } }, @@ -1070,21 +1034,21 @@ usingContextualConversions() " \"Carol\": \"192.168.1.3\" " "} " ) ); -//] +// end::doc_context_conversion_3[] (void)jv; } { -//[doc_context_conversion_5 +// tag::doc_context_conversion_5[] std::chrono::system_clock::time_point tp; value jv = value_from( tp, as_iso_8601() ); assert( jv == parse(R"( "1970-01-01T00:00:00" )") ); -//] +// end::doc_context_conversion_5[] (void)jv; } { -//[doc_context_conversion_7 +// tag::doc_context_conversion_7[] std::chrono::system_clock::time_point tp; value jv = value_from( tp, date_format{ "%T %D", 18 } ); @@ -1092,12 +1056,12 @@ usingContextualConversions() jv = value_from( tp, as_iso_8601() ); assert( jv == parse(R"( "1970-01-01T00:00:00" )") ); -//] +// end::doc_context_conversion_7[] (void)jv; } { -//[doc_context_conversion_8 +// tag::doc_context_conversion_8[] using time_point = std::chrono::system_clock::time_point; time_point start; std::vector< std::pair > log = { @@ -1114,14 +1078,14 @@ usingContextualConversions() " [ \"1970-01-01T02:14:10\", \"192.168.10.10\" ] " " ] " ) ); -//] +// end::doc_context_conversion_8[] (void)jv; } { using time_point = std::chrono::system_clock::time_point; time_point start; -//[doc_context_conversion_9 +// tag::doc_context_conversion_9[] std::map< time_point, ip_address > log = { { start += std::chrono::seconds(10), {192, 168, 10, 11} }, @@ -1139,7 +1103,7 @@ usingContextualConversions() " \"1970-01-01T02:14:10\": \"192.168.10.10\" " " } " ) ); -//] +// end::doc_context_conversion_9[] (void)jv; } } @@ -1147,11 +1111,11 @@ usingContextualConversions() void usingParseInto() { -//[doc_parse_into_1 +// tag::doc_parse_into_1[] std::map< std::string, std::vector > vectors; string_view input = R"( { "even": [2,4,6], "odd": [1,3,5] } )"; parse_into(vectors, input); -//] +// end::doc_parse_into_1[] std::string output = serialize(vectors); (void)output;