From acf71573a2a0ef1c8d4d4ac215c62625d8f88033 Mon Sep 17 00:00:00 2001 From: lemosjose Date: Wed, 14 Jun 2023 12:00:38 -0300 Subject: [PATCH 1/2] land antora --- .github/workflows/publish.yml | 25 + .gitignore | 1 + antora-playbook.yml | 20 + doc/Jamfile.v2 | 60 -- doc/antora.yml | 7 + doc/core/core.qbk | 17 - doc/core/serialization.qbk | 17 - doc/dynamic/Jamfile.v2 | 45 - doc/dynamic/algorithm.qbk | 198 ---- doc/dynamic/concept.qbk | 194 ---- doc/dynamic/dynamic.qbk | 62 -- doc/dynamic/function.qbk | 170 ---- doc/dynamic/guide.qbk | 18 - doc/dynamic/type.qbk | 171 ---- doc/json/error.qbk | 68 -- doc/json/json.qbk | 42 - doc/json/oarchive.qbk | 21 - doc/json/overview.qbk | 38 - doc/json/reference.qbk | 10 - doc/json/token.qbk | 101 -- .../core/pages/adapter.adoc} | 84 +- doc/modules/core/pages/serialization.adoc | 14 + .../dynamic/pages/acknowledgement.adoc | 6 + doc/modules/dynamic/pages/algorithm.adoc | 198 ++++ doc/modules/dynamic/pages/concept.adoc | 197 ++++ .../dynamic/pages/converter.adoc} | 84 +- doc/modules/dynamic/pages/design.adoc | 97 ++ doc/modules/dynamic/pages/dynamic.adoc | 118 +++ doc/modules/dynamic/pages/function.adoc | 174 ++++ doc/modules/dynamic/pages/guide.adoc | 17 + .../dynamic/pages/rationale.adoc} | 92 +- doc/modules/dynamic/pages/reference.adoc | 41 + .../dynamic/pages/tutorial.adoc} | 69 +- doc/modules/dynamic/pages/type.adoc | 181 ++++ doc/modules/home/pages/introduction.adoc | 45 + .../json/pages/design.adoc} | 38 +- doc/modules/json/pages/error.adoc | 73 ++ .../json/pages/guide.adoc} | 42 +- .../json/pages/iarchive.adoc} | 41 +- doc/modules/json/pages/json.adoc | 29 + doc/modules/json/pages/oarchive.adoc | 20 + doc/modules/json/pages/overview.adoc | 26 + .../json/pages/reader.adoc} | 135 ++- doc/modules/json/pages/reference.adoc | 37 + doc/modules/json/pages/reference.html | 954 ++++++++++++++++++ doc/modules/json/pages/token.adoc | 97 ++ .../json/pages/tutorial.adoc} | 207 ++-- .../json/pages/writer.adoc} | 84 +- doc/nav.adoc | 31 + doc/protocol.qbk | 98 -- supplemental-ui/partials/footer-content.hbs | 8 + supplemental-ui/partials/header-content.hbs | 35 + 52 files changed, 2934 insertions(+), 1723 deletions(-) create mode 100644 .github/workflows/publish.yml create mode 100644 antora-playbook.yml delete mode 100644 doc/Jamfile.v2 create mode 100644 doc/antora.yml delete mode 100644 doc/core/core.qbk delete mode 100644 doc/core/serialization.qbk delete mode 100644 doc/dynamic/Jamfile.v2 delete mode 100644 doc/dynamic/algorithm.qbk delete mode 100644 doc/dynamic/concept.qbk delete mode 100644 doc/dynamic/dynamic.qbk delete mode 100644 doc/dynamic/function.qbk delete mode 100644 doc/dynamic/guide.qbk delete mode 100644 doc/dynamic/type.qbk delete mode 100644 doc/json/error.qbk delete mode 100644 doc/json/json.qbk delete mode 100644 doc/json/oarchive.qbk delete mode 100644 doc/json/overview.qbk delete mode 100644 doc/json/reference.qbk delete mode 100644 doc/json/token.qbk rename doc/{core/adapter.qbk => modules/core/pages/adapter.adoc} (62%) create mode 100644 doc/modules/core/pages/serialization.adoc create mode 100644 doc/modules/dynamic/pages/acknowledgement.adoc create mode 100644 doc/modules/dynamic/pages/algorithm.adoc create mode 100644 doc/modules/dynamic/pages/concept.adoc rename doc/{dynamic/converter.qbk => modules/dynamic/pages/converter.adoc} (74%) create mode 100644 doc/modules/dynamic/pages/design.adoc create mode 100644 doc/modules/dynamic/pages/dynamic.adoc create mode 100644 doc/modules/dynamic/pages/function.adoc create mode 100644 doc/modules/dynamic/pages/guide.adoc rename doc/{dynamic/rationale.qbk => modules/dynamic/pages/rationale.adoc} (66%) create mode 100644 doc/modules/dynamic/pages/reference.adoc rename doc/{dynamic/tutorial.qbk => modules/dynamic/pages/tutorial.adoc} (80%) create mode 100644 doc/modules/dynamic/pages/type.adoc create mode 100644 doc/modules/home/pages/introduction.adoc rename doc/{json/design.qbk => modules/json/pages/design.adoc} (63%) create mode 100644 doc/modules/json/pages/error.adoc rename doc/{json/guide.qbk => modules/json/pages/guide.adoc} (54%) rename doc/{json/iarchive.qbk => modules/json/pages/iarchive.adoc} (67%) create mode 100644 doc/modules/json/pages/json.adoc create mode 100644 doc/modules/json/pages/oarchive.adoc create mode 100644 doc/modules/json/pages/overview.adoc rename doc/{json/reader.qbk => modules/json/pages/reader.adoc} (71%) create mode 100644 doc/modules/json/pages/reference.adoc create mode 100644 doc/modules/json/pages/reference.html create mode 100644 doc/modules/json/pages/token.adoc rename doc/{json/tutorial.qbk => modules/json/pages/tutorial.adoc} (67%) rename doc/{json/writer.qbk => modules/json/pages/writer.adoc} (66%) create mode 100644 doc/nav.adoc delete mode 100644 doc/protocol.qbk create mode 100644 supplemental-ui/partials/footer-content.hbs create mode 100644 supplemental-ui/partials/header-content.hbs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..90dc743 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,25 @@ +name: Publish +on: + push: + branches: [ "master" ] + # for possible necessary rebuilds + workflow_dispatch: +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v2 + - name: Install node + uses: actions/setup-node@v2 + with: + node-version: '16' + - name: Install antora + run: npm i antora + - name: Build + run: npx antora --fetch antora-playbook.yml + - name: Publish to GH Pages + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: build/site diff --git a/.gitignore b/.gitignore index 58d50e0..4ddf159 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ CTestTestfile.cmake doc/html doc/*.xml *.diff +/build diff --git a/antora-playbook.yml b/antora-playbook.yml new file mode 100644 index 0000000..a191306 --- /dev/null +++ b/antora-playbook.yml @@ -0,0 +1,20 @@ +site: + title: trial.protocol + start_page: trial.protocol:home:introduction.adoc +content: + sources: + - url: https://gitlab.com/Leminhos/breese-asciidoc-conversion + branches: antora + start_path: doc +ui: + bundle: + url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/HEAD/raw/build/ui-bundle.zip?job=bundle-stable + supplemental_files: + - path: partials/header-content.hubs + contents: ./supplemental-ui/partials/header-content.hbs + - path: partials/footer-content.hbs + content: ./supplemental-ui/partials/footer-content.hbs + - path: .nojekyll + contents: | + static_files: + - .nojekyll diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 deleted file mode 100644 index 6b0fb7e..0000000 --- a/doc/Jamfile.v2 +++ /dev/null @@ -1,60 +0,0 @@ -# Trial.Protocol Doc Jamfile -# -# Copyright (C) 2015 Bjorn Reese -# 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) - -using quickbook ; -using boostbook ; -using doxygen ; - -import doxygen ; - -doxygen json_reference - : - ../include/trial/protocol/json/error.hpp - ../include/trial/protocol/json/reader.hpp - ../include/trial/protocol/json/writer.hpp - ../include/trial/protocol/json/serialization/iarchive.hpp - : - HIDE_UNDOC_MEMBERS=YES - "PREDEFINED=\"BOOST_DOXYGEN_INVOKED\"" - HIDE_COMPOUND_REFERENCE=YES - "JSON Reference" - ; - -doxygen dynamic_reference - : - ../include/trial/dynamic/token.hpp - ../include/trial/dynamic/error.hpp - ../include/trial/dynamic/variable.hpp - ../include/trial/dynamic/functional.hpp - ../include/trial/dynamic/algorithm/visit.hpp - : - HIDE_UNDOC_MEMBERS=YES - "PREDEFINED=\"BOOST_DOXYGEN_INVOKED\"" - HIDE_COMPOUND_REFERENCE=YES - "Dynamic Reference" - ; - -xml protocol - : - protocol.qbk - : - json_reference - dynamic_reference - ; - -boostbook standalone - : - protocol - : - # HTML options - #boost.root=../../../../.. - boost.root=http://www.boost.org/doc/libs/1_66_0 - chunk.section.depth=8 - toc.section.depth=3 - toc.max.depth=2 - generate.section.toc.level=2 - ; diff --git a/doc/antora.yml b/doc/antora.yml new file mode 100644 index 0000000..85ca566 --- /dev/null +++ b/doc/antora.yml @@ -0,0 +1,7 @@ +name: trial.protocol +version: protocol +asciidoc: + attributes: + table-caption: false +nav: +- nav.adoc diff --git a/doc/core/core.qbk b/doc/core/core.qbk deleted file mode 100644 index dfd26c0..0000000 --- a/doc/core/core.qbk +++ /dev/null @@ -1,17 +0,0 @@ -[/ - Copyright (C) 2017 Bjorn Reese - - 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). -] - -[#protocol.core] -[section Core] - -The core component contains types that are used by all protocols. - -[include adapter.qbk] -[include serialization.qbk] - -[endsect] diff --git a/doc/core/serialization.qbk b/doc/core/serialization.qbk deleted file mode 100644 index 67ec254..0000000 --- a/doc/core/serialization.qbk +++ /dev/null @@ -1,17 +0,0 @@ -[/ - Copyright (C) 2017 Bjorn Reese - - 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). -] - -[#protocol.core.serialization] -[section Serialization] - -The core serialization contains the glue between __protocol__ and -Boost.Serialization. This glue is only needed when implementing a new protocol. -Protocol users are instead referred to the serialization headers in the -protocol-specific packages. - -[endsect] diff --git a/doc/dynamic/Jamfile.v2 b/doc/dynamic/Jamfile.v2 deleted file mode 100644 index 4f4bce1..0000000 --- a/doc/dynamic/Jamfile.v2 +++ /dev/null @@ -1,45 +0,0 @@ -# Trial.Dynamic Doc Jamfile -# -# Copyright (C) 2017 Bjorn Reese -# 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) - -using quickbook ; -using boostbook ; -using doxygen ; - -import doxygen ; - -doxygen dynamic_reference - : - ../../include/trial/dynamic/token.hpp - ../../include/trial/dynamic/error.hpp - ../../include/trial/dynamic/variable.hpp - ../../include/trial/dynamic/functional.hpp - ../../include/trial/dynamic/algorithm/visit.hpp - : - "Reference" - HIDE_UNDOC_MEMBERS=YES - "PREDEFINED=\"BOOST_DOXYGEN_INVOKED\"" - ; - -xml dynamic - : - dynamic.qbk - : - dynamic_reference - ; - -boostbook standalone - : - dynamic - : - # HTML options - #boost.root=../../../../.. - boost.root=http://www.boost.org/doc/libs/1_66_0 - chunk.section.depth=8 - toc.section.depth=3 - toc.max.depth=2 - generate.section.toc.level=2 - ; diff --git a/doc/dynamic/algorithm.qbk b/doc/dynamic/algorithm.qbk deleted file mode 100644 index b5ae0a5..0000000 --- a/doc/dynamic/algorithm.qbk +++ /dev/null @@ -1,198 +0,0 @@ -[/ - Copyright (C) 2017 Bjorn Reese - - 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). -] - -[section Algorithms] - -The dynamic variable is accompanied by a number of algorithms, but also works with most standard C++ algorithms. - -[heading Count] - -Counts the number of matching keys or values in a dynamic variable. - -[note `#include `] - -There are two count algorithms. One counts matching keys, and the other counts matching values. They are located -in the `dynamic::key` and `dynamic::value` namespaces respectively. - -[table -[[Expression] [Return type] [Semantics] [Conditions]] -[[`key::count(v, t)`][`size_type`][Counts elements with key `t` in dynamic variable.][ /Requires:/ - -`T` is a supported type. - -/Effects:/ - -`std::count(v.key_begin(), v.key_end(), t)` - -/Returns:/ - -Number of elements in `v` matching `t`.]] -[[`value::count(v, t)`][`size_type`][Counts elements with value `t` in dynamic variable.][ /Requires:/ - -`T` is a supported type. - -/Effects:/ - -`std::count(v.begin(), v.end(), t)` - -/Returns:/ - -Number of elements in `v` matching `t`.]] -] - -[heading Find] - -Finds an element by key or by value in a dynamic variable. - -[note `#include `] - -[table -[[Expression] [Return type] [Semantics] [Conditions]] -[[`key::find(v, t)`][`key_iterator`][Finds element with key in dynamic variable.][ /Requires:/ - -`T` is a supported type. - -/Effects:/ - -`std::find(v.key_begin(), v.key_end(), t)` - -/Returns:/ - -Iterator pointing to element in `v` matching `t`, or `v.key_end()` if not such element exits.]] -[[`value::find(v, t)`][`iterator`][Finds element with value in dynamic variable.][ /Requires:/ - -`T` is a supported type. - -/Effects:/ - -`std::find(v.begin(), v.end(), t)` - -/Returns:/ - -Iterator pointing to element in `v` matching `t`, or `v.end()` if not such element exits.]] -] - -[heading Visit] - -Invokes a function call operator on a visitor object with the stored value of the dynamic variable as the function parameter. - -[note `#include `] - -In addition to various ways of doing type checking, we can also invoke a typed callback on a customized visitor object. -The function call operator always takes a single input parameter whose type is one of the supported types. -The normal C++ function overloading rules applies when selecting which function call operator to invoke. - -``` -struct my_visitor -{ - template - void operator()(T value) - { - std::cout << value << std::end; - } -}; - -int main() -{ - dynamic::variable data = { true, 2, 3.0, "alpha" }; - - my_visitor visitor; - dynamic::visit(vistor, data); - return 0; -} -``` - -[/ FIXME: Overloading: e.g. throw-on-nullable visitor ] - -[/ FIXME: Return type ] - -The function call operator may return a value. All function call operators must -use the same return type, which is also the return type of the `dynamic::visit` -algorithm. - -``` -struct my_returning_visitor -{ - template - dynamic::symbol::value operator()(T value) - { - dynamic::variable tmp(value); - return tmp.symbol(); - } -}; - -int main() -{ - dynamic::variable data = { true, 2, 3.0, "alpha" }; - - my_returning_visitor visitor; - auto symbol = dynamic::visit(vistor, data); - assert(symbol == dynamic::symbol::array); - return 0; -} -``` - -[/ FIXME: Mutable versus immutable ] -[/ FIXME: Recursive visitation ] - -[heading #include ] - -The dynamic variable works with standard C++ algorithms that require at most bi-directional iterators. - -[/ FIXME: key_iterator ] - -Some algorithms assume that if they take two ranges, then the second range is at least as long as the first. `dynamic::nullable` is an empty container, so it cannot be used as the second range. - -The algorithms listed in the table below have been verified. Excluded are sorting algorithms and algorithms requiring special operators apart from `operator+` (e.g. `std::inner_product` without binary predicates.) - -[table -[[Algorithm] [Caveat]] -[[`std::accumulate`] [None.]] -[[`std::adjacent_find`] [None.]] -[[`std::all_of`] [None.]] -[[`std::any_of`] [None.]] -[[`std::binary_search`] [None.]] -[[`std::copy`] [None.]] -[[`std::copy_backward`] [None.]] -[[`std::count`] [None.]] -[[`std::count_if`] [None.]] -[[`std::equal`] [Using `dynamic::nullable` as the first range causes true to be returned regardless of the second range. - -Using `dynamic::nullable` as the second range causes undefined behavior.]] -[[`std::equal_range`] [None.]] -[[`std::find`] [Using `dynamic::nullable` as the range causes nothing to be found.]] -[[`std::find_if`] [Using `dynamic::nullable` as the range causes nothing to be found.]] -[[`std::insert_iterator`] [None.]] -[[`std::iota`] [Only arithmetic types can be inserted.]] -[[`std::is_partitioned`] [None.]] -[[`std::is_sorted`] [None.]] -[[`std::lexicographical_compare`] [None.]] -[[`std::lower_bound`] [None.]] -[[`std::max_element`] [None.]] -[[`std::mismatch`] [Using `dynamic::nullable` as the second range causes undefined behavior.]] -[[`std::move`] [None.]] -[[`std::move_backward`] [None.]] -[[`std::none_of`] [None.]] -[[`std::partial_sum`] [None.]] -[[`std::partition`] [None.]] -[[`std::partition_point`] [None.]] -[[`std::remove`] [In associated arrays entries are removed by value but the key order is kept.]] -[[`std::replace`] [Cannot insert container as new value because iterators will be changed during replacement.]] -[[`std::reverse`] [None.]] -[[`std::rotate`] [No effect on singular values.]] -[[`std::search`] [Using `dynamic::nullable` as the second range always returns the first entry.]] -[[`std::stable_partition`] [None.]] -[[`std::swap_ranges`] [Using `dynamic::nullable` as the first range has no effect. - -Using `dynamic::nullable` as the second range causes undefined behavior.]] -[[`std::transform`] [None.]] -[[`std::unique`] [None.]] -[[`std::upper_bound`] [None.]] -] - -[endsect] diff --git a/doc/dynamic/concept.qbk b/doc/dynamic/concept.qbk deleted file mode 100644 index 73bc752..0000000 --- a/doc/dynamic/concept.qbk +++ /dev/null @@ -1,194 +0,0 @@ -[/ - Copyright (C) 2017 Bjorn Reese - - 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). -] - -[section Concepts] - -[important Work-in-progress.] - -[#dynamic-nested] -[heading NestedContainer] - -A *NestedContainer* is a [@http://en.cppreference.com/w/cpp/concept/Container Container] that can store sequences of elements and ordered sequences. - -[/ FIXME: v CopyInsertable etc.] - -[table Types -[[Name] [Type] [Notes]] -[[`array_type`] [] [SequenceContainer.]] -[[`map_type`] [] [AssociativeContainer.]] -[[`pair_type`] [] []] -] - -A NestedContainer must keep track of whether the stored type is `value_type`, `array_type`, or `map_type`. The semantics of insertion and erasure depends on the stored type. - -[table Singular semantics -[[Expression] [Return type] [Singular semantics] [Conditions]] -[[`a.clear()`] [`void`] [Assigns value-initialized `T` to `a`.] [ /Effects:/ - -`*a == T{}`]] -[[`a.erase(p)`] [`iterator`] [No effect.] [ /Returns:/ - -Iterator `p`.]] -[[`a.erase(i, j)`] [`iterator`] [No effect.] [ /Returns:/ - -Iterator `i`.]] -[[`a.insert(t)`] [`iterator`] [Fails.] []] -[[`a.insert(i, j)`] [`void`] [Fails.] []] -[[`a.insert(p, t)`] [`iterator`] [Fails.] []] -[[`a.insert(p, i, j)`] [`void`] [Fails.] []] -] - -[table Sequence semantics -[[Expression] [Return type] [Sequence semantics] [Conditions]] -[[`a.clear()`] [`void`] [Removes all nested elements.] [ /Effects:/ - -`a.empty() == true`]] -[[`a.erase(p)`] [`iterator`] [Removes a given element.] [ /Requires:/ - -`T` shall be `MoveAssignable`. - -/Effects:/ - -Erases the element pointed to by `p`. - -/Returns:/ - -Iterator pointing to the element immediately following `p` prior to the element being erased, or `a.end()` if no such element exists.]] -[[`a.erase(i, j)`] [`iterator`] [Removes all elements in range.] [ /Requires:/ - -`T` shall be `MoveAssignable`. - -/Effects:/ - -Erases the elements in the range \[`i`, `j`\). - -/Returns:/ - -Iterator pointing to the element pointed to by `j` prior to the element being erased, or `a.end()` if no such element exists.]] -[[`a.insert(t)`] [`iterator`] [Inserts element at end.] [ /Requires:/ - -`T` shall be `CopyInsertable` into `a`. - -/Effects:/ - -`a.insert(a.end(), t)` - -/Returns:/ - -Iterator `a.end()`]] -[[`a.insert(i, j)`] [`void`] [Inserts all elements in range at end.] [ /Requires:/ - -`T` shall be `EmplaceConstructible` into `a` from `*i`. - -`i` and `j` are not iterators into `a`. - -/Effects:/ - -`a.insert(a.end(), i, j)`]] -[[`a.insert(p, t)`] [`iterator`] [Inserts element before position.] [ /Requires:/ - -`T` shall be `CopyInsertable` into `a`. - -`p` is iterator into `a`. - -/Effects:/ - -Inserts a copy of `t` before position `p`. - -/Returns:/ - -Iterator `p`.]] -[[`a.insert(p, i, j)`] [`void`] [Inserts all elements in range before position.] [ /Requires:/ - -`T` shall be `EmplaceConstructible` into `a` from `*i`. - -`p` is iterator into `a`. - -`i` and `j` are not iterators into `a`. - -/Effects:/ - -Inserts a copy of each element in the range \[`i`, `j`\) into `a` before position `p`.]] -] - -[table Associative semantics -[[Expression] [Return type] [Associative semantics] [Conditions]] -[[`a.clear()`] [`void`] [Removes all nested elements.] [ /Effects:/ - -`a.empty() == true`]] -[[`a.erase(p)`] [`iterator`] [Removes a given element.] [ /Effects:/ - -Erases the element pointed to by `p`. - -/Returns:/ - -Iterator pointing to the element immediately following `p` prior to the element being erased, or `a.end()` if no such element exists.]] -[[`a.erase(i, j)`] [`iterator`] [Removes all elements in range.] [ /Effects:/ - -Erases the elements in the range \[`i`, `j`\). - -/Returns:/ - -Iterator pointing to the element pointed to by `j` prior to the element being erased, or `a.end()` if no such element exists.]] -[[`a.insert(t)`] [`iterator`] [Inserts `t`.] [ /Requires:/ - -`T` is a `pair_type`. - -/Effects:/ - -``` -p = std::find(a.begin(), a.end(), get<0>(t)) -a.insert(p, t)```]] -[[`a.insert(i, j)`] [`void`] [Inserts all elements in range \[`i`, `j`\)] [ /Requires:/ - -Each elements in range \[`i`, `j`\) is a `pair_type`.]] -[[`a.insert(p, t)`] [`iterator`] [Inserts `t` with position `p` as hint.] [ /Requires:/ - -`T` is a `pair_type`.]] -[[`a.insert(p, i, j)`] [`void`] [Inserts all elements in range \[`i`, `j`\) with position `p` as hint.] [ /Requires:/ - -Each elements in range \[`i`, `j`\) is a `pair_type`.]] -] - -[#dynamic-container] -[heading DynamicContainer] - -A *DynamicContainer* is a [link dynamic-nested NestedContainer] that can also store heterogenous elements, one of which is a nullable element indicating the absence of a value. - -[table Nullable semantics -[[Expression] [Return type] [Nullable semantics] [Conditions]] -[[`a.clear()`] [`void`] [No effect.] []] -[[`a.erase(p)`] [`iterator`] [No effect.] [ /Returns:/ - -Iterator `p`.]] -[[`a.erase(i, j)`] [`iterator`] [No effect.] [ /Returns:/ - -Iterator `i`.]] -[[`a.insert(t)`] [`iterator`] [Changes stored type to SequenceContainer and inserts element.] [ /Requires:/ - -`T` shall be `CopyInsertable` into `a`. - -/Effects:/ - -`a` becomes a sequenced array containing a copy of `t`. - -/Returns:/ - -Iterator `a.end()`.]] -[[`a.insert(i, j)`] [`void`] [Changes stored type to SequenceContainer and inserts all elements in range.] [ /Requires:/ - -`T` shall be `EmplaceConstructible` into `a`. - -/Effects:/ - -`a` becomes a sequenced array containing a copy of each element in the range \[`i`, `j`\).]] -[[`a.insert(p, t)`] [`iterator`] [Fails.] []] -[[`a.insert(p, i, j)`] [`void`] [Fails.] []] -] - -[endsect] diff --git a/doc/dynamic/dynamic.qbk b/doc/dynamic/dynamic.qbk deleted file mode 100644 index 05b9b9b..0000000 --- a/doc/dynamic/dynamic.qbk +++ /dev/null @@ -1,62 +0,0 @@ -[/ - Copyright (C) 2017 Bjorn Reese - - 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). -] - -[/library Trial.Dynamic - [quickbook 1.5] - [id protocol] - [dirname dynamic] - [purpose Dynamic variable] - [authors [Reese, Bjorn]] - [copyright 2017 Bjorn Reese] - [license Distributed under the [@http://www.boost.org/LICENSE_1_0.txt Boost Software License, Version 1.0].] - [source-mode c++] -] - -[section Dynamic Variable] - -[def __dynamic__ Trial.Dynamic] - -[important __dynamic__ is not an official Boost library. - -__dynamic__ is still work-in-progress. -] - -[section Overview] - -__dynamic__ is a C++11 header-only library with a C++ dynamic variable. - -* The dynamic variable is a [@https://en.wikipedia.org/wiki/Tagged_union tagged union] whose type and value can change dynamically during program execution. -* The dynamic variable supports [@http://en.cppreference.com/w/cpp/language/types fundamental data types][footnote Characters (`char`, `wchar_t`, `char16_t`, and `char32_t`) are not supported directly, but only indirectly via strings.][footnote `void` is not a [@http://stepanovpapers.com/DeSt98.pdf regular type], so it has been replaced with the `dynamic::nullable` type and the `dynamic::null` value.] and strings. -* The dynamic variable supports sequenced and associative containers of dynamic variables and can therefore act as a heterogenous tree data structure. -* The dynamic variable meets the requirements of [@http://en.cppreference.com/w/cpp/concept/Container Container] and therefore works with standard C++ algorithms. -* The dynamic variable can be customized with an allocator. - -The resemblance between `dynamic::variable` and [@http://en.cppreference.com/w/cpp/utility/variant `std::variant`] is obvious, but there are notable differences. While `std::variant` supports custom types, `dynamic::variable` is restricted to the above-mentioned data types and containers. This restriction enables `dynamic::variable` to adhere to the Container concept, and thus to have a richer interface that works with algorithms. - -Dynamic variables are useful for carrying configuration data, constructing parse trees for data formats, and protocol serialization. - -[endsect] - -[include tutorial.qbk] -[include guide.qbk] -[include rationale.qbk] - -[section Acknowledgement] - -Ferruccio Barletta for an earlier C++03 compliant [@https://github.com/ferruccio/dynamic-cpp dynamic variable]. - -Agustín Bergé for the article [@http://talesofcpp.fusionfenix.com/post-17/eggs.variant---part-i "Eggs.Variant - Part I"]. - -Peter Dimov for the article "Simple C++11 metaprogramming" ([@http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html part 1] and [@http://pdimov.com/cpp2/simple_cxx11_metaprogramming_2.html part 2].) - -Thomas Köppe for the article [@https://rawgit.com/google/cxx-std-draft/allocator-paper/allocator_user_guide.html "A Visitor's Guide to C++ Allocators"]. - -[endsect] - -[xinclude ../dynamic_reference.xml] -[endsect] diff --git a/doc/dynamic/function.qbk b/doc/dynamic/function.qbk deleted file mode 100644 index 5bec58c..0000000 --- a/doc/dynamic/function.qbk +++ /dev/null @@ -1,170 +0,0 @@ -[/ - Copyright (C) 2017 Bjorn Reese - - 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). -] - -[section Functions] - -[heading Construction] - -[/ FIXME: Nullable type can change type on insert() or operator+= ] -[/ FIXME: Assignment operator ] - -Dynamic variables can be created in various ways. - -[table -[[Expression] [Semantics] [Conditions]] -[[`V v`] [Creates a nullable variable.] [ /Effects:/ - -[@http://en.cppreference.com/w/cpp/language/default_initialization Default-initializes] -variable to the nullable type. - -/Example:/ - -``` -// Default-initializes to nullable type -dynamic::variable data; -assert(data.is()); -```]] -[[[pre `V v(t)` -`V v = t`]] [Creates a variable of given supported type.] [ /Requires:/ - -`T` shall be a supported type. - -/Effects:/ - -[@http://en.cppreference.com/w/cpp/language/direct_initialization Direct-initializes] -variable to value. - -/Example:/ - -``` -// Direct-initializes to integer type -dynamic::variable data(1); -assert(data.is()); -```]] -[[[pre `V v(u)` -`V v = u`]] [Creates a copy of another dynamic variable.] [ /Effects:/ - -[@http://en.cppreference.com/w/cpp/language/copy_initialization Copy-initializes] -variable with the type and value from another dynamic variable. - -/Example:/ - -``` -// Copy-initializes from other value -dynamic::variable other(3.0); -dynamic::variable data(other); -assert(data.is()); -```]] -[[[pre `V v { u1, u2, u3, ... }` -`V v = { u1, u2, u3, ... }`]] [Creates an array from initializer list.] [ /Effects:/ - -[@http://en.cppreference.com/w/cpp/language/list_initialization List-initializes] variable as array from initializer list. - -If the initializer list consists of a sequence of pairs, then the values will be stored as an associative array. Otherwise the values will be stored as an array. Nested lists are allowed. - -/Example:/ - -``` -// List-initializes heterogenous array from initializer list -dynamic::variable data { null, true, 2, 3.0, "alpha" }; -assert(data.is()); -``` -``` -// List-initializes nested integer array from initializer list -dynamic::variable data { 1, 2, { 3, 4 }, 5 }; -assert(data.is()); -```]] -[[[pre `V v { {u1, u2}, {u3, u4}, ... }` -`V v = { {u1, u2}, {u3, u4}, ... }`]] [Creates an associative array from initializer list.] [ /Requires:/ - -Initializer list shall be a sequence of pairs[footnote A pair is a dynamic variable containing an array with exactly two dynamic variables.]. - -/Effects:/ - -[@http://en.cppreference.com/w/cpp/language/list_initialization List-initializes] variable as associative array from initializer list. - -If the initializer list consists of a sequence of pairs, then the values will be stored as an associative array. Otherwise the values will be stored as an array. Nested lists are allowed. - -/Example:/ - -``` -// List-initializes associative array from initializer list -dynamic::variable data { { "alpha", 1 }, { "bravo", 2 } }; -assert(data.is()); -```]] -[[] [Creates an array from factory method.] []] -[[] [Creates an associative array from factory method.] []] -] - -[/ FIXME: Move to table above ] -[heading Factory Initialization] - -Arrays and associated arrays can also be explicitly created with factories. - -``` -// Example: List-initializes empty array from factory -auto data = dynamic::array::make(); -assert(data.is()); -assert(data.size() == 0); -``` -``` -// Example: List-initializes array of 42 null values from factory -auto data = dynamic::array::repeat(42, null); -assert(data.is()); -assert(data.size() == 42); -``` - -[heading Capacity] - -[table -[[Function] [Returns] [Description]] -[[`v.empty()`] [`bool`] [Returns true if the variable is empty or nullable; return false otherwise.]] -[[`v.size()`] [`size_type`] []] -[[`v.max_size()`] [`size_type`] []] -] - -[heading Accessor] - -The `assume_value()` functions have a narrow contract. The requested type `T` must match the stored type exactly. It is undefined behavior if the pre-condition `same()` is false. - -[table -[[Function] [Returns] [Description]] -[[`v.value()`] [`T`] [Returns stored element by value.]] -[[`v.operator T()`] [`T`] [Conversion operator. - -Same operation as `v.value()`.]] -[[`v.assume_value()`] [`T&`] [Returns stored element by reference.]] -[[`v.assume_value() const`] [`const T&`] []] -[[`v.operator[u]`] [`const variable&`] [Looks up element by key `u`.]] -[[`v.operator[n]`] [`const variable&`] [Looks up element by integer position `n`.]] -] - -[heading Modifiers] - -[table -[[Function] [Returns] [Description]] -[[`v.clear()`] [`void`] [Erases all nested elements.]] -[[`v.erase(p)`] [`iterator`] [Erases element at position `p`.]] -[[`v.erase(i, j)`] [`iterator`] [Erases all elements in range \[`i`, `j`\).]] -[[`v.insert(t)`] [`iterator`] [Inserts element `t`.]] -[[`v.insert(p, t)`] [`iterator`] [Inserts element `t` at position `p`.]] -[[`v.insert(i, j)`] [`void`] [Inserts elements in range \[`i`, `j`\).]] -[[`v.insert(p, i, j)`] [`void`] [Inserts elements in range \[`i`, `j`\) at position `p`.]] -[[`v.swap(u)`] [] []] -] - -[heading Operators] - -[/ FIXME: operator+= ] -[/ FIXME: operator+ ] - -[heading Iterators] - -[/ FIXME: begin() end() etc. ] - -[endsect] diff --git a/doc/dynamic/guide.qbk b/doc/dynamic/guide.qbk deleted file mode 100644 index 4ed27bb..0000000 --- a/doc/dynamic/guide.qbk +++ /dev/null @@ -1,18 +0,0 @@ -[/ - Copyright (C) 2015 Bjorn Reese - - 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). -] - -[#dynamic.guide] -[section User Guide] - -[include type.qbk] -[include function.qbk] -[include algorithm.qbk] -[include converter.qbk] -[include concept.qbk] - -[endsect] diff --git a/doc/dynamic/type.qbk b/doc/dynamic/type.qbk deleted file mode 100644 index e4cfbf7..0000000 --- a/doc/dynamic/type.qbk +++ /dev/null @@ -1,171 +0,0 @@ -[/ - Copyright (C) 2017 Bjorn Reese - - 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). -] - -[/ FIXME: references ] - -[section Types] - -`dynamic::variable` supports a pre-defined list of fundamental data types, strings, and containers. -These are generally referred to as the /supported types/. -Each type also belongs to a type category that is represented by a tag type. - -[table Supported types and tags -[[Stored type] [Tag type] [Description]] -[[`dynamic::nullable`] [[enumref trial::dynamic::nullable `dynamic::nullable`]] [No value.]] -[[`bool`] [[classref trial::dynamic::boolean `dynamic::boolean`]] [Boolean value.]] -[[`signed char`] [[classref trial::dynamic::integer `dynamic::integer`]] [Very short signed integer.]] -[[`signed short int`] [`dynamic::integer`] [Short signed integer.]] -[[`signed int`] [`dynamic::integer`] [Signed integer.]] -[[`signed long int`] [`dynamic::integer`] [Long signed integer.]] -[[`signed long long int`] [`dynamic::integer`] [Very long signed integer.]] -[[`unsigned char`] [`dynamic::integer`] [Very short unsigned integer.]] -[[`unsigned short int`] [`dynamic::integer`] [Short unsigned integer.]] -[[`unsigned int`] [`dynamic::integer`] [Unsigned integer.]] -[[`unsigned long int`] [`dynamic::integer`] [Long unsigned integer.]] -[[`unsigned long long int`] [`dynamic::integer`] [Very long unsigned integer.]] -[[`float`] [[classref trial::dynamic::real `dynamic::real`]] [Short floating-point number.]] -[[`double`] [`dynamic::real`] [Floating-point number.]] -[[`long double`] [`dynamic::real`] [Long floating-point number.]] -[[[classref trial::dynamic::basic_variable::string_type `dynamic::string_type`]] [[classref trial::dynamic::string `dynamic::string`]] [Narrow-character string. Same as `std::string` by default.]] -[[[classref trial::dynamic::basic_variable::wstring_type `dynamic::wstring_type`]] [[classref trial::dynamic::wstring `dynamic::wstring`]] [Wide-character string. Same as `std::wstring` by default.]] -[[[classref trial::dynamic::basic_variable::u16string_type `dynamic::u16string_type`]] [[classref trial::dynamic::u16string `dynamic::u16string`]] [UTF-16 character string. Same as `std::u16string` by default.]] -[[[classref trial::dynamic::basic_variable::u32string_type `dynamic::u32string_type`]] [[classref trial::dynamic::u32string `dynamic::u32string`]] [UTF-32 character string. Same as `std::u32string` by default.]] -[[[classref trial::dynamic::basic_variable::array_type `dynamic::array_type`]] [[classref trial::dynamic::array `dynamic::array`]] [Sequence of zero or more `dynamic::variable`s.]] -[[[classref trial::dynamic::basic_variable::map_type `dynamic::map_type`]] [[classref trial::dynamic::map `dynamic::map`]] [Ordered sequence of zero or more key-value pairs sorted by the key. Both key and value are `dynamic::variable`.]] -] - -[heading Type Checking] - -The current type of a variable can either be queried by type or tag. -It is also possible to obtain an enumerator that identifies the current type. - -Query-by-type is done with the [memberref trial::dynamic::basic_variable::same `same()`] function, which returns true if the current value is stored as type `T`. -``` -dynamic::variable data = 3.0; -assert(data.same()); // Value is stored as a double. -assert(!data.same()); // Valus is not stored as a float. -``` -Query-by-tag is done with the [memberref trial::dynamic::basic_variable::is `is()`] function, which returns true if the current value is stored as a type belonging to the category `T`. -`T` can be a tag or a type. In the latter case the associated tag is looked up, and the variable is queried using this tag. -``` -dynamic::variable data = 3.0; -assert(data.is()); // Value is stored as a floating-point number. -assert(data.is()); // Query using the dynamic::real tag. -assert(data.is()); // Query using the dynamic::real tag. -``` -Notice that any supported floating-point type can be used to query for the tag. - -Query-by-enumeration is done with the [memberref trial::dynamic::basic_variable::code `code()`] -or [memberref trial::dynamic::basic_variable::symbol `symbol()`] functions. -These returns an enumerator that indicates the type. The enumerator is suitable for `switch` statements. -``` -switch (data.symbol()) -{ -case dynamic::symbol::integer: - break; // Do integer stuff -case dynamic::symbol::real: - break; // Do floating-point number stuff -default: - break; // Do other stuff -} -``` - -[table Codes and symbols -[[Stored type] [Code] [Symbol]] -[[`dynamic::nullable`] [[enumref trial::dynamic::token::code::value `dynamic::code::null`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::null`]]] -[[`bool`] [[enumref trial::dynamic::token::code::value `dynamic::code::boolean`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::boolean`]]] -[[`signed char`] [[enumref trial::dynamic::token::code::value `dynamic::code::signed_char`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`signed short int`] [[enumref trial::dynamic::token::code::value `dynamic::code::signed_short_integer`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`signed int`] [[enumref trial::dynamic::token::code::value `dynamic::code::signed_integer`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`signed long int`] [[enumref trial::dynamic::token::code::value `dynamic::code::signed_long_integer`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`signed long long int`] [[enumref trial::dynamic::token::code::value `dynamic::code::signed_long_long_integer`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`unsigned char`] [[enumref trial::dynamic::token::code::value `dynamic::code::unsigned_char`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`unsigned short int`] [[enumref trial::dynamic::token::code::value `dynamic::code::unsigned_short_integer`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`unsigned int`] [[enumref trial::dynamic::token::code::value `dynamic::code::unsigned_integer`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`unsigned long int`] [[enumref trial::dynamic::token::code::value `dynamic::code::unsigned_long_integer`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`unsigned long long int`] [[enumref trial::dynamic::token::code::value `dynamic::code::unsigned_long_long_integer`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::integer`]]] -[[`float`] [[enumref trial::dynamic::token::code::value `dynamic::code::real`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::real`]]] -[[`double`] [[enumref trial::dynamic::token::code::value `dynamic::code::long_real`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::real`]]] -[[`long double`] [[enumref trial::dynamic::token::code::value `dynamic::code::long_long_real`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::real`]]] -[[`dynamic::string_type`] [[enumref trial::dynamic::token::code::value `dynamic::code::string`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::string`]]] -[[`dynamic::wstring_type`] [[enumref trial::dynamic::token::code::value `dynamic::code::wstring`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::wstring`]]] -[[`dynamic::u16string_type`] [[enumref trial::dynamic::token::code::value `dynamic::code::u16string`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::u16string`]]] -[[`dynamic::u32string_type`] [[enumref trial::dynamic::token::code::value `dynamic::code::u32string`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::u32string`]]] -[[`dynamic::array_type`] [[enumref trial::dynamic::token::code::value `dynamic::code::array`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::array`]]] -[[`dynamic::map_type`] [[enumref trial::dynamic::token::code::value `dynamic::code::map`]] [[enumref trial::dynamic::token::symbol::value `dynamic::symbol::map`]]] -] - -[heading Comparison] - -A dynamic variable can be compared against another dynamic variable, or against a supported type. Supported types are grouped into comparison categories as shown below. Comparison against unsupported types results in a compile-time error. - -[table -[[Comparison category] [Tag type] [Rank]] -[[Nullable] [[enumref trial::dynamic::nullable `dynamic::nullable`]] [0]] -[[Arithmetic] [[pre [classref trial::dynamic::boolean `dynamic::boolean`] -[classref trial::dynamic::integer `dynamic::integer`] -[classref trial::dynamic::real `dynamic::real`]]] [1]] -[[Narrow string] [[classref trial::dynamic::string `dynamic::string`]] [2]] -[[Wide string] [[classref trial::dynamic::wstring `dynamic::wstring`]] [3]] -[[UTF-16 string] [[classref trial::dynamic::u16string `dynamic::u16string`]] [4]] -[[UTF-32 string] [[classref trial::dynamic::u32string `dynamic::u32string`]] [5]] -[[Sequenced array] [[classref trial::dynamic::array `dynamic::array`]] [6]] -[[Associative array] [[classref trial::dynamic::map `dynamic::map`]] [7]] -] - -Equality operations first check the argument types. If the argument types belong to different comparison categories, then they are unequal. Otherwise their values are compared according to the normal C++ rules, with the addition that null compares equal to null. - -``` -dynamic::variable first; -dynamic::variable second = 2; - -assert(first.is()); -assert(second.is()); - -assert(first != second); // Incompatible types are unequal -``` - -Relative operations first check the argument types. If the argument types belong to different comparison categories, then their ranks are compared. The ranks are shown in the table above. For example, a nullable type is always less than other types, while an associative array is always greater than other types. Otherwise their values are compared according to the normal C++ rules. - -``` -dynamic::variable first; -dynamic::variable second = 2; - -assert(first.is()); -assert(second.is()); - -assert(first < second); // Null is less than integers -``` - -Sequenced arrays perform a pair-wise comparison of the elements. - -``` -dynamic::variable first = { 1, 20, 300 }; -dynamic::variable second = { 1, 20, 300 }; - -assert(first == second); -assert(first <= second); -assert(!(first < second)); -assert(first >= second); -assert(!(first > second)); -``` - -Associative arrays perform a pair-wise comparison of the key-value elements. - -``` -dynamic::variable first = { { "alpha", true } }; -dynamic::variable second = { { "alpha", true } }; - -assert(first == second); -assert(first <= second); -assert(!(first < second)); -assert(first >= second); -assert(!(first > second)); -``` - -[endsect] diff --git a/doc/json/error.qbk b/doc/json/error.qbk deleted file mode 100644 index ca03d3a..0000000 --- a/doc/json/error.qbk +++ /dev/null @@ -1,68 +0,0 @@ -[/ - Copyright (C) 2015 Bjorn Reese - - 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). -] - -[#protocol.json.error] -[section Error] - -The [@http://en.cppreference.com/w/cpp/header/system_error ``] -framework is used for error codes and exceptions. - -[note Error codes and utilities are located in the `` header.] - -[h5 Error codes] - -__protocol__ defines its own `json::error_category` with associated error -enumerator constants. - -Normally, an `error_code` of the current error can be obtained via an `error()` -member function. -``` -std::string input = "illegal"; -json::reader reader(input); - -assert(reader.symbol() == json::symbol::error); -assert(reader.error() == json::invalid_value); -``` - -This conversion can also be done manually with `json::to_errc()` and -`json::make_error_code()`. -``` -std::string input = "illegal"; -json::reader reader(input); - -assert(reader.symbol() == json::symbol::error); -assert(reader.code() == json::code::error_invalid_value); - -enum json::errc ec = json::to_errc(reader.code()); -assert(ec == json::invalid_value); - -auto error = json::make_error_code(ec); -assert(error == json::invalid_value); -``` - -The following error codes exists. - -[table Error codes -[[`json::errc`][Description]] -[[`unexpected_token`][An unexpected token is encountered in the input.]] -[[`invalid_key`][An associative array key is not in a valid format.]] -[[`invalid_value`][The content is not in a valid format.]] -[[`incompatible_type`][Conversion between two incompatible types failed.]] -[[`unbalanced_end_array`][Encountered an end array token without a corresponding begin array token.]] -[[`unbalanced_end_object`][Encountered an end object token without a corresponding begin object token.]] -[[`expected_end_array`][Encountered an end array token outside an array.]] -[[`expected_end_object`][Encountered an end object token outside an associative array.]] -] - -[h5 Exception] - -Conversion errors will result in `json::error` exceptions being thrown. -`json::error` inherits from `std::system_error` which contains -a `std::error_code`. - -[endsect] diff --git a/doc/json/json.qbk b/doc/json/json.qbk deleted file mode 100644 index 31b9ef6..0000000 --- a/doc/json/json.qbk +++ /dev/null @@ -1,42 +0,0 @@ -[/ - Copyright (C) 2015 Bjorn Reese - - 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). -] - -[#protocol.json] -[section JSON] - -[@http://json.org/ JSON] is a textual data format that encodes booleans, numbers, -and strings, as well as [@http://en.wikipedia.org/wiki/Array_data_structure arrays] -and [@http://en.wikipedia.org/wiki/Associative_array associative arrays] (called -JSON objects). - -[note -__protocol__ supports [@http://tools.ietf.org/html/rfc7159 RFC 7159]. -No [@http://json5.org/ JSON extensions] are supported. -] - -[section Overview] - -__protocol__ provides the following classes for JSON parsing and generation. - -[table -[[] [Parser] [Generator]] -[[Incremental] [[link protocol.json.reader `json::reader`]] [[link protocol.json.writer `json::writer`]]] -[[Serialization] [[link protocol.json.iarchive `json::iarchive`]] [[link protocol.json.oarchive `json::oarchive`]]] -[[Tree] [`json::parse`] [`json::format`]] -] -[endsect] - -[/ Serialization does not support pointers or inheritance/tracking ] -[/ Pointers/links can be implemented via http://www.w3.org/TR/json-ld/ ] - -[include tutorial.qbk] -[include guide.qbk] -[include design.qbk] -[include reference.qbk] - -[endsect] diff --git a/doc/json/oarchive.qbk b/doc/json/oarchive.qbk deleted file mode 100644 index 9a37775..0000000 --- a/doc/json/oarchive.qbk +++ /dev/null @@ -1,21 +0,0 @@ -[/ - Copyright (C) 2015 Bjorn Reese - - 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). -] - -[#protocol.json.oarchive] -[section Output Archive] - -Formatting a full data struture is done with an output archive (called oarchive.) - -``` -dynamic::var data; -// Insert some values into data -std::ostringstream result; -json::oarchive archive(result) -archive << data; -``` -[endsect] diff --git a/doc/json/overview.qbk b/doc/json/overview.qbk deleted file mode 100644 index ffbf8a2..0000000 --- a/doc/json/overview.qbk +++ /dev/null @@ -1,38 +0,0 @@ -[/ - Copyright (C) 2015 Bjorn Reese - - 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). -] - -[section:incremental Overview] - -__protocol__ provides several ways of parsing and generating JSON as shown in -the table below. - -[table Approaches to parsing and generating JSON -[[][Parser][Generator]] -[[*Incremental*] - [The JSON input can be parsed token by token with an [link protocol.json.reader - incremental parser]. For each token we can obtain its current type and value.] - [The JSON output can be generated token by token with an - [link protocol.json.writer incremental generator].]] -[[*Serialization*] - [The JSON input can be deserialized directly into our C++ data structures with an - [link protocol.json.iarchive input archive]. - Notice that the input archive does not go through an intermediate - [@http://en.wikipedia.org/wiki/Document_Object_Model@ Document Object Model], - so we can get better performance.] - [Our C++ data structures can be serialized directly into a JSON output with an - [link protocol.json.oarchive output archive].]] -] - -The incremental parser and generator are the basic building-blocks. -Serialization combines these building-blocks with Boost.Serialization to offer -an API that is easier to use. - -The incremental parser can also be used to create other kinds of parser interfaces, -such as a push parser as we shall see in the tutorial. - -[endsect] diff --git a/doc/json/reference.qbk b/doc/json/reference.qbk deleted file mode 100644 index 8f22c76..0000000 --- a/doc/json/reference.qbk +++ /dev/null @@ -1,10 +0,0 @@ -[/ - Copyright (C) 2015 Bjorn Reese - - 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). -] - -[#protocol.json.reference] -[xinclude ../json_reference.xml] diff --git a/doc/json/token.qbk b/doc/json/token.qbk deleted file mode 100644 index 4155fb3..0000000 --- a/doc/json/token.qbk +++ /dev/null @@ -1,101 +0,0 @@ -[/ - Copyright (C) 2015 Bjorn Reese - - 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). -] - -[#protocol.json.token] -[section Token] - -The JSON parser and generator use tokens to identify data types as well as errors. -All token-related types are located in the `trial::protocol::json::token` namespace, -which we will simply refer to as `token` below. -[note Tokens are located in the `` header.] - -[h4 Token constants] - -A token is represented by the `token::code` enumeration type with a constant for -each possible token or error state. This means that each error is represented -by its own enumerator constant. - -[h5 Symbols] - -Working directly with `token::code` can be tedious. -Suppose you want to check if an error occurred, then you have to check if the -current token is one of the numerous error constants. -Each `token::code` enumerator has therefore been grouped into a more convenient -enumeration type called `token::symbol` that is better suited for normal -operation. - -All `token::code` error constants have been grouped into the single -`token::symbol::error` constant, and we can now check for errors with a single -comparison. - -[table JSON symbol constants -[[`token::symbol`][Description]] -[[`boolean`][True or false.]] -[[`integer`][Integer number.]] -[[`number`][Floating-point number.]] -[[`string`][String value.]] -[[`key`][String key for associative array.]] -[[`null`][No data.]] -[[`begin_array`][Start of an array.]] -[[`end_array`][End of an array.]] -[[`begin_object`][Start of an associative array.]] -[[`end_object`][End of an associative array.]] -[[`separator`][A context-specific separator.]] -[[`end`][End of input or output buffer.]] -[[`error`][Erroneous format.]] -] - -The symbol type will be the preferred manner to use tokens in the examples -throughout this documentation. -In fact, we are not even going to describe the `token::code` enumerator constants -here,[footnote The description of all `token::code` enumerator constants can be -found in the reference documentation.] because we are only interested in the -subset that contains the error codes and they are described in the section on -[link protocol.json.error errors]. - -[h5 Categories] - -The symbol constants are grouped into the `token::category` enumeration type. -There are different categories of tokens: - -[table JSON category constants -[[`token::category`][Description]] -[[`data`] - [Data tokens have a value associated with them, whose content can be retrieved. - Examples of data tokens are booleans, numbers, and strings.]] -[[`structural`] - [Structural tokens wrap containers and separate items.]] -[[`nullable`] - [The nullable token is a special case, because it can represent either a data - token without and associated value or structural token without an associated - container, such as a missing integer or a missing array. - The nullable token is typeless.]] -[[`status`] - [A status token indicates another condition.]] -] - -The following table shows which categories the the various symbols belong to. - -[table Relation between symbols and categories -[[`token::symbol`][`token::category`]] -[[`boolean`][`data`]] -[[`integer`][`data`]] -[[`number`][`data`]] -[[`string`][`data`]] -[[`key`][`data`]] -[[`null`][`nullable`]] -[[`begin_array`][`structural`]] -[[`end_array`][`structural`]] -[[`begin_object`][`structural`]] -[[`end_object`][`structural`]] -[[`separator`][`structural`]] -[[`end`][`status`]] -[[`error`][`status`]] -] - -[endsect] diff --git a/doc/core/adapter.qbk b/doc/modules/core/pages/adapter.adoc similarity index 62% rename from doc/core/adapter.qbk rename to doc/modules/core/pages/adapter.adoc index d3394c5..bbaa6fb 100644 --- a/doc/core/adapter.qbk +++ b/doc/modules/core/pages/adapter.adoc @@ -1,13 +1,15 @@ -[/ - Copyright (C) 2017 Bjorn Reese +// +// +// Copyright (C) 2017 Bjorn Reese - 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). -] +// 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). +// -[#protocol.core.adapter] -[section Adapter] += Adapter + +TIP: The core component contains types that are used by all protocols When generating a protocol-specific encoded output, the result is written to an output buffer. Several types of output buffers can be used, such as arrays, @@ -16,27 +18,28 @@ wrapped by an adapter. Custom output buffer types can be added with adapter traits. The output buffer is passed via the constructor of the protocol generator. -__protocol__ has support for a number of commonly used output buffer types. +Trial.protocol has support for a number of commonly used output buffer types. Using any of these requires the inclusion of an associated header file as shown in the table below. -[table -[[Type] [Header file]] -[[`CharT[N]`] [``]] -[[`std::array`] [``]] -[[`std::basic_ostream`] [``]] -[[`std::basic_string`] [``]] -[[`std::vector`] [``]] -] + +|=== + +|Type |Header File +|`CharT[N]` |`` +|`std::array` |`` +|`std::basic_ostream`|`` +|`std::basic_string`|`` +|`std::vector`|`` + +|=== where `CharT` can be `char` or `unsigned char`. Wide characters are not supported. Support for other output buffer types can be added via adapter traits. -[endsect] - -[section Adapter Traits] +== Adapter Traits The encoded output can be written to other output buffer types. This is done by specifying a buffer adapter and a trait to select this @@ -45,21 +48,22 @@ buffer adapter for the output buffer type. The buffer adapter must inherit from `buffer::base` and implement the following API: -[table -[[Member] [Description]] -[[`bool grow(size_type)`] [Reserve space in the output buffer. Returns - false if the requested space cannot be reserved, in which case no further - data will be written to the output buffer.]] -[[`void write(value_type)`] [Output a single character.]] -[[`void write(const view_type&)`] [Output a sequence of characters.]] -] -[heading Tutorial: Deque Adapter] +|=== +| Member |Description +|`bool grow(size_type)` |Reserve space in the output buffer. Returns false if the requested space cannot be reserved, in which case no further data will be written to the output buffer. +|`void write(value_type)` |Output a single character. +|`void write(const view_type&)` |Output a sequence of characters. +|=== + +=== Tutorial: Deque Adapter Assume that we add support for `std::deque`. First the buffer wrapper for `std::deque` is written like this: -``` +[source, cpp] +---- + #include #include @@ -98,12 +102,15 @@ private: }; } // namespace my -``` + +---- Next we must make this wrapper known to the protocol generator, which is done as follows: -``` +[source,cpp] +---- + namespace trial { namespace protocol { namespace buffer { template @@ -113,10 +120,13 @@ struct traits< std::deque > }; }}} -``` -[note -The `traits` struct must be located inside the `trial::protocol::buffer` -namespace.] +---- + +NOTE: The `traits` struct must be located inside the `trial::protocol::buffer` namespace. + + + + + -[endsect] diff --git a/doc/modules/core/pages/serialization.adoc b/doc/modules/core/pages/serialization.adoc new file mode 100644 index 0000000..b9a8046 --- /dev/null +++ b/doc/modules/core/pages/serialization.adoc @@ -0,0 +1,14 @@ +// Copyright (C) 2017 Bjorn Reese + +// 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). +//] + + += Serialization + +The core serialization contains the glue between Trial.Protocol and +Boost.Serialization. This glue is only needed when implementing a new protocol. +Protocol users are instead referred to the serialization headers in the +protocol-specific packages. diff --git a/doc/modules/dynamic/pages/acknowledgement.adoc b/doc/modules/dynamic/pages/acknowledgement.adoc new file mode 100644 index 0000000..e91a0d4 --- /dev/null +++ b/doc/modules/dynamic/pages/acknowledgement.adoc @@ -0,0 +1,6 @@ += Acknowledgement + +Ferrucio Barletta for an earlier C++ 03 compliant https://github.com/ferruccio/dynamic-cpp[dynamic variable] + +Agustín Bergé for the article http://talesofcpp.fusionfenix.com/post-17/eggs.variant---part-i["Eggs.variant - Part I"] +Peter Dimov for the article "Simple C++11 metaprogramming" (http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html[part 1] and http://pdimov.com/cpp2/simple_cxx11_metaprogramming_2.html[part 2]) diff --git a/doc/modules/dynamic/pages/algorithm.adoc b/doc/modules/dynamic/pages/algorithm.adoc new file mode 100644 index 0000000..e0a161a --- /dev/null +++ b/doc/modules/dynamic/pages/algorithm.adoc @@ -0,0 +1,198 @@ +// +// Copyright (C) 2017 Bjorn Reese + +// 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). +// + += Algorithms + +The dynamic variable is accompanied by a number of algorithms, but also works with most standard C++ algorithms. + +=== Count + +Counts the number of matching keys or values in a dynamic variable. + +NOTE: `#include ` + +There are two count algorithms. One counts matching keys, and the other counts matching values. They are located +in the `dynamic::key` and `dynamic::value` namespaces respectively. + + +|=== +|Expression |Return type |Semantics |Conditions +|`key::count(v, t)` |`size_type` |Counts elements with key `t` in dynamic variable. | _Requires:_ + +`T` is a supported type. + +_Effects:_ + +`std::count(v.key_begin(), v.key_end(), t)` + +_Returns:_ + +Number of elements in `v` matching `t`. +|`value::count(v, t)` |`size_type` |Counts elements with value `t` in dynamic variable.| _Requires:_ + +`T` is a supported type. + +_Effects:_ + +`std::count(v.begin(), v.end(), t)` + +_Returns:_ + +Number of elements in `v` matching `t`. +|=== + +=== Find + +Finds an element by key or by value in a dynamic variable. + +NOTE: `#include ` + + +|=== +|Expression] |Return type |Semantics |Conditions +|`key::find(v, t)` |`key_iterator` |Finds element with key in dynamic variable. | _Requires:_ + + +`T` is a supported type. + +_Effects:_ + +`std::find(v.key_begin(), v.key_end(), t)` + +_Returns:_ + +Iterator pointing to element in `v` matching `t`, or `v.key_end()` if not such element exits.]] +|`value::find(v, t)` |`iterator` |Finds element with value in dynamic variable. | _Requires:_ + +`T` is a supported type. + +_Effects:_ + +`std::find(v.begin(), v.end(), t)` + +_Returns:_ + +Iterator pointing to element in `v` matching `t`, or `v.end()` if not such element exits.]] +|=== + +=== Visit + +Invokes a function call operator on a visitor object with the stored value of the dynamic variable as the function parameter. + +NOTE: `#include ` + +In addition to various ways of doing type checking, we can also invoke a typed callback on a customized visitor object. +The function call operator always takes a single input parameter whose type is one of the supported types. +The normal C++ function overloading rules applies when selecting which function call operator to invoke. + +[source,cpp] +---- +struct my_visitor +{ + template + void operator()(T value) + { + std::cout << value << std::end; + } +}; + +int main() +{ + dynamic::variable data = { true, 2, 3.0, "alpha" }; + + my_visitor visitor; + dynamic::visit(vistor, data); + return 0; +} +---- + +// FIXME: Overloading: e.g. throw-on-nullable visitor + +// FIXME: Return type + +The function call operator may return a value. All function call operators must +use the same return type, which is also the return type of the `dynamic::visit` +algorithm. + +[source,cpp] +---- +struct my_returning_visitor +{ + template + dynamic::symbol::value operator()(T value) + { + dynamic::variable tmp(value); + return tmp.symbol(); + } +}; + +int main() +{ + dynamic::variable data = { true, 2, 3.0, "alpha" }; + + my_returning_visitor visitor; + auto symbol = dynamic::visit(vistor, data); + assert(symbol == dynamic::symbol::array); + return 0; +} +---- + +// FIXME: Mutable versus immutable ] +// FIXME: Recursive visitation ] + +=== #include + +The dynamic variable works with standard C++ algorithms that require at most bi-directional iterators. + +// FIXME: key_iterator + +Some algorithms assume that if they take two ranges, then the second range is at least as long as the first. `dynamic::nullable` is an empty container, so it cannot be used as the second range. + +The algorithms listed in the table below have been verified. Excluded are sorting algorithms and algorithms requiring special operators apart from `operator+` (e.g. `std::inner_product` without binary predicates.) + + +|=== +|Algorithm |Caveat +|`std::accumulate` |None. +|`std::adjacent_find` |None. +|`std::all_of` |None. +|`std::any_of` |None. +|`std::binary_search` |None. +|`std::copy` |None. +|`std::copy_backward` |None. +|`std::count` |None. +|`std::count_if` |None. +|`std::equal` | Using `dynamic::nullable` as the first range causes true to be returned regardless of the second range. Using `dynamic::nullable` as the second range causes undefined behavior. +|`std::equal_range` |None. +|`std::find` |Using `dynamic::nullable` as the range causes nothing to be found. +|`std::find_if` |Using `dynamic::nullable` as the range causes nothing to be found. +|`std::insert_iterator` |None. +|`std::iota` |Only arithmetic types can be inserted. +|`std::is_partitioned` |None. +|`std::is_sorted` |None. +|`std::lexicographical_compare` |None. +|`std::lower_bound` |None. +|`std::max_element` |None.]] +|`std::mismatch` |Using `dynamic::nullable` as the second range causes undefined behavior. +|`std::move` |None. +|`std::move_backward` |None. +|`std::none_of` |None. +|`std::partial_sum` | None. +|`std::partition` | None. +|`std::partition_point` | None. +|`std::remove` | In associated arrays entries are removed by value but the key order is kept. +|`std::replace` | Cannot insert container as new value because iterators will be changed during replacement. +|`std::reverse` | None. +|`std::rotate` | No effect on singular values. +|`std::search` | Using `dynamic::nullable` as the second range always returns the first entry. +|`std::stable_partition` | None. +|`std::swap_ranges` | Using `dynamic::nullable` as the first range has no effect. Using `dynamic::nullable` as the second range causes undefined behavior. +|`std::transform` |None. +|`std::unique` |None. +|`std::upper_bound` |None. +|=== diff --git a/doc/modules/dynamic/pages/concept.adoc b/doc/modules/dynamic/pages/concept.adoc new file mode 100644 index 0000000..5474c15 --- /dev/null +++ b/doc/modules/dynamic/pages/concept.adoc @@ -0,0 +1,197 @@ +// +// Copyright (C) 2017 Bjorn Reese +// +// 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). +// + += Concepts + +IMPORTANT: Work-in-progress. + +== NestedContainer + +A *NestedContainer* is a http://en.cppreference.com/w/cpp/concept/Container[Container] that can store sequences of elements and ordered sequences. + +// FIXME: v CopyInsertable etc. + +.Types +|=== +|Name |Type |Notes +|`array_type` | |SequenceContainer. +|`map_type` | |AssociativeContainer. +|`pair_type` | | +|=== + +A NestedContainer must keep track of whether the stored type is `value_type`, `array_type`, or `map_type`. The semantics of insertion and erasure depends on the stored type. + +.Singular Semantics +|=== +|Expression |Return type |Singular semantics |Conditions +|`a.clear()` |`void` |Assigns value-initialized `T` to `a`. | _Effects:_ +`*a == T{}` +|`a.erase(p)` |`iterator` |No effect. | _Returns:_ +Iterator `p`. +|`a.erase(i, j)` |`iterator` |No effect. | _Returns:_ + +Iterator `i`. +|`a.insert(t)` |`iterator` |Fails. | +|`a.insert(i, j)` |`void` |Fails. | +|`a.insert(p, t)` |`iterator` |Fails. | +|`a.insert(p, i, j)` |`void` |Fails. | +|=== + +.Sequence Semantics +|=== +|Expression |Return type |Sequence semantics |Conditions +|`a.clear()` |`void` |Removes all nested elements. | _Effects:_ + +`a.empty() == true` +|`a.erase(p)` |`iterator` |Removes a given element. | _Requires:_ + +`T` shall be `MoveAssignable`. + +_Effects:_ + +Erases the element pointed to by `p`. + +_Returns:_ + +Iterator pointing to the element immediately following `p` prior to the element being erased, or `a.end()` if no such element exists. +|`a.erase(i, j)` |`iterator` |Removes all elements in range. | _Requires:_ + +`T` shall be `MoveAssignable`. + +_Effects:_ + +Erases the elements in the range _[`i`, `j`_). + +_Returns:_ + +Iterator pointing to the element pointed to by `j` prior to the element being erased, or `a.end()` if no such element exists. + +|`a.insert(t)` |`iterator` |Inserts element at end. | _Requires:_ + +`T` shall be `CopyInsertable` into `a`. + +_Effects:_ + +`a.insert(a.end(), t)` + +_Returns:_ + +Iterator `a.end()` +|`a.insert(i, j)` |`void` |Inserts all elements in range at end. | _Requires:_ + +`T` shall be `EmplaceConstructible` into `a` from `*i`. + +`i` and `j` are not iterators into `a`. + +_Effects:_ + +`a.insert(a.end(), i, j)` +|`a.insert(p, t)` |`iterator` |Inserts element before position. | _Requires:_ + +`T` shall be `CopyInsertable` into `a`. + +`p` is iterator into `a`. + +_Effects:_ + +Inserts a copy of `t` before position `p`. + +_Returns:_ + +Iterator `p`. +|`a.insert(p, i, j)` |`void` |Inserts all elements in range before position. | _Requires:_ + +`T` shall be `EmplaceConstructible` into `a` from `*i`. + +`p` is iterator into `a`. + +`i` and `j` are not iterators into `a`. + +_Effects:_ + +Inserts a copy of each element in the range _[`i`, `j`_) into `a` before position `p`. +|=== + +.Associative Semantics +|=== +|Expression |Return type |Associative semantics |Conditions +|`a.clear()` |`void` |Removes all nested elements. | _Effects:_ + +`a.empty() == true` +|`a.erase(p)` |`iterator` |Removes a given element. | _Effects:_ + +Erases the element pointed to by `p`. + +_Returns:_ + +Iterator pointing to the element immediately following `p` prior to the element being erased, or `a.end()` if no such element exists. +|`a.erase(i, j)` |`iterator` |Removes all elements in range. | _Effects:_ + +Erases the elements in the range _[`i`, `j`_). + +_Returns:_ + +Iterator pointing to the element pointed to by `j` prior to the element being erased, or `a.end()` if no such element exists. +|`a.insert(t)` |`iterator` |Inserts `t`.| _Requires:_ + +`T` is a `pair_type`. + +_Effects:_ + + +``` +p = std::find(a.begin(), a.end(), get<0>(t)) +a.insert(p, t) +``` + +|`a.insert(i, j)` |`void` |Inserts all elements in range _[`i`, `j`_) | _Requires:_ + +Each elements in range _[`i`, `j`_) is a `pair_type`. +|`a.insert(p, t)` |`iterator` |Inserts `t` with position `p` as hint. | _Requires:_ + +`T` is a `pair_type`. +|`a.insert(p, i, j)` |`void` |Inserts all elements in range _[`i`, `j`_) with position `p` as hint. | _Requires:_ + +Each elements in range _[`i`, `j`_) is a `pair_type`. +|=== + +== DynamicContainer + +A *DynamicContainer* is a [link dynamic-nested NestedContainer] that can also store heterogenous elements, one of which is a nullable element indicating the absence of a value. + +.Nullable Semantics +|=== +|Expression |Return type |Nullable semantics |Conditions +|`a.clear()` |`void` |No effect. | +|`a.erase(p)` |`iterator` |No effect. | _Returns:_ + +Iterator `p`. +|`a.erase(i, j)` |`iterator` |No effect. | _Returns:_ + +Iterator `i`. +|`a.insert(t)` |`iterator` |Changes stored type to SequenceContainer and inserts element. | _Requires:_ + +`T` shall be `CopyInsertable` into `a`. + +_Effects:_ + +`a` becomes a sequenced array containing a copy of `t`. + +_Returns:_ + +Iterator `a.end()`. +|`a.insert(i, j)` |`void` |Changes stored type to SequenceContainer and inserts all elements in range. | _Requires:_ + +`T` shall be `EmplaceConstructible` into `a`. + +_Effects:_ + +`a` becomes a sequenced array containing a copy of each element in the range _[`i`, `j`_). +|`a.insert(p, t)` |`iterator` |Fails. | +|`a.insert(p, i, j)` |`void` |Fails. | +|=== diff --git a/doc/dynamic/converter.qbk b/doc/modules/dynamic/pages/converter.adoc similarity index 74% rename from doc/dynamic/converter.qbk rename to doc/modules/dynamic/pages/converter.adoc index 8137ca2..473afe6 100644 --- a/doc/dynamic/converter.qbk +++ b/doc/modules/dynamic/pages/converter.adoc @@ -1,100 +1,120 @@ -[/ - Copyright (C) 2017 Bjorn Reese +// +// Copyright (C) 2017 Bjorn Reese +// +// 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). +// +// - 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). -] - -[section Converters] += Converters Converters are convenience functions for copying data between the dynamic variable and other containers. The general pattern is `OutputType dynamic::convert::into(InputType)`. The converter derives the `InputType` from the function parameter. The `OutputType` cannot be derived, so it must be specified explicitly as a template parameter. -``` +[source,cpp] +---- InputType input = /* input data */; auto output = dynamic::convert::into(input); -``` +---- or... -``` +[source,cpp] +---- InputType input = /* input data */; OutputType output = dynamic::convert::into(input); -``` + +---- An error is raised when converting incompatible types. The error is either passed as an exception, or as an `std::error_code` if specified as output parameter. -``` +[source,cpp] +---- + InputType input = /* input data */; std::error_code error; auto output = dynamic::convert::into(input, error); if (error) /* Handle errors */ -``` + +---- The converters for each container are located in separate header files. -[heading enum] +// enum +//FIXME: empty section! + +== std::vector -[heading std::vector] -[note `#include `] +NOTE: `#include ` -``` +[source,cpp] +---- // Convert std::vector into dynamic variable std::vector input = { 1, 2, 3, 4 }; auto output = dynamic::convert::into(input); assert(output.is()); assert(output.size == 4); -``` -``` +---- + +[source,cpp] +---- // Convert dynamic variable into std::vector dynamic::variable input = { 1, 2, 3, 4 }; auto output = dynamic::convert::into>(input); assert(output.size() == 4); -``` -[heading std::map] +---- + +== std::map [note `#include `] -``` +[source,cpp] +---- // Convert std::map into dynamic variable std::map input = { { "alpha", "hydrogen" }, { "bravo", "helium" } }; auto output = dynamic::convert::info(input); assert(output.is()); assert(output.size == 2); -``` -``` +---- + +[source,cpp] +---- // Convert dynamic variable into std::map dynamic::variable input = { { "alpha", "hydrogen" }, { "bravo", "helium" } }; auto output = dynamic::convert::info>(input); assert(output.size() == 2); -``` -[heading boost::any] +---- + +== boost::any -[note `#include ` +[NOTE] +==== +`#include ` -This creates a compile-time dependency on Boost.Any.] +This creates a compile-time dependency on Boost.Any. +==== -``` +[source,cpp] +---- // Convert boost::any into dynamic variable boost::any input = 1; auto output = dynamic::convert::info(input); assert(output.is()); -``` -[endsect] +---- diff --git a/doc/modules/dynamic/pages/design.adoc b/doc/modules/dynamic/pages/design.adoc new file mode 100644 index 0000000..3e32580 --- /dev/null +++ b/doc/modules/dynamic/pages/design.adoc @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017 Bjorn Reese +// +// 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) +// +/////////////////////////////////////////////////////////////////////////////// + +== Design Rationale + +This section describes the principles behind the design of `dynamic::variable`. + +=== Supported Types + +`dynamic::variable` only supports a pre-defined list of types. No custom types +are allowed. This restriction is imposed to define various relationships between +supported types such that `dynamic::variable` can meet the requirements for the +Container concept. + +The `dynamic::nullable` type indicates that the variable contains no value. +It serves the same purpose as `std::nullopt_t`. + +// Arithmetic + +// The many strings (locale not mandated, thus no implicit conversion between them) + +The `dynamic::array` type adheres to the `SequenceContainer` concept. + +The `dynamic::map` type adheres to the `AssociativeContainer` concept with a `mapped_type`. + +A variable can only change its type via construction, assignment, or swapping. +For instance, the `clear()` member function resets the content of a variable, +but retains the type. Assigning an unsupported type to a variable results in a +compile-time error. + +//* Operations with incompatible supported types are detected at run-time. + +//Emulates operations of underlying types where possible, except: +// 1. Comparison (see below) +// 2. Throws exceptions instead of generating compiler errors for some operations. + +// is() versus is_boolean(): meta-programming +// pre-condition: assert(is()); + +// Implicit versus explicit conversion + +=== Container Concept + +`dynamic::variable` adheres to the Container concept. + +When container operations are performed on a variable, then a nullable value is +considered an array of size 0, a boolean, integer, floating-point, or string +value is considered an array of size 1, and array and map are containers whose +size is the number of contained elements. + +=== Comparison + +// http://en.cppreference.com/w/cpp/utility/optional/operator_cmp +// http://en.cppreference.com/w/cpp/utility/variant/operator_cmp + +The null value has a special meaning in comparisons. + +The rules for comparisons follows those of http://en.cppreference.com/w/cpp/utility/optional[`std::optional`], +albeit with a few deviations. + +* `dynamic::null` only equals `dynamic::null`, and is less than any other type. +* Comparison between arithmetic types follows C++ rules, with the + exception of comparison between signed and unsigned integers. +* Comparison between unrelated supported types use this relationship: + null > arithmetic > string > wstring > u16string > u32string > array > map + +//Comparison + +//Comparison: Works similar to std::nullopt: +// 1. variable::null only equals variable::null. +// 2. variable::null is smaller than objects of other types. + +//signed/unsigned: no warning (do not know how to enable) +//scalar vs string: works to make std::adjacent_find() work +// null > arithmetic > string > array > map + +=== Traversal + +// key_iterator +// std::pair +// const + +// Visitor + +=== Customization + +`dynamic::variable` is an alias for `dynamic::basic_variable`. + +`dynamic::basic_variable` can be customized with an allocator. The allocator is +passed to all its string types, as well as `array_type` and `map_type`. diff --git a/doc/modules/dynamic/pages/dynamic.adoc b/doc/modules/dynamic/pages/dynamic.adoc new file mode 100644 index 0000000..be2ebe2 --- /dev/null +++ b/doc/modules/dynamic/pages/dynamic.adoc @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017 Bjorn Reese +// +// 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) +// +/////////////////////////////////////////////////////////////////////////////// + +:doctype: book +:toc: left +:toclevels: 2 +:source-highlighter: pygments + += Trial.Dynamic +Bjørn Reese + +== Overview + +Trial.Dynamic is a pass:[C++11] header-only library with a pass:[C++] dynamic +variable similar to the Javascript dynamic variable. + +* `dynamic::variable` is a https://en.wikipedia.org/wiki/Tagged_union[tagged union] + whose type and value can change dynamically during program execution. + +* `dynamic::variable` supports http://en.cppreference.com/w/cpp/language/types[fundamental data types]footnote:[Sign-less + characters (`char`, `wchar_t`, `char16_t`, and `char32_t`) are not supported + directly, but only indirectly via strings.]footnote:[`void` is not a + http://stepanovpapers.com/DeSt98.pdf[regular type], + so it has been replaced with the `dynamic::nullable` type and the + `dynamic::null` value.] and strings. + +* `dynamic::variable` supports arrays and associative arrays and can therefore + act as a heterogenous tree data structure. + +* `dynamic::variable` meets the requirements of http://en.cppreference.com/w/cpp/concept/Container[Container] + and therefore works with standard pass:[C++] algorithms. + +The resemblance between `dynamic::variable` and http://en.cppreference.com/w/cpp/utility/variant[`std::variant`] +is obvious, but there are also notable differences. While `std::variant` supports +custom types, `dynamic::variable` is restricted to the above-mentioned data types +and containers. This restriction enables `dynamic::variable` to adhere to the +Container concept, and thus to have a richer interface that works with algorithms. + +`dynamic::variable` is intended for tasks like handling configuration data, parse +tree for network protocols, and protocol serialization. + +== Introduction by Example + +All examples throughout this documentation assumes the following prologue: + +[source,cpp] +---- +#include +namespace dynamic = trial::dynamic; +---- + +A http://en.cppreference.com/w/cpp/language/default_initialization[default-initialized] +`dynamic::variable` has no value. No value is represented as the `dynamic::nullable` +type. +[source] +---- +// Create an empty variable +dynamic::variable data; +assert(data.is()); +assert(data == dynamic::null); +---- +We can morph the above variable into a boolean type simply by assigning a boolean +value to it. +[source,cpp] +---- +// Change into boolean +data = true; +assert(data.is()); +assert(data == true); +---- +We can even morph it into an array by assigning an +http://en.cppreference.com/w/cpp/utility/initializer_list[initializer list] to it. +[source,cpp] +---- +// Change into array +data = { 1, 20, 300 }; +assert(data.is()); +assert(data.size() == 3); +assert(data[0] == 1); +assert(data[1] == 20); +assert(data[2] == 300); +---- +Now that the variable contains an array of integers, it makes sense to calculate +its sum. +[source] +---- +#include + +// Calculate sum +auto sum = std::accumulate(data.begin(), data.end(), dynamic::variable()); +assert(sum == 1 + 20 + 300); +---- +Finally we morph our variable into an associative array. +[source,cpp] +---- + +// Change into associative array +data = + { + { "alpha", null }, // nullable + { "bravo", true }, // boolean + { "charlie", 2 }, // integer + { "delta", 3.0 }, // floating-point number + { "echo", "blue" }, // string + { "foxtrot", { 1, 2, 3 } }, // nested array of integers + { "golf", { { "level", 2 } } } // nested associative array + }; +assert(data.is()); +assert(data.size() == 7); + +---- diff --git a/doc/modules/dynamic/pages/function.adoc b/doc/modules/dynamic/pages/function.adoc new file mode 100644 index 0000000..f6b0c3e --- /dev/null +++ b/doc/modules/dynamic/pages/function.adoc @@ -0,0 +1,174 @@ +// +// Copyright (C) 2017 Bjorn Reese +// +// 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). +// + += Functions + +== Construction + +// FIXME: Nullable type can change type on insert() or operator+= +// FIXME: Assignment operator + +Dynamic variables can be created in various ways. + +|=== +|Expression |Semantics |Conditions +|`V v` |Creates a nullable variable. | _Effects:_ + +http://en.cppreference.com/w/cpp/language/default_initialization[Default-initializes] +variable to the nullable type. + +_Example:_ + +```` +// Default-initializes to nullable type +dynamic::variable data; +assert(data.is()); +```` + +|`V v(t)` +`V v = t` | Creates a variable of given supported type. | _Requires:_ + +`T` shall be a supported type. + +_Effects:_ + +http://en.cppreference.com/w/cpp/language/direct_initialization[Direct-initializes] variable to value. + +_Example:_ + +```` +// Direct-initializes to integer type +dynamic::variable data(1); +assert(data.is()); +```` + +|`V v(u)` +`V v = u` |Creates a copy of another dynamic variable. | _Effects:_ + +http://en.cppreference.com/w/cpp/language/copy_initialization[Copy-initializes] +variable with the type and value from another dynamic variable. + +_Example:_ + +``` +// Copy-initializes from other value +dynamic::variable other(3.0); +dynamic::variable data(other); +assert(data.is()); +``` + +|`V v { u1, u2, u3, ... }` +`V v = { u1, u2, u3, ... }` |Creates an array from initializer list. | _Effects:_ + +http://en.cppreference.com/w/cpp/language/list_initialization[List-initializes] variable as array from initializer list. + +If the initializer list consists of a sequence of pairs, then the values will be stored as an associative array. Otherwise the values will be stored as an array. Nested lists are allowed. + +_Example:_ + +``` +// List-initializes heterogenous array from initializer list +dynamic::variable data { null, true, 2, 3.0, "alpha" }; +assert(data.is()); +// List-initializes nested integer array from initializer list +dynamic::variable data { 1, 2, { 3, 4 }, 5 }; +assert(data.is()); +``` + +| `V v { {u1, u2}, {u3, u4}, ... }` +`V v = { {u1, u2}, {u3, u4}, ... }` |Creates an associative array from initializer list. | _Requires:_ +Initializer list shall be a sequence of pairs.footnote:[A pair is a dynamic variable containing an array with exactly two dynamic variables.]. + +_Effects:_ + +http://en.cppreference.com/w/cpp/language/list_initialization[List-initializes] variable as associative array from initializer list. + +If the initializer list consists of a sequence of pairs, then the values will be stored as an associative array. Otherwise the values will be stored as an array. Nested lists are allowed. + +_Example:_ + + +``` +// List-initializes associative array from initializer list +dynamic::variable data { { "alpha", 1 }, { "bravo", 2 } }; +assert(data.is()); +``` +| |Creates an array from factory method. | +| |Creates an associative array from factory method. | + +|=== + +// FIXME: Move to table above +== Factory Initialization + +Arrays and associated arrays can also be explicitly created with factories. + +[source,cpp] +---- +// Example: List-initializes empty array from factory +auto data = dynamic::array::make(); +assert(data.is()); +assert(data.size() == 0); + +---- +[source,cpp] +---- +// Example: List-initializes array of 42 null values from factory +auto data = dynamic::array::repeat(42, null); +assert(data.is()); +assert(data.size() == 42); + +---- + +== Capacity + +|=== +|Function |Returns |Description +|`v.empty()` |`bool` |Returns true if the variable is empty or nullable; return false otherwise. +|`v.size()` |`size_type` | +|`v.max_size()` |`size_type` | +|=== + +== Acessor + +The `assume_value()` functions have a narrow contract. The requested type `T` must match the stored type exactly. It is undefined behavior if the pre-condition `same()` is false. + +|=== +|Function |Returns |Description +|`v.value()` |`T` |Returns stored element by value. +|`v.operator T()` |`T` |Conversion operator. + +Same operation as `v.value()`. +|`v.assume_value()` |`T&` |Returns stored element by reference. +|`v.assume_value() const` |`const T&` | +|`v.operator[u]` |`const variable&` |Looks up element by key `u`. +|`v.operator[n]` |`const variable&` |Looks up element by integer position `n`. + +|=== + +.Modifiers +|=== +|Function |Returns |Description +|`v.clear()` |`void` |Erases all nested elements.]] +|`v.erase(p)` |`iterator` |Erases element at position `p`.]] +|`v.erase(i, j)` |`iterator` |Erases all elements in range _[`i`, `j`_). +|`v.insert(t)` |`iterator` |Inserts element `t`. +|`v.insert(p, t)` |`iterator` |Inserts element `t` at position `p`. +|`v.insert(i, j)` |`void` |Inserts elements in range _[`i`, `j`_). +|`v.insert(p, i, j)` |`void` |Inserts elements in range _[`i`, `j`_) at position `p`. +|`v.swap(u)`| | +|=== + +== Operators + +// FIXME: operator+ +// FIXME: operator+ + +== Iterators + +// FIXME: begin() end() etc. diff --git a/doc/modules/dynamic/pages/guide.adoc b/doc/modules/dynamic/pages/guide.adoc new file mode 100644 index 0000000..2fe9099 --- /dev/null +++ b/doc/modules/dynamic/pages/guide.adoc @@ -0,0 +1,17 @@ +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// + += User Guide + +include::type.adoc[] +include::function.adoc[] +include::algorithm.adoc[] +include::converter.adoc[] +include::concept.adoc[] + +//included for backup diff --git a/doc/dynamic/rationale.qbk b/doc/modules/dynamic/pages/rationale.adoc similarity index 66% rename from doc/dynamic/rationale.qbk rename to doc/modules/dynamic/pages/rationale.adoc index 92d1897..fb20cd7 100644 --- a/doc/dynamic/rationale.qbk +++ b/doc/modules/dynamic/pages/rationale.adoc @@ -1,36 +1,39 @@ -[/ - Copyright (C) 2017 Bjorn Reese +// +// Copyright (C) 2017 Bjorn Reese - 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). -] +// 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). +// +// -[section Design Rationale] += Design Rationale This section describes the rationale behind the design of the dynamic variable. -[heading Supported Types] +=== Supported Types The dynamic variable only supports a pre-defined list of types. No custom types are allowed. This restriction is imposed to define various relationships between supported types such that the dynamic variable can meet the requirements for the Container concept. -[table -[[Tag type] [Description]] -[[`dynamic::nullable`] [Indicates the absence of data. It serves a similar purpose as [@http://en.cppreference.com/w/cpp/utility/optional/nullopt_t `std::nullopt_t`.]]] -[[[pre `dynamic::boolean` +|=== +|Tag type |Description +|`dynamic::nullable` |Indicates the absence of data. It serves a similar purpose as http://en.cppreference.com/w/cpp/utility/optional/nullopt_t[`std::nullopt_t`] +|`dynamic::boolean` `dynamic::integer` -`dynamic::number`]] [Indicates a single value of an [@http://en.cppreference.com/w/c/language/arithmetic_types arithmetic type]. Arithmetic operations on dynamic variables will use the normal C++ arithmetic operations.]] -[[[pre `dynamic::string` +`dynamic::number` |Indicates a single value of an http://en.cppreference.com/w/c/language/arithmetic_types[arithmetic type]. Arithmetic operations on dynamic variables will use the normal C++ arithmetic operations. +|`dynamic::string` `dynamic::wstring` `dynamic::u16string` -`dynamic::u32string`]] [Indicates a sequence of characters. Single characters[footnote Except `signed char` and `unsigned char` which are considered small integers.] cannot be stored directly, but have to be added as strings. +`dynamic::u32string` |Indicates a sequence of characters. Single charactersfootnote:[Except `signed char` and `unsigned char` which are considered small integers.] cannot be stored directly, but have to be added as strings. -The string types are considered incompatible types, so no comparison or conversion between them are supported.]] -[[`dynamic::array`] [Indicates a sequence of nested dynamic variables. Adheres to the `SequenceContainer` concept.]] -[[`dynamic::map`] [Indicates an ordered sequence of key-value pairs, where both key and value are dynamic variables. +The string types are considered incompatible types, so no comparison or conversion between them are supported. +|`dynamic::array` |Indicates a sequence of nested dynamic variables. Adheres to the `SequenceContainer` concept. +`dynamic::map` |Indicates an ordered sequence of key-value pairs, where both key and value are dynamic variables. +|=== + + +Adheres to the `AssociativeContainer` concept with a `mapped_type`. -Adheres to the `AssociativeContainer` concept with a `mapped_type`.]] -] A variable can only change its type via construction, assignment, or swapping. For instance, the `clear()` member function resets the content of a variable, but retains the type. An exemption is when the variable is nullable, in which case insertion can also change the type via arithmetic operations. @@ -38,31 +41,32 @@ As the type can change dynamically during program execution, operations between Assigning an unsupported type to a variable results in a compile-time error. -[heading Concepts] +=== Concepts -The dynamic variable meets the requirements of the [@http://en.cppreference.com/w/cpp/concept/Container Container], [@http://en.cppreference.com/w/cpp/concept/ReversibleContainer ReversibleContainer], and [link dynamic-container DynamicContainer] concepts. +The dynamic variable meets the requirements of the http://en.cppreference.com/w/cpp/concept/Container[Container], http://en.cppreference.com/w/cpp/concept/ReversibleContainer[ReversibleContainer], and xref:concept.adoc#dynamic-container[DynamicContainer] concepts. Meeting the requirements of the Container concept means that the dynamic variable can be used together with C++ algorithms. In order to meet the Container concept, each supported type must be considered a container. Singular types like the fundamental data types and strings are considered containers with a single element, except nullable which has no elements. The singular types can be used both as a value and as a container. The container size of each supported type is listed in the table below. -[table -[[Tag type] [Container size]] -[[`dynamic::nullable`] [0]] -[[[pre `dynamic::boolean` + +|=== +|Tag type | Container size +|`dynamic::nullable` |0 +|`dynamic::boolean` `dynamic::integer` `dynamic::number` `dynamic::string` `dynamic::wstring` `dynamic::u16string` -`dynamic::u32string`]] [1]] -[[`dynamic::array`] [`dynamic::variable::array_type::size()`]] -[[`dynamic::map`] [`dynamic::variable::map_type::size()`]] -] +`dynamic::u32string` |1 +|`dynamic::array` |`dynamic::variable::array_type::size()` +|`dynamic::map` |`dynamic::variable::map_type::size()` +|=== -The DynamicContainer concept has been constructed as a common set of insertion and erasure operations that maps to the [@http://en.cppreference.com/w/cpp/concept/SequenceContainer SequenceContainer] and [@http://en.cppreference.com/w/cpp/concept/AssociativeContainer AssociativeContainer] concepts. +The DynamicContainer concept has been constructed as a common set of insertion and erasure operations that maps to the http://en.cppreference.com/w/cpp/concept/SequenceContainer[SequenceContainer] and http://en.cppreference.com/w/cpp/concept/AssociativeContainer[AssociativeContainer] concepts. -The dynamic variable does not meet the requirements of the [@http://en.cppreference.com/w/cpp/concept/AllocatorAwareContainer AllocatorAwareContainer] concept. +The dynamic variable does not meet the requirements of the http://en.cppreference.com/w/cpp/concept/AllocatorAwareContainer[AllocatorAwareContainer] concept. -[heading Type Checks] +=== Type Checks The stored type of a variable can be queried in different ways. @@ -70,27 +74,27 @@ The stored type of a variable can be queried in different ways. * Query by enumeration is done with the `code()` and `symbol()` member functions. These are intended for dispatching based on the stored type. The use of enumeration enables the compiler to warn against missing cases in switch statements. * Visitation via the `dynamic::visit` algorithm. -[heading Construction] +=== Construction -[/ FIXME: Initializer-lists ] -[/ FIXME: Factories ] +// FIXME: Initializer-lists +// FIXME: Factories -[heading Comparison] +=== Comparison Comparison takes both the type and value into account. Some types, such as arithmetic types, are directly comparable, and they will be compared using the normal C++ rules. An exemption is comparison between signed and unsigned integers, which does not trigger compiler warnings nor raises run-time errors. When supported types are not value-comparable, a type ordering is imposed. Nullable types always compares less than other types. Apart from the nullable type, the ordering between the remaining type has been chosen arbitrarily. The reason for type ordering is to ensure that any combination of values can be compared. This is needed because `dynamic::variable::map_type` uses `dynamic::variable` as the key, so the less-than operator must work. It is also useful for algorithms with predicates that use relational comparison. -Strings of different types are not value-comparable, which is in accordance with normal C++ rules,[footnote Despite attempts to make different string types directly comparable, such as [@http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2012/n3398.html N3398].] so they will be compared using their types. The ordering between string types is arbitrary. +Strings of different types are not value-comparable, which is in accordance with normal C++ rules,footnote:[Despite attempts to make different string types directly comparable, such as http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2012/n3398.html[N3398].] so they will be compared using their types. The ordering between string types is arbitrary. Comparison against unsupported types results in compiler errors. -[heading Traversal] +=== Traversal The dynamic variable supports container types, so it must be possible to traverse the content of these containers. There are two ways to traverse a dynamic variable. -# Adherence to the Container concept means that the dynamic variable can traversed by iterators. -# Visitation can be done using the `dynamic::visit` algorithm. The array and associative arrays can be iterated over for recursive visitation. +. Adherence to the Container concept means that the dynamic variable can traversed by iterators. +. Visitation can be done using the `dynamic::visit` algorithm. The array and associative arrays can be iterated over for recursive visitation. Special attention is needed for iterator dereferencing, because it returns a reference to the embedded value. This return type must be the same for any value. The iterators use `dynamic::variable` as the return type. The associative array stands out because the key-value pair is stored as an `std::pair`, not as a `dynamic::variable`. The solution is that dereference returns a reference to the value, not the entire key-value pair. The iterator is therefore a value iterator. @@ -100,7 +104,7 @@ The iterator has explicit methods to obtain both the key and the value by refere A key iterator also exists. It works like the value iterator but the dereferencing operator returns the key rather than the value. Only the associative array has keys, so the key of other supported types is their index. The key iterator is const, because the key cannot be changed. Changing the key can only be done by erasing the old key and inserting the new key. -[heading Customization] +=== Customization The only customization point in the dynamic variable is allocator support. @@ -108,6 +112,6 @@ A custom allocator can be specified as a template parameter for the `dynamic::ba `dynamic::variable` is a convenience alias for `dynamic::basic_variable>`. -[/ FIXME: Why not custom array or map? ] +// FIXME: Why not custom array or map? + -[endsect] diff --git a/doc/modules/dynamic/pages/reference.adoc b/doc/modules/dynamic/pages/reference.adoc new file mode 100644 index 0000000..b9f0c49 --- /dev/null +++ b/doc/modules/dynamic/pages/reference.adoc @@ -0,0 +1,41 @@ += Dynamic Reference + + +== + +[source,cpp] +---- +include::../../../../src/dynamic/visit.hpp[] +---- + +== + +[source,cpp] +---- +include::../../../../src/dynamic/error.hpp[] + +---- + +== + +[source,cpp] +---- +include::../../../../src/dynamic/functional.hpp[] + +---- + +== + +[source,cpp] +---- +include::../../../../src/dynamic/token.hpp[] +---- + +== + + +[source,cpp] +---- +include::../../../../src/dynamic/variable.hpp[] +---- + diff --git a/doc/dynamic/tutorial.qbk b/doc/modules/dynamic/pages/tutorial.adoc similarity index 80% rename from doc/dynamic/tutorial.qbk rename to doc/modules/dynamic/pages/tutorial.adoc index 31ffa4f..e47ed7f 100644 --- a/doc/dynamic/tutorial.qbk +++ b/doc/modules/dynamic/pages/tutorial.adoc @@ -1,21 +1,22 @@ -[/ - Copyright (C) 2017 Bjorn Reese +// +// Copyright (C) 2017 Bjorn Reese - 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). -] +// 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). +// -[section Tutorial] += Tutorial All examples throughout this documentation assumes the following prologue: -``` +[source,cpp] +---- #include namespace dynamic = trial::dynamic; -``` +---- -[section Shapeshifter] +== Shapeshifter A default-initialized `dynamic::variable` has no value. No value is represented with the `dynamic::nullable` type. @@ -24,22 +25,29 @@ We can query if the variable has a certain type with the [memberref trial::dynamic::basic_variable::is `dynamic::variable::is()`] member function. We can also examine if the variable has no value by comparing it with `dynamic::null`. -``` + +[source,cpp] +---- // Create an empty variable dynamic::variable data; assert(data.is()); assert(data == dynamic::null); -``` +---- If we assign a boolean value to the above variable then the variable changes into a boolean type. -``` + +[source,cpp] +---- // Change into boolean data = true; assert(data.is()); assert(data == true); -``` +---- + We can even change it into an array by assigning an initializer list to it. -``` + +[source,cpp] +---- // Change into array data = { 1, 20, 300 }; assert(data.is()); @@ -47,9 +55,11 @@ assert(data.size() == 3); assert(data[0] == 1); assert(data[1] == 20); assert(data[2] == 300); -``` +---- Finally, we will change our variable into an associative array. -``` + +[source,cpp] +---- // Change into associative array data = { @@ -63,20 +73,23 @@ data = }; assert(data.is()); assert(data.size() == 7); -``` -[endsect] +---- -[section Algorithms] -[heading Accumulation] +== Algorithms + +=== Accumulation Suppose we have a dynamic variable that contains an array of integers. -``` + +[source,cpp] +---- dynamic::variable data = { 1, 20, 300 }; -``` +---- + We can calculate the sum of this array with the -[@http://en.cppreference.com/w/cpp/algorithm/accumulate `std::accumulate()`] +http://en.cppreference.com/w/cpp/algorithm/accumulate[`std::accumulate()`] algorithm. As `std::accumulate()` works on homogeneous types, the initial value must be a @@ -84,15 +97,15 @@ As `std::accumulate()` works on homogeneous types, the initial value must be a example below we simply start with a empty dynamic variable. The empty dynamic variable behaves as a zero value when used in integer additions, so the resulting dynamic variable will contain an integer type. -``` + +[source,cpp] +---- #include // Calculate sum auto sum = std::accumulate(data.begin(), data.end(), dynamic::variable()); assert(sum.is()); assert(sum == 1 + 20 + 300); -``` +---- -[endsect] -[endsect] diff --git a/doc/modules/dynamic/pages/type.adoc b/doc/modules/dynamic/pages/type.adoc new file mode 100644 index 0000000..60749b3 --- /dev/null +++ b/doc/modules/dynamic/pages/type.adoc @@ -0,0 +1,181 @@ +// +// Copyright (C) 2017 Bjorn Reese +// +// 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). +// + +// FIXME: references + +== Types + +`dynamic::variable` supports a pre-defined list of fundamental data types, strings, and containers. +These are generally referred to as the _supported types_. +Each type also belongs to a type category that is represented by a tag type. + +.Supported types and tags +|=== +|Stored type |Tag type |Description +|`dynamic::nullable` | `dynamic::nullable` |No value. +|`bool` | `dynamic::boolean` |Boolean value. +|`signed char` | `dynamic::integer` |Very short signed integer. +|`signed short int` |`dynamic::integer` |Short signed integer. +|`signed int` |`dynamic::integer` |Signed integer. +|`signed long int` |`dynamic::integer` |Long signed integer. +|`signed long long int` |`dynamic::integer` |Very long signed integer. +|`unsigned char` |`dynamic::integer` |Very short unsigned integer. +|`unsigned short int` |`dynamic::integer` |Short unsigned integer. +|`unsigned int` |`dynamic::integer` |Unsigned integer. +|`unsigned long int` |`dynamic::integer` |Long unsigned integer. +|`unsigned long long int` |`dynamic::integer` |Very long unsigned integer. +|`float` |`dynamic::real` |Short floating-point number. +|`double` |`dynamic::real` |Floating-point number. +|`long double` |`dynamic::real` |Long floating-point number. +| dynamic::string_type` | `dynamic::string` |Narrow-character string. Same as `std::string` by default. +| dynamic::wstring_type` | `dynamic::wstring` |Wide-character string. Same as `std::wstring` by default. +| `dynamic::u16string_type` | `dynamic::u16string` |UTF-16 character string. Same as `std::u16string` by default. +| `dynamic::u32string_type` | `dynamic::u32string` |UTF-32 character string. Same as `std::u32string` by default. +| `dynamic::array_type` | `dynamic::array` |Sequence of zero or more `dynamic::variable`s. +| `dynamic::map_type` | `dynamic::map` |Ordered sequence of zero or more key-value pairs sorted by the key. Both key and value are `dynamic::variable`. +|=== + +== Type Checking + +The current type of a variable can either be queried by type or tag. +It is also possible to obtain an enumerator that identifies the current type. + +Query-by-type is done with the http://breese.github.io/trial/protocol/trial/dynamic/basic_variable.html#idm9983-bb[`same()`] function, which returns true if the current value is stored as type `T`. + +[source,cpp] +---- +dynamic::variable data = 3.0; +assert(data.same()); // Value is stored as a double. +assert(!data.same()); // Valus is not stored as a float. +---- + +Query-by-tag is done with the http://breese.github.io/trial/protocol/trial/dynamic/basic_variable.html#idm9962-bb[`is()`] function, which returns true if the current value is stored as a type belonging to the category `T`. +`T` can be a tag or a type. In the latter case the associated tag is looked up, and the variable is queried using this tag. + +[source,cpp] +---- +dynamic::variable data = 3.0; +assert(data.is()); // Value is stored as a floating-point number. +assert(data.is()); // Query using the dynamic::real tag. +assert(data.is()); // Query using the dynamic::real tag. +---- + +Notice that any supported floating-point type can be used to query for the tag. + +Query-by-enumeration is done with the http://breese.github.io/trial/protocol/trial/dynamic/basic_variable.html#idm9994-bb[`code()`] or http://breese.github.io/trial/protocol/trial/dynamic/basic_variable.html#idm10113-bb[`symbol()`] functions. These returns an enumerator that indicates the type. The enumerator is suitable for `switch` statements. + +[source,cpp] +---- +switch (data.symbol()) +{ +case dynamic::symbol::integer: + break; // Do integer stuff +case dynamic::symbol::real: + break; // Do floating-point number stuff +default: + break; // Do other stuff +} +---- + +.Codes and symbols +|=== +|Stored type |Code |Symbol +|`dynamic::nullable` | `dynamic::code::null` | `dynamic::symbol::null` +|`bool` | `dynamic::code::boolean` | `dynamic::symbol::boolean` +|`signed char` | `dynamic::code::signed_char` | `dynamic::symbol::integer` +|`signed short int` | `dynamic::code::signed_short_integer` | `dynamic::symbol::integer` +|`signed int` | `dynamic::code::signed_integer` | `dynamic::symbol::integer` +|`signed long int` | `dynamic::code::signed_long_integer` | `dynamic::symbol::integer` +|`signed long long int` |`dynamic::code::signed_long_long_integer` | `dynamic::symbol::integer` +|`unsigned char` |`dynamic::code::unsigned_char` |`dynamic::symbol::integer` +|`unsigned short int` |`dynamic::code::unsigned_short_integer` |`dynamic::symbol::integer` +|`unsigned int` |`dynamic::code::unsigned_integer` |`dynamic::symbol::integer` +|`unsigned long int` |`dynamic::code::unsigned_long_integer` |`dynamic::symbol::integer` +|`unsigned long long int`| `dynamic::code::unsigned_long_long_integer` | `dynamic::symbol::integer` +|`float` |`dynamic::code::real` |`dynamic::symbol::real` +|`double` |`dynamic::code::long_real` | `dynamic::symbol::real` +|`long double`| `dynamic::code::long_long_real` | `dynamic::symbol::real` +|`dynamic::string_type` | `dynamic::code::string` |`dynamic::symbol::string` +|`dynamic::wstring_type` | `dynamic::code::wstring` | `dynamic::symbol::wstring` +|`dynamic::u16string_type` | `dynamic::code::u16string` | `dynamic::symbol::u16string` +|`dynamic::u32string_type` | `dynamic::code::u32string` | `dynamic::symbol::u32string` +|`dynamic::array_type` | `dynamic::code::array` | `dynamic::symbol::array` +|`dynamic::map_type` | `dynamic::code::map` | `dynamic::symbol::map` +|=== + +=== Comparison + +A dynamic variable can be compared against another dynamic variable, or against a supported type. Supported types are grouped into comparison categories as shown below. Comparison against unsupported types results in a compile-time error. + +|=== +|Comparison category |Tag type |Rank +|Nullable | http://breese.github.io/trial/protocol/trial_protocol/dynamic_variable/user_guide.html[`dynamic::nullable`] |0 +|Arithmetic | http://breese.github.io/trial/protocol/trial/dynamic/boolean.html[`dynamic::boolean`] +http://breese.github.io/trial/protocol/trial/dynamic/integer.html[`dynamic::integer`] +http://breese.github.io/trial/protocol/trial/dynamic/real.html[`dynamic::real`] |1 +|Narrow string |http://breese.github.io/trial/protocol/trial/dynamic/string.html[`dynamic::string`] |2 +|Wide string | http://breese.github.io/trial/protocol/trial/dynamic/wstring.html[`dynamic::wstring`] |3 +|UTF-16 string | http://breese.github.io/trial/protocol/trial/dynamic/u16string.html[`dynamic::u16string`] |4 +|UTF-32 string | http://breese.github.io/trial/protocol/trial/dynamic/u32string.html[`dynamic::u32string`] |5 +|Sequenced array | http://breese.github.io/trial/protocol/header/boost/libs/trial_protocol/include/trial/dynamic/variable_hpp.html#trial.dynamic.array[`dynamic::array`] |6 +|Associative array] | http://breese.github.io/trial/protocol/header/boost/libs/trial_protocol/include/trial/dynamic/variable_hpp.html#trial.dynamic.map[`dynamic::map`] |7 +|=== + +Equality operations first check the argument types. If the argument types belong to different comparison categories, then they are unequal. Otherwise their values are compared according to the normal C++ rules, with the addition that null compares equal to null. + +[source,cpp] +---- +dynamic::variable first; +dynamic::variable second = 2; + +assert(first.is()); +assert(second.is()); + +assert(first != second); // Incompatible types are unequal +---- + +Relative operations first check the argument types. If the argument types belong to different comparison categories, then their ranks are compared. The ranks are shown in the table above. For example, a nullable type is always less than other types, while an associative array is always greater than other types. Otherwise their values are compared according to the normal C++ rules. + +[source,cpp] +---- +dynamic::variable first; +dynamic::variable second = 2; + +assert(first.is()); +assert(second.is()); + +assert(first < second); // Null is less than integers +---- + +Sequenced arrays perform a pair-wise comparison of the elements. + +[source,cpp] +---- +dynamic::variable first = { 1, 20, 300 }; +dynamic::variable second = { 1, 20, 300 }; + +assert(first == second); +assert(first <= second); +assert(!(first < second)); +assert(first >= second); +assert(!(first > second)); +---- + +Associative arrays perform a pair-wise comparison of the key-value elements. + +[source,cpp] +---- +dynamic::variable first = { { "alpha", true } }; +dynamic::variable second = { { "alpha", true } }; + +assert(first == second); +assert(first <= second); +assert(!(first < second)); +assert(first >= second); +assert(!(first > second)); +---- diff --git a/doc/modules/home/pages/introduction.adoc b/doc/modules/home/pages/introduction.adoc new file mode 100644 index 0000000..e177c62 --- /dev/null +++ b/doc/modules/home/pages/introduction.adoc @@ -0,0 +1,45 @@ += Introduction + +[IMPORTANT] +==== +Trial.Protocol is not an official Boost library, Trial.Protocol is still work-in-progress +==== + +Trial.procol is a header only library.footnote:[Trial.Protocol serialization relies on Boost.Serialization, which is not header-only. Serialization is an optional feature.] for processing (parsing, manipulating, and generating) encoded data for network wire protocols. Trial.Protocol contains several interfaces for parsing and generating encoded data, as well as a heterogeneous tree data structure that can be used as a parse tree. + +Currently supported protocols.footnote:[Trial.Protocol only supports protocols that can be tokenized without using a schema.] are: + +. https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/json.html[JSON] +. BinToken + +== Levels of Abstraction + +Protocol processing can be done at any of three levels of abstraction: + +. Incremental processors transforms the data token by token. There are two types of incremental processing: (i) push processing where the processing is done automatically and each token causes a callback to be invoked, and (ii) pull processing where the user has to advance manually from one token to the next.footnote:[Pull processors resembles a https://en.cppreference.com/w/cpp/concept/ForwardIterator[ForwardIterator], albeit with an interface closer to the https://en.wikipedia.org/wiki/Iterator_pattern[Iterator pattern].]Incremental processing is also called stream processing. +. Serialization archives are used to transform directly between the protocol format and C++ data structures. The serialization archives do not go through an intermediate representation and can therefore perform faster and in less memory. The mapping between the protocol format and the C++ data structures can be specified both (i) intrusively by augmenting the C++ data structure with the mapping, and (ii) non-intrusively by specifying the mapping in separate function outside the C++ data structure. +. Tree processing.footnote:[Tree processing is similar to creating a https://en.wikipedia.org/wiki/Document_Object_Model[Document Object Model]] transforms the the entire encoded data into a generic tree structure which can then be examined and manipulated with tree operations. + +At each level of abstraction there are processors for both parsing and generating protocol formats. These are summarized below. + +|=== +| | Parser | Generator +| Incremental | The encoded input can be parsed token by token with an incremental parser. For each token we can query the current token type and value. | The encoded output can be generated token by token with an incremental generator. +| Serialization | The encoded input can be deserialized directly into arbitrary C++ data structures with an input archive. | Arbitrary C++ data structures can be serialized directly into encoded output with an output archive. +| Tree | The encoded input can be parsed into a dedicated parse tree. | The dedicated parse tree can be transformed into an encoded output. +|=== + +The protocol generators can write the encoded output to different types of buffers as long as an https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/core/adapter.html[adapter] exists for the buffer type. The correct header files must be included for this to work seamlessly. + + +[NOTE] +==== +For brevity all examples in this documentation assumes + +[source,cpp] +---- +using namespace trial:protocol; +---- +==== +// Serialization does not support pointers or inheritance/tracking +// Pointers/links can be implemented via http://www.w3.org/TR/json-ld/ diff --git a/doc/json/design.qbk b/doc/modules/json/pages/design.adoc similarity index 63% rename from doc/json/design.qbk rename to doc/modules/json/pages/design.adoc index 9982743..d221b84 100644 --- a/doc/json/design.qbk +++ b/doc/modules/json/pages/design.adoc @@ -1,20 +1,19 @@ -[/ - Copyright (C) 2015 Bjorn Reese +//// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +//// - 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). -] += Design Rationale -[#protocol.json.design] -[section Design Rationale] - -[heading Incremental] +=== Incremental The JSON parser is incremental because that is a versatile building-block for network wire protocols. Many other JSON parsers are restricted to the -[@http://en.wikipedia.org/wiki/Document_Object_Model Document Object Model], +http://en.wikipedia.org/wiki/Document_Object_Model[Document Object Model], wherein the entire JSON input is parsed into a parse tree before you can operate on it. If the JSON input should end up in your own C++ data structures, then the parse tree becomes an unnecessary intermediate step. In this case your program @@ -22,21 +21,21 @@ becomes both slower and consumes more memory. A JSON DOM can be created using the incremental parser and Boost.Serialization. -[/ incremental vs serialization vs sax] -[/ The omission of a DOM is simply a matter of prioritization] +// incremental vs serialization vs sax +// The omission of a DOM is simply a matter of prioritization -[heading Iterator] +=== Iterator -The ability of [link protocol.json.reader `json::reader`] to read the input one +The ability of https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/reader.html[`json::reader`] to read the input one token at the time makes it work like an iterator. The design is build around the more traditional Iterator design pattern as described in the Gang-of-Four book, instead of C++ iterators. -[heading Numbers] +=== Numbers JSON numbers are arithmetic - there is no distinction between integer and floating-point numbers. -C++ does make that distinction, so [link protocol.json.reader `json::reader`] +C++ does make that distinction, so https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/reader.html[`json::reader`] will identify numbers either as integers (identified by the `json::token::symbol::integer` token) if they consist solely of digits, or as floating-point numbers (identified by the `json::token::symbol::number` token) @@ -47,7 +46,6 @@ as either a C++ integer or floating-point number. This means that integer numbers that are too big to fit into a C++ integer type such as `std::intmax_t` can be read as a floating-point number. -[/ Limits] -[/ bigint/multiprecision?] +// Limits +// bigint/multiprecision -[endsect] diff --git a/doc/modules/json/pages/error.adoc b/doc/modules/json/pages/error.adoc new file mode 100644 index 0000000..3f04697 --- /dev/null +++ b/doc/modules/json/pages/error.adoc @@ -0,0 +1,73 @@ +//// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +//// + += Error + +The http://en.cppreference.com/w/cpp/header/system_error[``] +framework is used for error codes and exceptions. + +NOTE: Error codes and utilities are located in the `` header. + +===== Error codes + +Trial.Protocol defines its own `json::error_category` with associated error +enumerator constants. + +Normally, an `error_code` of the current error can be obtained via an `error()` +member function. + +[source,cpp] +---- +std::string input = "illegal"; +json::reader reader(input); + +assert(reader.symbol() == json::symbol::error); +assert(reader.error() == json::invalid_value); + +---- + +This conversion can also be done manually with `json::to_errc()` and +`json::make_error_code()`. + +[source,cpp] +---- +std::string input = "illegal"; +json::reader reader(input); + +assert(reader.symbol() == json::symbol::error); +assert(reader.code() == json::code::error_invalid_value); + +enum json::errc ec = json::to_errc(reader.code()); +assert(ec == json::invalid_value); + +auto error = json::make_error_code(ec); +assert(error == json::invalid_value); + +---- + + +The following error codes exists. + +.Error Codes +|=== +|`json::errc` |Description +|`unexpected_token` |An unexpected token is encountered in the input. +|`invalid_key` |An associative array key is not in a valid format. +|`invalid_value` |The content is not in a valid format. +|`incompatible_type` |Conversion between two incompatible types failed. +|`unbalanced_end_array` |Encountered an end array token without a corresponding begin array token. +|`unbalanced_end_object` |Encountered an end object token without a corresponding begin object token. +|`expected_end_array` |Encountered an end array token outside an array. +|`expected_end_object` |Encountered an end object token outside an associative array. +|=== + +=== Exception + +Conversion errors will result in `json::error` exceptions being thrown. +`json::error` inherits from `std::system_error` which contains +a `std::error_code`. diff --git a/doc/json/guide.qbk b/doc/modules/json/pages/guide.adoc similarity index 54% rename from doc/json/guide.qbk rename to doc/modules/json/pages/guide.adoc index 36b74b4..c19a750 100644 --- a/doc/json/guide.qbk +++ b/doc/modules/json/pages/guide.adoc @@ -1,18 +1,17 @@ -[/ - Copyright (C) 2017 Bjorn Reese +// +// Copyright (C) 2017 Bjorn Reese - 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). -] +// 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). +// -[#protocol.json.guide] -[section User Guide] += User Guide -__protocol__ can process JSON incrementally or via serialization. +Trial.protocol can process JSON incrementally or via serialization. Incremental processing handles JSON one token at the time, whereas serialization processes the entire JSON buffer in a single operation. -A [link protocol.json.token token] is a single element in the JSON grammar, +A https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/token.html[token] is a single element in the JSON grammar, such as a number, a string, the beginning or the end of an array. Incremental processing has a lower-level interface than serialization or @@ -29,26 +28,3 @@ Some examples of these use cases are: * We can build other parser types from an incremental parser, such as the push parser in the tutorials. -[section Incremental Processing] - -Incremental processing is a low-level API which regards JSON as a sequence of -tokens to be processed one by one. - -[include token.qbk] -[include error.qbk] -[include reader.qbk] -[include writer.qbk] - -[endsect] - -[section Serialization] - -[include iarchive.qbk] -[include oarchive.qbk] - -[endsect] - -[section Tree processing] -[endsect] - -[endsect] diff --git a/doc/json/iarchive.qbk b/doc/modules/json/pages/iarchive.adoc similarity index 67% rename from doc/json/iarchive.qbk rename to doc/modules/json/pages/iarchive.adoc index 477a817..4c7ccd6 100644 --- a/doc/json/iarchive.qbk +++ b/doc/modules/json/pages/iarchive.adoc @@ -1,13 +1,12 @@ -[/ - Copyright (C) 2015 Bjorn Reese +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// - 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). -] - -[#protocol.json.iarchive] -[section Input Archive] += Input Archive Deserialization of JSON input directly into C++ data structures is done with the help of Boost.Serialization. @@ -16,11 +15,12 @@ at once. The full parser transforms the entire input into a data structure. This is done via an input archive (called iarchive.) You can either use a generic tree data structure, such as dynamic-cpp, as the Document Object Model, or you can parse the input directly into your own data structures or the standard containers in C++. -[/ Intrusive vs non-intrusive] +// Intrusive vs non-intrusive -[heading Simple types] +=== Simple types -``` +[source,cpp] +---- #include #include @@ -30,13 +30,14 @@ json::iarchive archive(input); bool result; archive >> result; // Read the boolean value assert(result == true); -``` +---- -[heading Custom types] +=== Custom types -[heading STL types] +==== STL types -``` +[source,cpp] +---- #include #include @@ -46,10 +47,10 @@ json::iarchive archive(input); std::vector result; archive >> result; // Read the entire input into a vector assert(result == {1,2,3}); -``` -[heading Integration with reader] +---- + +=== Integration with reader -[/ Start with reader, then pass it to iarchive] +// Start with reader, then pass it to iarchive -[endsect] diff --git a/doc/modules/json/pages/json.adoc b/doc/modules/json/pages/json.adoc new file mode 100644 index 0000000..d5a75b7 --- /dev/null +++ b/doc/modules/json/pages/json.adoc @@ -0,0 +1,29 @@ +// +// Copyright (C) 2015 Bjorn Reese + +// 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). +// + += JSON + +http://json.org/[JSON] is a textual data format that encodes booleans, numbers, +and strings, as well as http://en.wikipedia.org/wiki/Array_data_structure[arrays] +and http://en.wikipedia.org/wiki/Associative_array[associative arrays] (called JSON objects). + + + +NOTE: Trial.Protocol supports http://tools.ietf.org/html/rfc7159[RFC 7159]. No http://json5.org/[JSON extensions] are supported. + +== Overview + +Trial.Protocol provides the following classes for JSON parsing and generation. + +|=== +| | Parser | Generator +|Incremental | https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/reader.html[json::reader] | https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/writer.html[json:writer] +|Serialization| https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/iarchive.html[json:iarchive] | https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/oarchive.html[json::oarchive] +|Tree | json::parse | json::format +|=== + diff --git a/doc/modules/json/pages/oarchive.adoc b/doc/modules/json/pages/oarchive.adoc new file mode 100644 index 0000000..5ecc72e --- /dev/null +++ b/doc/modules/json/pages/oarchive.adoc @@ -0,0 +1,20 @@ +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// + += Output Archive + +Formatting a full data struture is done with an output archive (called oarchive.) + +[source,cpp] +---- +dynamic::var data; +// Insert some values into data +std::ostringstream result; +json::oarchive archive(result) +archive << data; +---- diff --git a/doc/modules/json/pages/overview.adoc b/doc/modules/json/pages/overview.adoc new file mode 100644 index 0000000..0417a12 --- /dev/null +++ b/doc/modules/json/pages/overview.adoc @@ -0,0 +1,26 @@ +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// + +== Incremental Overview + +Trial.Protocol provides several ways of parsing and generating JSON as shown in +the table below. + +.Approaches to parsing and generating JSON +|=== +|Parser |Generator +|*Incremental* |The JSON input can be parsed token by token with an [incremental parser]. For each token we can obtain its current type and value. The JSON output can be generated token by token with an https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/guide.html#protocol[incremental generator]. +|*Serialization* |The JSON input can be deserialized directly into our C++ data structures with an https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/iarchive.html[input archive]. Notice that the input archive does not go through an intermediate http://en.wikipedia.org/wiki/Document_Object_Model[Document Object Model], so we can get better performance. Our C++ data structures can be serialized directly into a JSON output with an https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/oarchive.html[output archive]. + +The incremental parser and generator are the basic building-blocks. +Serialization combines these building-blocks with Boost.Serialization to offer +an API that is easier to use. + +The incremental parser can also be used to create other kinds of parser interfaces, +such as a push parser as we shall see in the tutorial. + diff --git a/doc/json/reader.qbk b/doc/modules/json/pages/reader.adoc similarity index 71% rename from doc/json/reader.qbk rename to doc/modules/json/pages/reader.adoc index 5ae96c1..039ef5c 100644 --- a/doc/json/reader.qbk +++ b/doc/modules/json/pages/reader.adoc @@ -1,34 +1,33 @@ -[/ - Copyright (C) 2015 Bjorn Reese +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// - 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). -] - -[#protocol.json.reader] -[section Reader] += Reader Reader is an incremental parser (also called a pull parser) that transforms the JSON input into a sequence of C++ tokens. This transformation is done piecemeal, which means that the reader will stay at the first token until explicitly instructed to parse the next token. -Based on the [@http://en.wikipedia.org/wiki/Iterator_pattern Iterator design -pattern], `reader` parses just enough of the input to identify a single token. +Based on the http://en.wikipedia.org/wiki/Iterator_pattern[Iterator design pattern], `reader` parses just enough of the input to identify a single token. The `reader` provides various accessors that can be used to examine or convert -the current [link protocol.json.token token]. - -[table Reader Accessors -[[Reader member function][Description]] -[[`token::code::value code()`][Returns the current token.]] -[[`token::symbol::value symbol()`][Returns the symbol of the current token.]] -[[`token::category::value category()`][Returns the category of the current token.]] -[[`size_type level()`][Returns the current level of nested containers. The levels starts with zero for the outmost level.]] -[[`error_code error()`][Returns the current error code.]] -[[`const view_type& literal()`][Returns a view of the raw input of the current value.]] -[[`T value()`][Returns the current value. The raw input is converted into the requested value type.]] -] +the current https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/token.html[token]. + +.Reader Accessors +|=== +|Reader member function |Description +|`token::code::value code()` |Returns the current token. +|`token::symbol::value symbol()` |Returns the symbol of the current token. +|`token::category::value category()` | Returns the category of the current token. +|`size_type level()` | Returns the current level of nested containers. The levels starts with zero for the outmost level. +|`error_code error()` |Returns the current error code. +|`const view_type& literal()` |Returns a view of the raw input of the current value.]] +|`T value()` |Returns the current value. The raw input is converted into the requested value type. +|=== No data is converted until explicitly requested with `reader::value()`. Notice that the return type for `reader::value()` must be specified as a @@ -36,18 +35,29 @@ template parameter. The return type can be a boolean, a number, or a string as described below. The requested type must match the current token as returned by `reader::type()`; otherwise a run-time error will be raised. -[note Requesting the value of an incompatible type will result in a run-time +NOTE: +==== +Requesting the value of an incompatible type will result in a run-time error. For example, attempting to read a string as an integer: -``` + +[source,cpp] +---- assert(reader.symbol() == json::symbol::string); int number = reader.value(); // Throws exception -```] -[note Requesting an unsupported type will result in a compile-time error. +---- +==== + +[NOTE] +==== +Requesting an unsupported type will result in a compile-time error. For example, attempting to read a user-defined struct: -``` + +[source,cpp] +---- struct dummy {}; dummy d = reader.value(); // Causes compilation error -```] +---- +==== The unconverted textual data of the current token can be obtained with `reader::literal()`. @@ -63,102 +73,120 @@ Whitespaces and separators are skipped. Errors in the input are identified with an error token, and the current error can be obtained with `reader::error()`. -[heading Boolean] +=== Boolean Boolean values are indicated by the `json::symbol::boolean` token. The value is requested by `reader::value()`. -``` + +[source,cpp] +---- std::string input = "true"; json::reader reader(input); assert(reader.symbol() == json::symbol::boolean); assert(reader.literal() == "true"); assert(reader.value()); -``` -[heading Number] +---- + +=== Number While JSON does not distinguish between integer and floating-point numbers, C++ -does make this distinction and therefore __protocol__ does too. +does make this distinction and therefore Trial.Protocol does too. However, integers can be read as floating-point numbers, and floating-point numbers can be read as integers. -[note Reading a floating-point number as an integer will round the number, so +NOTE: Reading a floating-point number as an integer will round the number, so it may result in loss of information.] Numbers are identified as integers if the consists of digits only. -``` +[source,cpp] +---- std::string input = "42"; json::reader reader(input); assert(reader.symbol() == json::symbol::integer); assert(reader.literal() == "42"); assert(reader.value() == 42); -``` + +---- + + Numbers are detected as floating-point if they contain a decimal point or an exponent. -``` + +[source,cpp] +---- std::string input = "3.1415"; json::reader reader(input); assert(reader.symbol() == json::symbol::number); assert(reader.literal() == "3.1415"); assert(reader.value() == 3.1415); -``` +---- + Integers can be read as floating-point numbers as well. -``` + +[source,cpp] +---- std::string input = "42"; json::reader reader(input); assert(reader.symbol() == json::symbol::integer); assert(reader.value() == 42.0); -``` +---- + Floating-point numbers can also be read as integers. The number will be rounded to the nearest integer. -``` + +[source,cpp] +---- std::string input = "3.1415"; json::reader reader(input); assert(reader.symbol() == json::symbol::number); assert(reader.value() == 3); -``` +---- Any kind of additional constraints have to be enforced by the application layer. For instance, if we have a protocol with a size field, then logically this field cannot be negative or a fraction, even if JSON numbers allow this. -[heading String] +=== String Strings are identified with the `json::symbol::string` token, and is converted into a UTF-8 encoded string with `reader::value()`. -``` +[source,cpp] +---- std::string input = "\"alpha\\n\""; json::reader reader(input); assert(reader.symbol() == json::symbol::string); assert(reader.literal() == "\"alpha\\n\""); assert(reader.value() == "alpha\n"); -``` +---- -[heading Null] +=== Null Null indicates the absence of a value, although it is encoded explicitly in the JSON format as the `null` literal string. -``` +[source,cpp] +---- std::string input = "null"; json::reader reader(input); assert(reader.symbol() == json::symbol::null); -``` +---- -[heading Array] +=== Array Arrays are delimited by a begin-token and an end-token. Array members are comma separated. Arrays can contain any JSON type, including a nested array or a nested associative array. -``` +[source,cpp] +---- std::string input = "[42]"; json::reader reader(input); @@ -172,11 +200,10 @@ assert(reader.value() == 42); reader.next(); assert(reader.symbol() == json::symbol::end_array); -``` +---- -[heading Associative array] +=== Associative array An associative array is called a JSON object, which as a first approximation can be thought of as a `std::map` in C++. -[endsect] diff --git a/doc/modules/json/pages/reference.adoc b/doc/modules/json/pages/reference.adoc new file mode 100644 index 0000000..6ecbe82 --- /dev/null +++ b/doc/modules/json/pages/reference.adoc @@ -0,0 +1,37 @@ +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// + += JSON Reference + +== +[source,cpp] +---- +include::error.hpp[] +---- + +== +[source,cpp] +---- +include::reader.hpp[] +---- + +== +[source,cpp] +---- +include::iarchive.hpp[] + +---- + +== + +[source,cpp] +---- + +include::writer.hpp[] + +---- diff --git a/doc/modules/json/pages/reference.html b/doc/modules/json/pages/reference.html new file mode 100644 index 0000000..f421616 --- /dev/null +++ b/doc/modules/json/pages/reference.html @@ -0,0 +1,954 @@ + + + + + + + +JSON Reference + + + + + +
+
+

<boost/libs/trial.protocol/include/trial/protocol/json/error.hpp>

+
+
+
+
#ifndef TRIAL_PROTOCOL_JSON_ERROR_HPP
+#define TRIAL_PROTOCOL_JSON_ERROR_HPP
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2015 Bjorn Reese <breese@users.sourceforge.net>
+//
+// 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)
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <system_error>
+#include <trial/protocol/json/token.hpp>
+
+namespace trial
+{
+namespace protocol
+{
+namespace json
+{
+
+enum errc
+{
+    no_error = 0,
+
+    unexpected_token,
+    invalid_key,
+    invalid_value,
+    incompatible_type,
+
+    unbalanced_end_array,
+    unbalanced_end_object,
+    expected_end_array,
+    expected_end_object,
+
+    insufficient_tokens
+};
+
+const std::error_category& error_category();
+
+enum errc to_errc(token::code::value) noexcept;
+
+inline std::error_code make_error_code(json::errc e = no_error)
+{
+    return std::error_code(static_cast<int>(e),
+                           json::error_category());
+}
+
+class error : public std::system_error
+{
+public:
+    error(std::error_code ec)
+        : system_error(std::move(ec))
+    {}
+    error(enum errc e)
+        : system_error(make_error_code(e))
+    {}
+};
+
+inline void throw_on_error(enum json::errc);
+
+} // namespace json
+} // namespace protocol
+} // namespace trial
+
+namespace std
+{
+
+template <>
+struct is_error_code_enum<trial::protocol::json::errc>
+    : public std::true_type
+{
+};
+
+} // namespace std
+
+#include <trial/protocol/json/detail/error.ipp>
+
+#endif // TRIAL_PROTOCOL_JSON_ERROR_HPP
+
+
+
+
+
+

<boost/libs/trial.protocol/include/trial/protocol/json/reader.hpp>

+
+
+
+
#ifndef TRIAL_PROTOCOL_JSON_READER_HPP
+#define TRIAL_PROTOCOL_JSON_READER_HPP
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2015 Bjorn Reese <breese@users.sourceforge.net>
+//
+// 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)
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <string>
+#include <stack>
+#include <vector>
+#include <trial/protocol/json/error.hpp>
+#include <trial/protocol/json/token.hpp>
+#include <trial/protocol/json/detail/decoder.hpp>
+
+namespace trial
+{
+namespace protocol
+{
+namespace json
+{
+
+//! @brief Incremental JSON reader.
+//!
+//! Parse a JSON formatted input buffer incrementally. Incrementally means that
+//! the reader only parses enough of the input to identify the next token.
+//! The entire input has to be parsed by repeating parsing the next token until
+//! the end of the input.
+template <typename CharT>
+class basic_reader
+{
+public:
+    using value_type = typename detail::basic_decoder<CharT>::value_type;
+    using size_type = typename detail::basic_decoder<CharT>::size_type;
+    using view_type = core::detail::basic_string_view<CharT, core::char_traits<CharT>>;
+
+    basic_reader();
+
+    //! @brief Construct an incremental JSON reader.
+    //!
+    //! The first token is automatically parsed.
+    //!
+    //! The reader does not assume ownership of the view.
+    //!
+    //! @param[in] view A string view of a JSON formatted buffer.
+    basic_reader(const view_type& view);
+
+    //! @brief Copy-construct an incremental JSON reader.
+    //!
+    //! Copies the internal parsing state from the input reader, and continues
+    //! parsing independently from where the input reader had reached.
+    //!
+    //! @param[in] other The reader that is copied.
+    basic_reader(const basic_reader& other) = default;
+
+    basic_reader(basic_reader&&) = default;
+
+    basic_reader& operator=(const basic_reader&) = default;
+    basic_reader& operator=(basic_reader&&) = default;
+
+    //! @brief Parse the next token.
+    //!
+    //! @returns false if an error occurred or end-of-input was reached, true otherwise.
+    bool next();
+
+    //! @brief Parse the next token if current token has a given value.
+    //!
+    //! @param[in] expect Expected value of current token.
+    //! @returns false if current token does not have the expected value.
+    bool next(token::code::value expect);
+
+    //! @brief Get the current nesting level.
+    //!
+    //! Keep track of the nesting level of containers.
+    //! The outermost root level is 0.
+    //!
+    //! @returns The current nesting level.
+    size_type level() const noexcept;
+
+    //! @brief Get the current token as a detailed code.
+    //!
+    //! @returns The code of the current token.
+    token::code::value code() const noexcept;
+
+    //! @brief Get the current token as a symbol.
+    //!
+    //! @returns The symbol of the current token.
+    token::symbol::value symbol() const noexcept;
+
+    //! @brief Get the current token as a category.
+    //!
+    //! @returns The category of the current token.
+    token::category::value category() const noexcept;
+
+    //! @brief Get the current error.
+    //!
+    //! The error code contains an error enumerator. If parsing did not result
+    //! in an error, the json::no_error enumerator is used.
+    //!
+    //! @returns The current error code.
+    std::error_code error() const noexcept;
+
+    //! @brief Converts the current value into ReturnType.
+    //!
+    //! The following conversions are valid:
+    //! -# Convert a symbol::boolean token into bool.
+    //! -# Convert a symbol::integer token into an integral C++ type (expect bool.)
+    //! -# Convert a symbol::real token into a floating-point C++ type.
+    //! -# Convert a symbol::string token into std::string.
+    //!
+    //! @returns The converted value.
+    //! @throws json::error if requested type is incompatible with the current token.
+    template <typename ReturnType> ReturnType value() const;
+
+    //! @brief Converts the current value into T.
+    //!
+    //! The following conversions are valid:
+    //! -# Convert a symbol::boolean token into bool.
+    //! -# Convert a symbol::integer token into an integral C++ type (expect bool.)
+    //! -# Convert a symbol::real token into a floating-point C++ type.
+    //! -# Convert a symbol::string token into std::string.
+    //!
+    //! @param[out] output The converted value if no error occurs.
+    //! @returns json::errc if requested type is incompatible with the current token.
+    template <typename T> json::errc value(T& output) const noexcept;
+
+    //! @brief Collects a converted string.
+    //!
+    //! The Collector must implement the following functions:
+    //! -# push_back(value_type)
+    //! -# append(const value_type *, size_type)
+    //!
+    //! @param[out] collector The object that receives the converted string.
+    //! @returns json::errc if requested type is incompatible with the current token.
+    template <typename Collector> json::errc string(Collector& collector) const noexcept;
+
+    //! @returns A view of the current value before it is converted into its type.
+    view_type literal() const noexcept;
+
+    //! @returns A view of the remaining buffer.
+    view_type tail() const noexcept;
+
+#ifndef BOOST_DOXYGEN_INVOKED
+protected:
+    template <typename ReturnType, typename Enable = void>
+    struct overloader;
+
+    bool next_frame();
+
+protected:
+    using decoder_type = detail::basic_decoder<value_type>;
+    decoder_type decoder;
+
+    struct frame
+    {
+        frame(token::null) noexcept;
+        frame(token::begin_array) noexcept;
+        frame(token::begin_object) noexcept;
+
+        token::code::value (frame::*next)(decoder_type&) noexcept;
+
+    private:
+        token::code::value next_outer(decoder_type&) noexcept;
+        token::code::value next_array(decoder_type&) noexcept;
+        token::code::value next_array_value(decoder_type&) noexcept;
+        token::code::value next_object(decoder_type&) noexcept;
+        token::code::value next_object_key(decoder_type&) noexcept;
+        token::code::value next_object_value(decoder_type&) noexcept;
+    };
+    std::stack<frame, std::vector<frame>> stack;
+#endif
+};
+
+using reader = basic_reader<char>;
+
+} // namespace json
+} // namespace protocol
+} // namespace trial
+
+#include <trial/protocol/json/detail/reader.ipp>
+
+#endif // TRIAL_PROTOCOL_JSON_READER_HPP
+
+
+
+
+
+

<boost/libs/trial.protocol/include/trial/protocol/json/serialization/iarchive.hpp>

+
+
+
+
#ifndef TRIAL_PROTOCOL_JSON_SERIALIZATION_IARCHIVE_HPP
+#define TRIAL_PROTOCOL_JSON_SERIALIZATION_IARCHIVE_HPP
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2015 Bjorn Reese <breese@users.sourceforge.net>
+//
+// 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)
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <string>
+#include <boost/archive/detail/common_iarchive.hpp>
+#include <boost/archive/detail/register_archive.hpp>
+#include <trial/protocol/json/reader.hpp>
+
+namespace trial
+{
+namespace protocol
+{
+namespace json
+{
+
+template <typename CharT>
+class basic_iarchive
+    : public boost::archive::detail::common_iarchive< basic_iarchive<CharT> >
+{
+    friend class boost::archive::load_access;
+
+public:
+    using value_type = CharT;
+
+    basic_iarchive(const json::reader&);
+    basic_iarchive(const json::reader::view_type&);
+    template <typename Iterator>
+    basic_iarchive(Iterator begin, Iterator end);
+
+    template<typename T>
+    void load_override(T& data);
+
+    template<typename T>
+    void load_override(T& data, long);
+
+    template <typename Tag>
+    void load();
+
+    template <typename T>
+    void load(T&);
+
+    template <typename Tag>
+    bool at() const;
+
+    token::code::value code() const;
+    token::symbol::value symbol() const;
+    token::category::value category() const;
+
+    const json::basic_reader<value_type>& reader() const;
+    void reader(json::basic_reader<value_type>);
+
+#ifndef BOOST_DOXYGEN_INVOKED
+    // Ignore these
+    void load(boost::archive::version_type&) {}
+    void load(boost::archive::object_id_type) {}
+    void load(boost::archive::object_reference_type) {}
+    void load(boost::archive::class_id_type) {}
+    void load(boost::archive::class_id_optional_type) {}
+    void load(boost::archive::class_id_reference_type) {}
+    void load(boost::archive::tracking_type) {}
+    void load(boost::archive::class_name_type&) {}
+#endif
+
+#ifndef BOOST_DOXYGEN_INVOKED
+private:
+    void next();
+    void next(token::code::value);
+
+private:
+    struct
+    {
+        json::basic_reader<value_type> reader;
+    } member;
+#endif
+};
+
+using iarchive = basic_iarchive<char>;
+
+} // namespace json
+} // namespace protocol
+} // namespace trial
+
+#include <trial/protocol/json/serialization/detail/iarchive.ipp>
+#include <boost/archive/detail/register_archive.hpp>
+
+BOOST_SERIALIZATION_REGISTER_ARCHIVE(trial::protocol::json::iarchive)
+
+#endif // TRIAL_PROTOCOL_JSON_SERIALIZATION_IARCHIVE_HPP
+
+
+
+
+
+

<boost/libs/trial.protocol/include/trial/protocol/json/writer.hpp>

+
+
+
+
#ifndef TRIAL_PROTOCOL_JSON_WRITER_HPP
+#define TRIAL_PROTOCOL_JSON_WRITER_HPP
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2015 Bjorn Reese <breese@users.sourceforge.net>
+//
+// 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)
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <stack>
+#include <vector>
+#include <trial/protocol/buffer/base.hpp>
+#include <trial/protocol/json/error.hpp>
+#include <trial/protocol/json/token.hpp>
+#include <trial/protocol/json/detail/encoder.hpp>
+
+namespace trial
+{
+namespace protocol
+{
+namespace json
+{
+
+//! @brief Incremental JSON writer.
+//!
+//! Generate JSON output incrementally by appending C++ data.
+template <typename CharT, std::size_t N = 2 * sizeof(void *)>
+class basic_writer
+{
+public:
+    using value_type = CharT;
+    using size_type = std::size_t;
+    using view_type = typename detail::basic_encoder<value_type, N>::view_type;
+
+    //! @brief Construct an incremental JSON writer.
+    //!
+    //! The buffer type can be any for which a buffer wrapper exists.
+    //!
+    //! @param[in] buffer A buffer where the JSON formatted output is stored.
+    template <typename T> basic_writer(T& buffer);
+
+    std::error_code error() const BOOST_NOEXCEPT;
+    size_type level() const BOOST_NOEXCEPT;
+
+    //! @brief Write structural output.
+    template <typename T>
+    size_type value();
+
+    //! @brief Write data output.
+    template <typename T>
+    size_type value(T&& value);
+
+    //! @brief Write raw output.
+    size_type literal(const view_type&) BOOST_NOEXCEPT;
+
+#ifndef BOOST_DOXYGEN_INVOKED
+private:
+    void validate_scope();
+    void validate_scope(token::code::value, enum json::errc);
+
+    template <typename T, typename Enable = void>
+    struct overloader;
+
+    size_type null_value();
+    size_type begin_array_value();
+    size_type end_array_value();
+    size_type begin_object_value();
+    size_type end_object_value();
+
+private:
+    using encoder_type = detail::basic_encoder<value_type, N>;
+    encoder_type encoder;
+    mutable enum json::errc last_error;
+
+    struct frame
+    {
+        frame(encoder_type& encoder, token::code::value);
+
+        void write_separator();
+
+        encoder_type& encoder;
+        token::code::value code;
+        std::size_t counter;
+    };
+    std::stack<frame, std::vector<frame>> stack;
+#endif // BOOST_DOXYGEN_INVOKED
+};
+
+using writer = basic_writer<char>;
+
+} // namespace json
+} // namespace protocol
+} // namespace trial
+
+#include <trial/protocol/json/detail/writer.ipp>
+
+#endif // TRIAL_PROTOCOL_JSON_WRITER_HPP
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/doc/modules/json/pages/token.adoc b/doc/modules/json/pages/token.adoc new file mode 100644 index 0000000..1231c03 --- /dev/null +++ b/doc/modules/json/pages/token.adoc @@ -0,0 +1,97 @@ +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// + += Token + +IMPORTANT: Incremental processing is a low-level API which regards JSON as a sequence of tokens to be processed one by one. + +The JSON parser and generator use tokens to identify data types as well as errors. +All token-related types are located in the `trial::protocol::json::token` namespace, +which we will simply refer to as `token` below. + +NOTE: Tokens are located in the `` header. + +=== Token constants + +A token is represented by the `token::code` enumeration type with a constant for +each possible token or error state. This means that each error is represented +by its own enumerator constant. + +=== Symbols + +Working directly with `token::code` can be tedious. +Suppose you want to check if an error occurred, then you have to check if the +current token is one of the numerous error constants. +Each `token::code` enumerator has therefore been grouped into a more convenient +enumeration type called `token::symbol` that is better suited for normal +operation. + +All `token::code` error constants have been grouped into the single +`token::symbol::error` constant, and we can now check for errors with a single +comparison. + +.JSON symbol constants +|=== +|`*_token::symbol_*` | **Description** +|`boolean` |True or false. +|`integer` |Integer number. +|`number` |Floating-point number. +|`string` |String value. +|`key` |String key for associative array. +|`null` |No data. +|`begin_array` |Start of an array. +|`end_array` |End of an array. +|`begin_object` |Start of an associative array. +|`end_object` |End of an associative array. +|`separator` |A context-specific separator. +|`end` |End of input or output buffer. +|`error` |Erroneous format. + +|=== + +The symbol type will be the preferred manner to use tokens in the examples +throughout this documentation. +In fact, we are not even going to describe the `token::code` enumerator constants +here,.footnote:[The description of all `token::code` enumerator constants can be found in the reference documentation.] because we are only interested in the +subset that contains the error codes and they are described in the section on +https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/error.html[errors]. + +=== Categories + +The symbol constants are grouped into the `token::category` enumeration type. +There are different categories of tokens: + +.JSON category constants +|=== +|`token::category` |Description +|`data` |Data tokens have a value associated with them, whose content can be retrieved. Examples of data tokens are booleans, numbers, and strings. +|`structural` |Structural tokens wrap containers and separate items. +|`nullable` |The nullable token is a special case, because it can represent either a data token without and associated value or structural token without an associated container, such as a missing integer or a missing array. The nullable token is typeless. +|`status` |A status token indicates another condition. +|=== + +The following table shows which categories the the various symbols belong to. + +.Relation between symbols and categories +|=== +|`token::symbol`|`token::category` +|`boolean`|`data` +|`integer`|`data` +|`number`|`data` +|`string`|`data` +|`key`|`data` +|`null`|`nullable` +|`begin_array`|`structural` +|`end_array`|`structural` +|`begin_object`|`structural` +|`end_object`|`structural` +|`separator`|`structural` +|`end`|`status` +|`error`|`status` +|=== + diff --git a/doc/json/tutorial.qbk b/doc/modules/json/pages/tutorial.adoc similarity index 67% rename from doc/json/tutorial.qbk rename to doc/modules/json/pages/tutorial.adoc index 5461a71..f709a42 100644 --- a/doc/json/tutorial.qbk +++ b/doc/modules/json/pages/tutorial.adoc @@ -1,49 +1,63 @@ -[/ - Copyright (C) 2015 Bjorn Reese +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// - 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). -] += Tutorials -[section:tutorial Tutorials] +[NOTE] +==== +The tutorials assume that the following alias has been declared + +[source,cpp] +---- -[note The tutorials assume that the following alias has been declared -``` namespace json = trial::protocol::json; -```] -[section Serialization] +---- +==== + +== Serialization -This tutorial shows how the [link protocol.json.oarchive JSON output archive] -and the [link protocol.json.iarchive JSON input archive] can be used to serialize -C++ data into JSON and deserialize JSON into C++ data in just a few lines of code. +This tutorial shows how the https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/oarchive.html[JSON output archive] and the https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/iarchive.html[JSON input archive] can be used to serialize C++ data into JSON and deserialize JSON into C++ data in just a few lines of code. -[heading Fundamental types] +=== Fundamental types -We start by serializing [@http://en.cppreference.com/w/cpp/language/types -fundamental types], because there is built-in serialization support for most +We start by serializing http://en.cppreference.com/w/cpp/language/types[fundamental types], because there is built-in serialization support for most of them. The JSON output archive knows how to generate valid JSON, but it needs a buffer to store the output in. This buffer is passed to the constructor of the output -archive. We can choose between several [link protocol.core.adapter buffer types]. +archive. We can choose between several https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/core/adapter.html[buffer types] In the following we will only demonstrate how to serialize to a `std::string`. We first need to include a wrapper for `std::string` that is used by the output archive. -``` + +[source,cpp] +---- + #include // Use std::string as output buffer -``` -We also need to include [link protocol.json.oarchive `json::oarchive`] together -with other headers to glue __protocol__ into Boost.Serialization. + +---- + +We also need to include https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/oarchive.html[`json::oarchive`] together +with other headers to glue Trial.protocol into Boost.Serialization. This is most easily done like this: -``` + +[source,cpp] +---- + #include -``` + +---- Serializing is as simple as creating a `std::string` and a `json::oarchive`, and then stream our data to the archive. -``` +[source,cpp] +---- // Create data bool input = true; @@ -51,29 +65,35 @@ bool input = true; std::string buffer; json::oarchive oarchive(buffer); oarchive << input; -``` +---- + The `buffer` string now contains the formatted JSON output. -``` +[source,cpp] +---- assert(buffer == "true"); -``` +---- + +We can deserialize the buffer again with the https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/iarchive.html[JSON input archive]. -We can deserialize the buffer again with the [link protocol.json.iarchive JSON input archive]. -``` +[source,cpp] +---- bool output = false; json::iarchive iarchive(buffer); iarchive >> output; assert(output == true); -``` +---- -[heading Containers] +=== Containers There is also built-in support for serialization of certain standard C++ containers, such as `std::vector`, `std::set`, and `std::map`. The following example shows how to serialize an `std::map`. The serialization code follows the same pattern as used in the previous example. -``` + +[source,cpp] +---- #include #include @@ -88,9 +108,12 @@ json::oarchive oarchive(buffer); oarchive << input; assert(buffer == "{\"alpha\":\"hydrogen",\"bravo\":\"helium\"}"); -``` +---- + Deserialization is done by adding: -``` + +[source,cpp] +---- std::map output; json::iarchive iarchive(buffer); iarchive >> output; @@ -98,11 +121,11 @@ iarchive >> output; assert(output.size() == 2); assert(output["alpha"] == "hydrogen"); assert(output["bravo"] == "helium"); -``` -[endsect] +---- + -[section Incremental Processing] +== Incremental Processing Serialization and document processing is build on top of incremental processing. Incremental processing can also be used directly for more efficient processing @@ -110,49 +133,57 @@ such as searching for keys in a JSON file without having to convert strings or from JSON into C++ types. We are going to start with incremental generation to create JSON formatted -output using the [link protocol.json.writer `json::writer`]. -Afterwards we will use the [link protocol.json.reader `json::reader`] to parse +output using the https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/writer.html[`json::writer`]. +Afterwards we will use the https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/reader.html[`json::reader`] to parse JSON formatted input. -[heading Generating fundamental types] +=== Generating fundamental types -The [link protocol.json.writer `json::writer`] is used to incrementally generate +The https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/writer.html[`json::writer`] is used to incrementally generate a JSON formatted buffer. We can either write a fundamental type, or use tags to write special tokens. We first need to include a couple of headers. -``` +[source,cpp] +---- #include #include -``` +---- + Let us generate a boolean value: -``` + +[source,cpp] +---- std::string output; json::writer writer(output); writer.write(true); assert(output == "true"); -``` +---- + We can also generate a `null` value, which means that the current entry does not have a value. Think of it as an uninitialized optional value. We pass a tag as template parameter to indicate that `null` should be inserted. -``` + +[source,cpp] +---- std::string output; json::writer writer(output); writer.write(); assert(output == "null"); -``` +---- -[heading Generating array] +=== Generating array Containers have to start with with a begin bracket and terminate with an end bracket. These brackets must be written explicitly with a tag. The separators between entries are automatically inserted. -``` +[source,cpp] +---- #include #include @@ -176,12 +207,11 @@ assert(output == "[true,2,3.0,\"alpha\""); writer.write(); assert(output == "[true,2,3.0.\"alpha\"]"); -``` +---- -[heading Counting keys] +=== Counting keys -We now turn our attention to incremental parsing. [link protocol.json.reader -`json::reader`] is a pull parser that lazily parses a single token in the input. +We now turn our attention to incremental parsing. https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/reader.html[`json::reader`] is a pull parser that lazily parses a single token in the input. The `json::reader::next()` function is used to advance the cursor to the next token. @@ -191,7 +221,8 @@ want to count all key-value pairs with a given key called `needle`. For the sake of simplicity we are going to assume that there are no nested containers. -``` +[source,cpp] +---- #include std::size_t prefix_count(const std::string& haystack, @@ -210,7 +241,7 @@ std::size_t prefix_count(const std::string& haystack, } while (reader.next()); // Skip value return count; } -``` +---- In the above example we convert the current `key` from JSON to `std::string` before doing the comparison. @@ -218,7 +249,9 @@ before doing the comparison. We can optimize this by converting the `needle` into a JSON string and then comparing it with the unconverted JSON string. We will use the `json::writer` for that. -``` + +[source,cpp] +---- #include #include #include @@ -243,22 +276,14 @@ std::size_t prefix_count_fast(const std::string& haystack, } while (reader.next()) // Skip value return count; } -``` +---- -[endsect] -[section Push Parser] +== Push Parser -In this tutorial we are going to use the incremental [link protocol.json.reader -`json::reader`] parser to build another kind of incremental parser, so we are -going to introduce a distinction between incremental /pull/ parsers and -incremental /push/ parsers. -The main difference between them is the direction of control. -With pull parsers, like [link protocol.json.reader `json::reader`], -the user extracts or pulls one token after another, whereas with push parser the -tokens are automatically pushed to the user via callback functions. +In this tutorial we are going to use the incremental https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/reader.html[`json::reader`] parser to build another kind of incremental parser, so we are going to introduce a distinction between incremental _pull_ parsers and incremental _push_ parsers. The main difference between them is the direction of control. With pull parsers, like https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/reader.html[`json::reader`], the user extracts or pulls one token after another, whereas with push parser the tokens are automatically pushed to the user via callback functions. -We will use [link protocol.json.reader `json::reader`] to build the push parser, +We will use https://leminhos.gitlab.io/doc.trial.protocol/trial.protocol/protocol/json/reader.html[`json::reader`] to build the push parser, because pull parsers are well-suited to create other kinds of parser interfaces. The serialization output archives that we saw in a previous tutorial is another example of a higher-level parser build on top of pull parsers. @@ -267,16 +292,17 @@ This tutorial demonstrates how `json::reader` can be used to create a push parse A push parser iterates over the JSON input and invokes callback functions for each parsed data item. Each data type has a distinct callback function. The user provides the implemention of these callback functions. The design is a -variation of the [@http://en.wikipedia.org/wiki/Builder_pattern Builder pattern], -and this is how XML [@http://en.wikipedia.org/wiki/Simple_API_for_XML SAX] parsers +variation of the http://en.wikipedia.org/wiki/Builder_pattern[Builder pattern], +and this is how XML http://en.wikipedia.org/wiki/Simple_API_for_XML[SAX] parsers work. -[heading Definitions] +=== Definitions First we define the `push_parser` class which takes the callback functions as -a template parameter.[footnote We could also have used a polymorphic interface +a template parameter..footnote:[We could also have used a polymorphic interface for the callback functions.] -``` +[source,cpp] +---- #include template @@ -291,11 +317,13 @@ private: Callbacks callbacks; json::reader reader; }; -``` +---- + The `Callbacks` template parameter must be a class that implements a member function for each callback function. The `Callbacks` class looks something like this: -``` +[source,cpp] +---- #include #include @@ -312,24 +340,25 @@ public: void on_begin_object(); void on_end_object(); }; -``` +---- + We are not going to implement `my_callbacks` here, although a simple implementation could be to simply print the type and value in each callback function. -[heading Execution] +=== Execution After these preliminary definitions, we have now arrived at the crux of the problem: how to implement the `push_parser::parse()` function. Fortunately that is very simple using a pull parser: -# Iterate over the JSON input using `json::reader::next()`. - # Identify the current token with `json::reader::symbol()`. - # Invoke the appropriate callback function. - The current value for data tokens is obtained with `json::reader::value()`. +. Iterate over the JSON input using `json::reader::next()`. +.. Identify the current token with `json::reader::symbol()`. +.. Invoke the appropriate callback function. The current value for data tokens is obtained with `json::reader::value()`. Here is the entire implementation in its full glory: -``` +[source,cpp] +---- void push_parser::parse() { do @@ -378,15 +407,15 @@ void push_parser::parse() } while (reader.next()); } -``` +---- Finally, we use the above push parser as follows: -``` -json::reader reader("[null,true,42]"); // Replace with actual JSON input + +[source,cpp] +---- +json::reader reader("[null,true,42]"); // Re1place with actual JSON input push_parser parser(reader); parser.parse(); -``` +---- -[endsect] -[endsect] diff --git a/doc/json/writer.qbk b/doc/modules/json/pages/writer.adoc similarity index 66% rename from doc/json/writer.qbk rename to doc/modules/json/pages/writer.adoc index 500dd3d..edd3fd1 100644 --- a/doc/json/writer.qbk +++ b/doc/modules/json/pages/writer.adoc @@ -1,27 +1,27 @@ -[/ - Copyright (C) 2015 Bjorn Reese +// +// Copyright (C) 2015 Bjorn Reese +// +// 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). +// - 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). -] - -[#protocol.json.writer] -[section Writer] += Writer Writer is an incremental generator that outputs C++ data types in a JSON format. The output is generated piece by piece as C++ data types are inserted. The `writer` keeps track of the context and inserts the appropriate separators between values where needed. -[table Writer Accessors -[[Writer member function][Description]] -[[`size_type level()`][Returns the current level of nested containers.]] -[[`error_code error()`][Returns the current error code.]] -[[`size_type literal(const view_type&)`][Write a literal value directly into the JSON output without formatting it. Returns the number of characters written. Returns zero if an error occurred.]] -[[`size_type value()`][Write a formatted tag into the JSON output. Returns the number of characters written. Returns zero if an error occurred.]] -[[`size_type value(T)`][Write a formatted value into the JSON output. Returns the number of characters written. Returns zero if an error occurred.]] -] +.Writer Accessors +|=== +|Writer member function |Description +|`size_type level()` |Returns the current level of nested containers. +|`error_code error()` |Returns the current error code. +|`size_type literal(const view_type&)` |Write a literal value directly into the JSON output without formatting it. Returns the number of characters written. Returns zero if an error occurred. +|`size_type value()` |Write a formatted tag into the JSON output. Returns the number of characters written. Returns zero if an error occurred. +|`size_type value(T)` |Write a formatted value into the JSON output. Returns the number of characters written. Returns zero if an error occurred. +|=== Values are properly formatted and written into the JSON output with `writer::value(T)`. The parameter `T` can be a boolean, a number, or a string. @@ -35,58 +35,70 @@ These can be useful useful for adding whitespaces, but special care should be exerted to not violate the JSON format. As `writer` has been designed for wire protocols, it does not insert whitespaces -into the output[footnote See `example/json/pretty_printer` for an example of how +into the output.footnote:[See `example/json/pretty_printer` for an example of how to produce an indented output.]. -[note The following examples assume that you have included the following header +[NOTE] +==== +The following examples assume that you have included the following header files: -``` +[source,cpp] +---- #include #include -```] -[heading Boolean] +---- +==== + +=== Boolean Boolean values are output via `writer::value(bool)`. -``` + +[source,cpp] +---- std::ostringstream result; json::writer writer(result); writer.value(true); // Write boolean value assert(result.str() == "true"); -``` -[heading Number] +---- + +=== Number Numbers can either be integer values or floating-point values. -[heading String] +=== String Strings are written by passing an `std::string` or a string literal to `writer::value(T)`. All strings will be quoted in the JSON output, and special characters will be escaped. Strings must be UTF-8 encoded. -``` + +[source,cpp] +---- std::ostringstream result; json::writer writer(result); writer.value("alpha"); // Write string assert(result.str() == "\"alpha\""); -``` +---- -[heading Null] +=== Null Nullable value are output with `writer::value()`. -``` +[source,cpp] +---- std::ostringstream result; json::writer writer(result); writer.value(); // Write nullable value assert(result.str() == "null"); -``` -[heading Array] +---- + +=== Array An array is initiated by passing the `json::begin_array` tag to `writer::value(T)`, and terminated by passing the `json::end_array`. These tags must be properly balanced, @@ -94,7 +106,8 @@ otherwise an error will be raised. Value separators are automatically inserted between values. -``` +[source,cpp] +---- std::ostringstream result; json::writer writer(result); @@ -109,10 +122,9 @@ assert(result.str() == "[42,43"); // Write number writer.value(json::end_array); assert(result.str() == "[42,43]"); // Write ending of array -``` +---- -[heading Associative array] +=== Associative array Name separators are automatically inserted between the key and the value, and value separators are automatically inserted between key-value pairs. -[endsect] diff --git a/doc/nav.adoc b/doc/nav.adoc new file mode 100644 index 0000000..d1e172e --- /dev/null +++ b/doc/nav.adoc @@ -0,0 +1,31 @@ +* Core +** xref:core:adapter.adoc[] +** xref:core:serialization.adoc[] +* JSON +** xref:json:json.adoc[] +** xref:json:tutorial.adoc[] +** Guide +*** xref:json:guide.adoc[] +*** Incremental Processing +**** xref:json:token.adoc[] +**** xref:json:error.adoc[] +**** xref:json:reader.adoc[] +**** xref:json:writer.adoc[] +*** Serialization +**** xref:json:iarchive.adoc[] +**** xref:json:oarchive.adoc[] +** xref:json:design.adoc[] +//** xref:json:reference.adoc[] +* Dynamic Variable +** xref:dynamic:dynamic.adoc[] +** xref:dynamic:tutorial.adoc[] +** User Guide +*** xref:dynamic:type.adoc[] +*** xref:dynamic:function.adoc[] +*** xref:dynamic:algorithm.adoc[] +*** xref:dynamic:converter.adoc[] +*** xref:dynamic:concept.adoc[] +** xref:dynamic:rationale.adoc[] +** xref:dynamic:acknowledgement.adoc[] +//** xref:dynamic:reference.adoc[] +//FIXME: broken includes diff --git a/doc/protocol.qbk b/doc/protocol.qbk deleted file mode 100644 index a85131d..0000000 --- a/doc/protocol.qbk +++ /dev/null @@ -1,98 +0,0 @@ -[/ - Copyright (C) 2015 Bjorn Reese - - 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). -] - -[library Trial.Protocol - [quickbook 1.5] - [id protocol] - [dirname protocol] - [purpose JSON parser and generator] - [authors [Reese, Bjorn]] - [copyright 2015-2018 Bjorn Reese] - [license Distributed under the [@http://www.boost.org/LICENSE_1_0.txt Boost Software License, Version 1.0].] -] - -[def __protocol__ Trial.Protocol] - -[important __protocol__ is not an official Boost library. - -__protocol__ is still work-in-progress. -] - -[section Introduction] -__protocol__ is a header-only library[footnote __protocol__ serialization relies -on Boost.Serialization, which is not header-only. Serialization is an optional -feature.] for processing (parsing, manipulating, and generating) encoded data -for network wire protocols. __protocol__ contains several interfaces for parsing -and generating encoded data, as well as a heterogeneous tree data structure that -can be used as a parse tree. - -Currently supported protocols[footnote __protocol__ only supports protocols that -can be tokenized without using a schema.] are: - -* [link protocol.json JSON] -* BinToken - -[heading Levels of Abstraction] - -Protocol processing can be done at any of three levels of abstraction: - -* Incremental processors transforms the data token by token. - There are two types of incremental processing: (i) push processing where the - processing is done automatically and each token causes a callback to be - invoked, and (ii) pull processing where the user has to advance manually from - one token to the next.[footnote Pull processors resembles a - [@http://en.cppreference.com/w/cpp/concept/ForwardIterator ForwardIterator], - albeit with an interface closer to the - [@https://en.wikipedia.org/wiki/Iterator_pattern Iterator pattern].] - Incremental processing is also called stream processing. -* Serialization archives are used to transform directly between the protocol - format and C++ data structures. - The serialization archives do not go through an intermediate representation - and can therefore perform faster and in less memory. - The mapping between the protocol format and the C++ data structures - can be specified both (i) intrusively by augmenting the C++ data - structure with the mapping, and (ii) non-intrusively by specifying the - mapping in separate function outside the C++ data structure. -* Tree processing[footnote Tree processing is similar to creating a - [@http://en.wikipedia.org/wiki/Document_Object_Model Document Object Model].] - transforms the the entire encoded data into a generic tree structure which - can then be examined and manipulated with tree operations. - -At each level of abstraction there are processors for both parsing and -generating protocol formats. These are summarized below. - -[table -[[] [Parser] [Generator]] -[[Incremental] - [The encoded input can be parsed token by token with an incremental parser. - For each token we can query the current token type and value.] - [The encoded output can be generated token by token with an incremental - generator.]] -[[Serialization] - [The encoded input can be deserialized directly into arbitrary C++ data - structures with an input archive.] - [Arbitrary C++ data structures can be serialized directly into encoded output - with an output archive.]] -[[Tree] - [The encoded input can be parsed into a dedicated parse tree.] - [The dedicated parse tree can be transformed into an encoded output.]] -] - -The protocol generators can write the encoded output to different types of -buffers as long as an [link protocol.core.adapter adapter] exists for the buffer -type. The correct header files must be included for this to work seamlessly. - -[note For brevity all examples in this documentation assumes -```using namespace trial::protocol;``` -] - -[endsect] - -[include core/core.qbk] -[include json/json.qbk] -[include dynamic/dynamic.qbk] diff --git a/supplemental-ui/partials/footer-content.hbs b/supplemental-ui/partials/footer-content.hbs new file mode 100644 index 0000000..0021ece --- /dev/null +++ b/supplemental-ui/partials/footer-content.hbs @@ -0,0 +1,8 @@ +
+

Copyright © 2008 Beman Dawes, Rene Rivera

+

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)

+

This software is Open Source Initiative approved Open Source Software.

+

Open Source Initiative Approved is a trademark of the Open Source Initiative.

+

This page was built using the Antora default UI using supplemental files.

+

The source code for this UI is licensed under the terms of the MPL-2.0 license.

+
diff --git a/supplemental-ui/partials/header-content.hbs b/supplemental-ui/partials/header-content.hbs new file mode 100644 index 0000000..08933e1 --- /dev/null +++ b/supplemental-ui/partials/header-content.hbs @@ -0,0 +1,35 @@ +
+ +
From 19f0db78e126f03ded8388a267536a491bf71bb1 Mon Sep 17 00:00:00 2001 From: lemosjose Date: Thu, 15 Jun 2023 22:48:25 -0300 Subject: [PATCH 2/2] correct branch --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 90dc743..914c54f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,7 +1,7 @@ name: Publish on: push: - branches: [ "master" ] + branches: [ "develop" ] # for possible necessary rebuilds workflow_dispatch: jobs: