From 57bb1a45e6370da86fb429fa9e6f0aea809ebf5e Mon Sep 17 00:00:00 2001 From: "Cleison.Oliveira" Date: Mon, 29 Sep 2025 22:41:02 -0300 Subject: [PATCH] Convert docs from boost quickbook to antora (asciidoc) --- .github/workflows/publish.yml | 40 + antora-playbook.yml | 20 + doc/Jamfile.v2 | 60 - doc/antora.yml | 7 + doc/core/adapter.qbk | 122 -- 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/converter.qbk | 100 -- doc/dynamic/dynamic.qbk | 62 - doc/dynamic/function.qbk | 170 --- doc/dynamic/guide.qbk | 18 - doc/dynamic/tutorial.qbk | 98 -- doc/dynamic/type.qbk | 171 --- doc/json/error.qbk | 68 - doc/json/guide.qbk | 54 - 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 -- doc/modules/ROOT/pages/index.adoc | 49 + doc/modules/core/pages/adapter-traits.adoc | 91 ++ doc/modules/core/pages/adapter.adoc | 34 + doc/modules/core/pages/serialization.adoc | 10 + .../dynamic/pages/acknowledgement.adoc | 8 + doc/modules/dynamic/pages/algorithm.adoc | 201 +++ doc/modules/dynamic/pages/concept.adoc | 198 +++ doc/modules/dynamic/pages/converter.adoc | 111 ++ doc/modules/dynamic/pages/design.adoc | 98 ++ doc/modules/dynamic/pages/dynamic.adoc | 145 +++ doc/modules/dynamic/pages/function.adoc | 164 +++ doc/modules/dynamic/pages/guide.adoc | 17 + doc/modules/dynamic/pages/header-error.adoc | 51 + .../dynamic/pages/header-functional.adoc | 65 + doc/modules/dynamic/pages/header-token.adoc | 56 + .../dynamic/pages/header-variable.adoc | 1104 +++++++++++++++++ doc/modules/dynamic/pages/header-visit.adoc | 58 + .../dynamic/pages/rationale.adoc} | 109 +- doc/modules/dynamic/pages/type.adoc | 208 ++++ .../json/pages/design-rationale.adoc} | 46 +- doc/modules/json/pages/error.adoc | 74 ++ doc/modules/json/pages/header-error.adoc | 66 + doc/modules/json/pages/header-iarchive.adoc | 68 + doc/modules/json/pages/header-reader.adoc | 171 +++ doc/modules/json/pages/header-writer.adoc | 48 + .../json/pages/iarchive.adoc} | 52 +- doc/modules/json/pages/json-overview.adoc | 26 + doc/modules/json/pages/oarchive.adoc | 20 + doc/modules/json/pages/overview.adoc | 28 + .../json/pages/reader.adoc} | 151 ++- doc/modules/json/pages/token.adoc | 95 ++ doc/modules/json/pages/tree-processing.adoc | 1 + .../json/pages/tutorials.adoc} | 211 ++-- doc/modules/json/pages/user-guide.adoc | 18 + .../json/pages/writer.adoc} | 95 +- doc/modules/nav.adoc | 40 + doc/protocol.qbk | 98 -- supplemental-ui/partials/footer-content.hbs | 8 + supplemental-ui/partials/header-content.hbs | 102 ++ 62 files changed, 3875 insertions(+), 1993 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/adapter.qbk 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/converter.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/tutorial.qbk delete mode 100644 doc/dynamic/type.qbk delete mode 100644 doc/json/error.qbk delete mode 100644 doc/json/guide.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 create mode 100644 doc/modules/ROOT/pages/index.adoc create mode 100644 doc/modules/core/pages/adapter-traits.adoc create mode 100644 doc/modules/core/pages/adapter.adoc 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 create mode 100644 doc/modules/dynamic/pages/converter.adoc 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 create mode 100644 doc/modules/dynamic/pages/header-error.adoc create mode 100644 doc/modules/dynamic/pages/header-functional.adoc create mode 100644 doc/modules/dynamic/pages/header-token.adoc create mode 100644 doc/modules/dynamic/pages/header-variable.adoc create mode 100644 doc/modules/dynamic/pages/header-visit.adoc rename doc/{dynamic/rationale.qbk => modules/dynamic/pages/rationale.adoc} (52%) create mode 100644 doc/modules/dynamic/pages/type.adoc rename doc/{json/design.qbk => modules/json/pages/design-rationale.adoc} (52%) create mode 100644 doc/modules/json/pages/error.adoc create mode 100644 doc/modules/json/pages/header-error.adoc create mode 100644 doc/modules/json/pages/header-iarchive.adoc create mode 100644 doc/modules/json/pages/header-reader.adoc create mode 100644 doc/modules/json/pages/header-writer.adoc rename doc/{json/iarchive.qbk => modules/json/pages/iarchive.adoc} (53%) create mode 100644 doc/modules/json/pages/json-overview.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} (68%) create mode 100644 doc/modules/json/pages/token.adoc create mode 100644 doc/modules/json/pages/tree-processing.adoc rename doc/{json/tutorial.qbk => modules/json/pages/tutorials.adoc} (72%) create mode 100644 doc/modules/json/pages/user-guide.adoc rename doc/{json/writer.qbk => modules/json/pages/writer.adoc} (60%) create mode 100644 doc/modules/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..47ab6e4 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,40 @@ +name: trail.protocol +on: + push: + branches: [main] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: +concurrency: + group: github-pages + cancel-in-progress: false +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write +jobs: + build: + runs-on: ubuntu-latest + # environment: + # name: github-pages + # url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Checkout repository + uses: actions/checkout@v5 + - name: Configure Pages + uses: actions/configure-pages@v5 + - name: Install Node.js + uses: actions/setup-node@v5 + with: + node-version: '18' + - name: Install Antora + run: npm i antora + - name: Generate Site + run: npx antora antora-playbook.yml + - name: Upload Artifacts + uses: actions/upload-pages-artifact@v4 + with: + path: build/site + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/antora-playbook.yml b/antora-playbook.yml new file mode 100644 index 0000000..8f694d2 --- /dev/null +++ b/antora-playbook.yml @@ -0,0 +1,20 @@ +site: + title: Trail.Protocol + start_page: Trial.Protocol:ROOT:index.adoc +content: + sources: + - url: https://github.com/Cleison-oliveira/trial.protocol.git + branches: HEAD + 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.hbs + contents: ./supplemental-ui/partials/header-content.hbs + - path: partials/footer-content.hbs + contents: ./supplemental-ui/partials/footer-content.hbs + - path: .nojekyll + contents: | + static_files: + - .nojekyll \ No newline at end of file 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..e7a1227 --- /dev/null +++ b/doc/antora.yml @@ -0,0 +1,7 @@ +name: Trial.Protocol +version: v1.0 +asciidoc: + attributes: + table-caption: false +nav: +- modules/nav.adoc \ No newline at end of file diff --git a/doc/core/adapter.qbk b/doc/core/adapter.qbk deleted file mode 100644 index d3394c5..0000000 --- a/doc/core/adapter.qbk +++ /dev/null @@ -1,122 +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.adapter] -[section Adapter] - -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, -strings, and I/O streams. As these have different interfaces, each type is -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. -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`] [``]] -] - -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] - -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 -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] - -Assume that we add support for `std::deque`. First the buffer wrapper for -`std::deque` is written like this: - -``` -#include -#include - -namespace my { - -template -class deque_wrapper : public buffer::base -{ -public: - using value_type = typename buffer::base::value_type; - using size_type = typename buffer::base::size_type; - using view_type = typename buffer::base::view_type; - - deque_wrapper(std::deque& buffer) - : buffer(buffer) - {} - -protected: - virtual bool grow(size_type delta) - { - buffer.resize(buffer.size() + delta); - } - - virtual void write(value_type value) - { - buffer.push_back(value); - } - - virtual void write(const view_type& view) - { - buffer.insert(buffer.end(), view.begin(), view.end()); - } - -private: - std::deque& buffer; -}; - -} // namespace my -``` - -Next we must make this wrapper known to the protocol generator, which is done -as follows: - -``` -namespace trial { namespace protocol { namespace buffer { - -template -struct traits< std::deque > -{ - using buffer_type = my::deque_wrapper; -}; - -}}} -``` - -[note -The `traits` struct must be located inside the `trial::protocol::buffer` -namespace.] - -[endsect] 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/converter.qbk b/doc/dynamic/converter.qbk deleted file mode 100644 index 8137ca2..0000000 --- a/doc/dynamic/converter.qbk +++ /dev/null @@ -1,100 +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 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. - -``` -InputType input = /* input data */; - -auto output = dynamic::convert::into(input); -``` - -or... - -``` -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. - -``` -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] - -[heading std::vector] - -[note `#include `] - -``` -// 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); -``` - -``` -// 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] - -[note `#include `] - -``` -// 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); -``` - -``` -// 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] - -[note `#include ` - -This creates a compile-time dependency on Boost.Any.] - -``` -// Convert boost::any into dynamic variable -boost::any input = 1; - -auto output = dynamic::convert::info(input); -assert(output.is()); -``` - -[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/tutorial.qbk b/doc/dynamic/tutorial.qbk deleted file mode 100644 index 31ffa4f..0000000 --- a/doc/dynamic/tutorial.qbk +++ /dev/null @@ -1,98 +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 Tutorial] - -All examples throughout this documentation assumes the following prologue: - -``` -#include -namespace dynamic = trial::dynamic; -``` - -[section Shapeshifter] - -A default-initialized `dynamic::variable` has no value. No value is represented -with the `dynamic::nullable` type. - -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`. -``` -// 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. -``` -// 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. -``` -// 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); -``` -Finally, we will change our variable into an associative array. -``` -// 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); -``` - -[endsect] - -[section Algorithms] - -[heading Accumulation] - -Suppose we have a dynamic variable that contains an array of integers. -``` -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()`] -algorithm. - -As `std::accumulate()` works on homogeneous types, the initial value must be a -`dynamic::variable`. We could wrap zero with `dynamic::variable(0)`, but in the -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. -``` -#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/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/guide.qbk b/doc/json/guide.qbk deleted file mode 100644 index 36b74b4..0000000 --- a/doc/json/guide.qbk +++ /dev/null @@ -1,54 +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.json.guide] -[section User Guide] - -__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, -such as a number, a string, the beginning or the end of an array. - -Incremental processing has a lower-level interface than serialization or -document processing, but even though incremental processing is more laborious to -use, it covers more use cases and can be used more efficiently. -Some examples of these use cases are: - -* The input and output serialization archives are build on top of incremental - parser and generators. -* Certain operations on JSON buffers does not require that individual JSON - elements, such as JSON escaped strings, are converted into in-memory data - structures. For instance, searching for data in a JSON file, or pretty-printing - the content of a JSON file. -* 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/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/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc new file mode 100644 index 0000000..a5af632 --- /dev/null +++ b/doc/modules/ROOT/pages/index.adoc @@ -0,0 +1,49 @@ +:C++: {cpp} + += Introduction + +[IMPORTANT] +==== +Trial.Protocol is not an official Boost library. + +Trial.Protocol is still work-in-progress. +==== + +Trial.Protocol is a header-only library{empty}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{empty}footnote:[Trial.Protocol only supports protocols that can be tokenized without using a schema.] are: + +. xref:json:json-overview.adoc[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 {cpp} 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 {cpp} data structures can be specified both (i) intrusively by augmenting the {cpp} data structure with the mapping, and (ii) non-intrusively by specifying the mapping in separate function outside the {cpp} data structure. +* Tree processing{empty}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 {cpp} data structures with an input archive. | Arbitrary {cpp} 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 xref:core:adapter.adoc[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/ \ No newline at end of file diff --git a/doc/modules/core/pages/adapter-traits.adoc b/doc/modules/core/pages/adapter-traits.adoc new file mode 100644 index 0000000..36dcefd --- /dev/null +++ b/doc/modules/core/pages/adapter-traits.adoc @@ -0,0 +1,91 @@ += 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 +buffer adapter for the output buffer type. + +The buffer adapter must inherit from `buffer::base` and implement the +following API: + + +|=== +| `*Member*` | `*Description*` +|`[blue]#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. +|`[blue]#void# write(value_type)` |Output a single character. +|`[blue]#void# write([blue]#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 + +namespace my { + +template +class deque_wrapper : public buffer::base +{ +public: + using value_type = typename buffer::base::value_type; + using size_type = typename buffer::base::size_type; + using view_type = typename buffer::base::view_type; + + deque_wrapper(std::deque& buffer) + : buffer(buffer) + {} + +protected: + virtual bool grow(size_type delta) + { + buffer.resize(buffer.size() + delta); + } + + virtual void write(value_type value) + { + buffer.push_back(value); + } + + virtual void write(const view_type& view) + { + buffer.insert(buffer.end(), view.begin(), view.end()); + } + +private: + std::deque& buffer; +}; + +} // 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 +struct traits< std::deque > +{ + using buffer_type = my::deque_wrapper; +}; + +}}} + +---- + +NOTE: The `traits` struct must be located inside the `trial::protocol::buffer` namespace. + + + + + + diff --git a/doc/modules/core/pages/adapter.adoc b/doc/modules/core/pages/adapter.adoc new file mode 100644 index 0000000..6478730 --- /dev/null +++ b/doc/modules/core/pages/adapter.adoc @@ -0,0 +1,34 @@ +// +// +// 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). +// + += 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, strings, and I/O streams. As these have different interfaces, each type is 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. + +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. + + +|=== + +|`*Type*` |`*Header File*` +|`CharT[N]` |`` +|`std::array` |`` +|`std::basic_ostream`|`` +|`std::basic_string`|`` +|`std::vector`|`` + +|=== + +where `CharT` can be `[blue]#char#` or `[blue]#unsigned char#.` Wide characters are not +supported. + +Support for other output buffer types can be added via adapter traits. + diff --git a/doc/modules/core/pages/serialization.adoc b/doc/modules/core/pages/serialization.adoc new file mode 100644 index 0000000..02d125b --- /dev/null +++ b/doc/modules/core/pages/serialization.adoc @@ -0,0 +1,10 @@ +// 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. \ No newline at end of file diff --git a/doc/modules/dynamic/pages/acknowledgement.adoc b/doc/modules/dynamic/pages/acknowledgement.adoc new file mode 100644 index 0000000..c4ec11d --- /dev/null +++ b/doc/modules/dynamic/pages/acknowledgement.adoc @@ -0,0 +1,8 @@ +:C++: {cpp} + += Acknowledgement + +Ferruccio Barletta for an earlier {cpp}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 {cpp}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 {cpp} Allocators"]. diff --git a/doc/modules/dynamic/pages/algorithm.adoc b/doc/modules/dynamic/pages/algorithm.adoc new file mode 100644 index 0000000..bd2812b --- /dev/null +++ b/doc/modules/dynamic/pages/algorithm.adoc @@ -0,0 +1,201 @@ +// +// 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). +// +:C++: {cpp} + += Algorithms + +The dynamic variable is accompanied by a number of algorithms, but also works with most standard {cpp} algorithms. + +== Count + +Counts the number of matching keys or values in a dynamic variable. + +NOTE: `[.blue]##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: `[.blue]##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: `[.blue]##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 {cpp} 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 {cpp} 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 `[.blue]#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..e38c94e --- /dev/null +++ b/doc/modules/dynamic/pages/concept.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). +// + += Concepts + +IMPORTANT: Work-in-progress. + +== NestedContainer + +A *NestedContainer* is a https://en.cppreference.com/w/cpp/container.html[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` | | +|=== + +[[dynamic-nested]] +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()` |`[.blue]#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)` |`[.blue]#void#` |Fails. | +|`a.insert(p, t)` |`iterator` |Fails. | +|`a.insert(p, i, j)` |`[.blue]#void#` |Fails. | +|=== + +.Sequence Semantics +|=== +| `*Expression*` | `*Return type*` | `*Sequence semantics*` | `*Conditions*` +|`a.clear()` |`[.blue]#void#` |Removes all nested elements. | _Effects:_ + +`a.empty() == [.blue]#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)` |`[.blue]#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)` | `[.blue]#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()` | `[.blue]#void#` |Removes all nested elements. | _Effects:_ + +`a.empty() == [.blue]#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)` |`[.blue]#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)` | `[.blue]#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 xref:dynamic:concept.adoc#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()` | `[.blue]#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)` | `[.blue]#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)` | `[.blue]#void#` |Fails. | +|=== diff --git a/doc/modules/dynamic/pages/converter.adoc b/doc/modules/dynamic/pages/converter.adoc new file mode 100644 index 0000000..927565c --- /dev/null +++ b/doc/modules/dynamic/pages/converter.adoc @@ -0,0 +1,111 @@ +// +// 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). +// +// + += Converters + +Converters are convenience functions for copying data between the dynamic variable and other containers. + +The general pattern is `OutputType dynamic::convert(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(input); +---- + +or... + +[source,cpp] +---- +InputType input = /* input data */; + +OutputType output = dynamic::convert(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(input, error); +if (error) + /* Handle errors */ +---- + +The converters for each container are located in separate header files. + +// enum +//FIXME: empty section! + +== std::vector + + +NOTE: `[.blue]##include# ` + +[source,cpp] +---- +// Convert std::vector into dynamic variable +std::vector input = { 1, 2, 3, 4 }; + +auto output = dynamic::convert(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>(input); +assert(output.size() == 4); +---- + +== std::map + +NOTE: `[.blue]##include# ` + +[source,cpp] +---- +// Convert std::map into dynamic variable +std::map input = { { "alpha", "hydrogen" }, { "bravo", "helium" } }; + +auto output = dynamic::convert(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>(input); +assert(output.size() == 2); +---- + +== boost::any + +[NOTE] +==== +`[.blue]##include# ` + +`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(input); +assert(output.is()); +---- diff --git a/doc/modules/dynamic/pages/design.adoc b/doc/modules/dynamic/pages/design.adoc new file mode 100644 index 0000000..331e987 --- /dev/null +++ b/doc/modules/dynamic/pages/design.adoc @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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) +// +/////////////////////////////////////////////////////////////////////////////// +:C++: {cpp} + += 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 {cpp} 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..6113111 --- /dev/null +++ b/doc/modules/dynamic/pages/dynamic.adoc @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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) +// +/////////////////////////////////////////////////////////////////////////////// +:C++: {cpp} + + += Trial.Dynamic +Bjørn Reese + +== Overview + +Trial.Dynamic is a {cpp}11 header-only library with a {cpp} 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:[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. + +* 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 https://en.cppreference.com/w/cpp/container.html[Container] + and therefore works with standard {cpp} 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. + +== Tutorial + +All examples throughout this documentation assumes the following prologue: + +[source,cpp] +---- +#include +namespace dynamic = trial::dynamic; +---- + +== Shapeshifter + +A default-initialized `dynamic::variable` has no value. No value is represented +with the `dynamic::nullable` type. + +We can query if the variable has a certain type with the xref:header-variable.adoc#dynamic_variable[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()); +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 = + { + { "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); + +---- + + +== 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()] +algorithm. + +As `std::accumulate()` works on homogeneous types, the initial value must be a +`dynamic::variable`. We could wrap zero with `dynamic::variable([.cyan]#0#)`, but in the +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); +---- diff --git a/doc/modules/dynamic/pages/function.adoc b/doc/modules/dynamic/pages/function.adoc new file mode 100644 index 0000000..2e9b2cf --- /dev/null +++ b/doc/modules/dynamic/pages/function.adoc @@ -0,0 +1,164 @@ +// +// 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 + +Dynamic variables can be created in various ways. +[width=100%] +|=== +| `*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:_ + +[.red]#// 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:_ + +[.red]#// Direct-initializes to integer type# + +dynamic::variable data([.cyan]#1#); + +assert(data.is<[.blue]##int##>()); + +|`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:_ + +[.red]#// Copy-initializes from other value# + +dynamic::variable other([.cyan]#3.0#); + +dynamic::variable data(other); + +assert(data.is<[.blue]##double##>()); + +|`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:_ + +[.red]#// List-initializes heterogenous array from initializer list# + +dynamic::variable data { null, [.blue]#true#, [.cyan]#2#, [.cyan]#3.0#, [.cyan]#"alpha"# }; + +assert(data.is()); + + +[.red]#// List-initializes nested integer array from initializer list# + +dynamic::variable data { [.cyan]#1#, [.cyan]#2#, { [.cyan]#3#, [.cyan]#4# }, [.cyan]#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{empty}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:_ + + +[.red]#// List-initializes associative array from initializer list# + +dynamic::variable data { { [.cyan]#"alpha"#, [.cyan]#1# }, { [.cyan]#"bravo"#, [.cyan]#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()` |`[.blue]#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.[.blue]#operator# T()` |`T` |Conversion operator. + +Same operation as `v.value()`. +|`v.assume_value()` |`T&` |Returns stored element by reference. +|`v.assume_value() [.blue]#const#` |`[.blue]#const# T&` | +|`v.[.blue]#operator#[u]` |`[.blue]#const# variable&` |Looks up element by key `u`. +|`v.[.blue]#operator#[n]` |`[.blue]#const# variable&` |Looks up element by integer position `n`. + +|=== + +.Modifiers +|=== +| `*Function*` | `*Returns*` | `*Description*` +|`v.clear()` |`[.blue]#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)` |`[.blue]#void#` |Inserts elements in range [`i`, `j`). +|`v.insert(p, i, j)` |`[.blue]#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..c3fc40c --- /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 \ No newline at end of file diff --git a/doc/modules/dynamic/pages/header-error.adoc b/doc/modules/dynamic/pages/header-error.adoc new file mode 100644 index 0000000..f5c846a --- /dev/null +++ b/doc/modules/dynamic/pages/header-error.adoc @@ -0,0 +1,51 @@ +=== Header + +[source,cpp] +---- +namespace trial { + namespace dynamic { + class error; + + // Error value. + enum errc { no_error = = 0, incompatible_type }; + + // Error category. + const std::error_category & error_category(); + + // Error code factory. + std::error_code make_error_code(dynamic::errc e = no_error); + } +} + + +---- + +== Class error + +[[class_error]] +trial::dynamic::error — Dynamic system error. + +=== Synopsis + +[source,cpp] +---- +// In header: + + +class error : public system_error { +public: + // construct/copy/destruct + error(std::error_code); +}; +---- + +== Description + +Specialization of std::system_error that is used to distinguish between exceptions throw from Trial.Dynamic and other components. + +=== error public construct/copy/destruct + + + +1. `error(std::error_code ec);` + + Constructs an exception from an error code. \ No newline at end of file diff --git a/doc/modules/dynamic/pages/header-functional.adoc b/doc/modules/dynamic/pages/header-functional.adoc new file mode 100644 index 0000000..f60f802 --- /dev/null +++ b/doc/modules/dynamic/pages/header-functional.adoc @@ -0,0 +1,65 @@ +=== Header + +[source,cpp] +---- +namespace trial { + namespace dynamic { + template struct is; + template struct same; + } +} +---- + +== Struct template is + +trial::dynamic::is — Function object to check if variable has a given tag. + +=== Synopsis +[source,cpp] +---- +// In header: + +template +struct is { + + // public member functions + bool operator()(const variable &) const; +}; +---- + +== Description + +*See Also:* + +basic_variable::is() + +== is public member functions + +1. [.blue]#bool operator#()([.blue]#const# xref:dynamic:header-variable.adoc[variable] & value) [.blue]#const#; + + +== Struct template same + +trial::dynamic::same — Function object to check if variable has exactly a given type. + +=== Synopsis +[source,cpp] +---- +// In header: + +template +struct same { + + // public member functions + bool operator()(const variable &) const; +}; +---- + +== Description + +*See Also:* + + +basic_variable::same() + +same public member functions + +1. [.blue]#bool operator#()([.blue]#const# xref:dynamic:header-variable.adoc[variable] & value) [.blue]#const#; \ No newline at end of file diff --git a/doc/modules/dynamic/pages/header-token.adoc b/doc/modules/dynamic/pages/header-token.adoc new file mode 100644 index 0000000..bc89bc0 --- /dev/null +++ b/doc/modules/dynamic/pages/header-token.adoc @@ -0,0 +1,56 @@ +=== Header + +[source,cpp] +---- +namespace trial { + namespace dynamic { + namespace token { + struct code; + struct symbol; + } + } +} +---- + +[[struct_code]] +== Struct code + +trial::dynamic::token::code + +=== Synopsis + +[source,cpp] +---- +// In header: + + +struct code { + + enum value { null, boolean, signed_char, unsigned_char, + signed_short_integer, unsigned_short_integer, signed_integer, + unsigned_integer, signed_long_integer, unsigned_long_integer, + signed_long_long_integer, unsigned_long_long_integer, real, + long_real, long_long_real, string, wstring, u16string, + u32string, array, map, float_number = = real, + double_number = = long_real, + long_double_number = = long_long_real }; +}; +---- +[[struct_symbol]] +== Struct symbol + +trial::dynamic::token::symbol + +=== Synopsis + +[source,cpp] +---- +// In header: + + +struct symbol { + + enum value { null, boolean, integer, real, string, wstring, u16string, + u32string, array, map }; +}; +---- diff --git a/doc/modules/dynamic/pages/header-variable.adoc b/doc/modules/dynamic/pages/header-variable.adoc new file mode 100644 index 0000000..7ccfaeb --- /dev/null +++ b/doc/modules/dynamic/pages/header-variable.adoc @@ -0,0 +1,1104 @@ +:space:       + +[[dynamic_variable]] +=== Header + +[source,cpp] +---- +namespace trial { + namespace dynamic { + template class Allocator> struct basic_array; + template class Allocator> struct basic_map; + + template class Allocator> class basic_variable; + + struct boolean; + struct integer; + struct real; + struct string; + struct u16string; + struct u32string; + struct wstring; + + enum nullable { null }; + + typedef basic_variable< std::allocator > variable; + typedef basic_array< std::allocator > array; + typedef basic_map< std::allocator > map; + template class Allocator, typename U> + basic_variable< Allocator > + operator+(const basic_variable< Allocator > &, const U &); + template class Allocator> + basic_variable< Allocator > + operator+(nullable, const basic_variable< Allocator > &); + template bool operator==(const T &, const U &); + template bool operator!=(const T &, const U &); + template bool operator<(const T &, const U &); + template class Allocator, typename U> + bool operator<=(const basic_variable< Allocator > &, const U &); + template class Allocator, typename U> + bool operator>(const basic_variable< Allocator > &, const U &); + template class Allocator, typename U> + bool operator>=(const basic_variable< Allocator > &, const U &); + } +} +---- + +== Struct template basic_array + +trial::dynamic::basic_array + +=== Synopsis + +[source,cpp] +---- +// In header: + +template class Allocator> +struct basic_array { +}; +---- + +== Struct template basic_map + +trial::dynamic::basic_map + +=== Synopsis + +[source,cpp] +---- +// In header: + +template class Allocator> +struct basic_map { +}; +---- + + +== Class template basic_variable + + +trial::dynamic::basic_variable — Dynamic variable. [[trail_dynamic]] + +=== Synopsis +[[basic_variable]] +[source,cpp] +---- +// In header: + +template class Allocator> +class basic_variable { +public: + // types + typedef basic_variable< Allocator > value_type; + typedef value_type & reference; + typedef const value_type & const_reference; + typedef unspecified difference_type; // signed integer type + typedef unspecified size_type; // unsigned integer type + typedef std::basic_string< char, std::char_traits< char >, Allocator > string_type; // std::string by default + typedef std::basic_string< wchar_t, std::char_traits< wchar_t >, Allocator > wstring_type; // std::wstring by default + typedef std::basic_string< char16_t, std::char_traits< char16_t >, Allocator > u16string_type; // std::u16string by default + typedef std::basic_string< char32_t, std::char_traits< char32_t >, Allocator > u32string_type; // std::u32string by default + typedef unspecified array_type; // SequenceContainer. + typedef unspecified map_type; // AssociativeContainer. + typedef unspecified pair_type; + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + + // member classes/structs/unions + + class const_iterator : + public iterator_base< const_iterator, const basic_variable::value_type > + { + public: + // construct/copy/destruct + const_iterator(); + const_iterator(const const_iterator &); + const_iterator(const_iterator &&); + const_iterator(const iterator &); + explicit const_iterator(pointer, bool = true); + + // public member functions + const_reference key() const; + const_reference value() const; + const_reference operator*() const; + }; + + class iterator : + public iterator_base< iterator, basic_variable::value_type > + { + public: + // construct/copy/destruct + iterator(); + iterator(const iterator &); + iterator(iterator &&); + explicit iterator(const const_iterator &); + explicit iterator(pointer, bool = true); + explicit iterator(pointer, typename super::array_iterator); + explicit iterator(pointer, typename super::map_iterator); + iterator & operator=(const iterator &); + iterator & operator=(iterator &&); + + // public member functions + const_reference key() const; + reference value(); + const_reference value() const; + reference operator*(); + const_reference operator*() const; + }; + + class key_iterator : + public iterator_base< key_iterator, const basic_variable::value_type > + { + public: + // construct/copy/destruct + key_iterator(); + key_iterator(const key_iterator &); + key_iterator(key_iterator &&); + explicit key_iterator(pointer, bool = true); + key_iterator & operator=(const key_iterator &); + key_iterator & operator=(key_iterator &&); + + // public member functions + const_reference key() const; + const_reference value() const; + const_reference operator*(); + key_iterator & operator++(); + }; + template + struct tag_traits { + }; + template + struct traits { + }; + + // construct/copy/destruct + basic_variable(); + basic_variable(const basic_variable &); + basic_variable(basic_variable &&) noexcept; + template basic_variable(T); + basic_variable(const std::initializer_list< value_type > &); + basic_variable(const nullable &); + basic_variable(const char *); + basic_variable(const wchar_t *); + basic_variable(const char16_t *); + basic_variable(const char32_t *); + basic_variable(const typename basic_variable::array_type &); + basic_variable(typename basic_variable::array_type &&); + basic_variable(const typename basic_variable::map_type &); + basic_variable(typename basic_variable::map_type &&); + basic_variable & operator=(const basic_variable &); + basic_variable & operator=(basic_variable &&); + template basic_variable & operator=(T); + basic_variable & operator=(nullable); + basic_variable & operator=(const char *); + basic_variable & operator=(const wchar_t *); + basic_variable & operator=(const char16_t *); + basic_variable & operator=(const char32_t *); + + // public member functions + basic_variable & operator+=(const basic_variable &); + template basic_variable & operator+=(const T &); + basic_variable & operator+=(const char *); + basic_variable & operator+=(const wchar_t *); + basic_variable & operator+=(const char16_t *); + basic_variable & operator+=(const char32_t *); + template T value() const; + template T value(std::error_code &) const noexcept; + template explicit operator R() const; + template R & assume_value(); + template const R & assume_value() const; + basic_variable & operator[](size_type); + const basic_variable & operator[](size_type) const; + basic_variable & operator[](const typename map_type::key_type &); + const basic_variable & operator[](const typename map_type::key_type &) const; + template bool is() const noexcept; + template bool same() const noexcept; + dynamic::code::value code() const noexcept; + dynamic::symbol::value symbol() const noexcept; + bool empty() const noexcept; + size_type size() const noexcept; + size_type max_size() const noexcept; + void clear() noexcept; + iterator insert(const basic_variable &); + template void insert(InputIterator, InputIterator); + iterator insert(const_iterator, const basic_variable &); + template + void insert(const_iterator, InputIterator, InputIterator); + iterator erase(const_iterator); + iterator erase(const_iterator, const_iterator); + void swap(basic_variable &) noexcept; + iterator begin(); + const_iterator begin() const; + const_iterator cbegin() const; + iterator end(); + const_iterator end() const; + const_iterator cend() const; + reverse_iterator rbegin(); + const_reverse_iterator rbegin() const; + const_reverse_iterator crbegin() const; + reverse_iterator rend(); + const_reverse_iterator rend() const; + const_reverse_iterator crend() const; + key_iterator key_begin() const; + key_iterator key_end() const; +}; +---- + +== Description + +Dynamic variable is a tagged union that can change both its type and value during program execution. + +Dynamic variable is a heterogenous hierarchical data structure with a pre-determined list of supported types: fundamental data types, strings, arrays, and associative arrays. + +|=== +| `*Type*` | `*Tag*` +| `dynamic::nullable` | `dynamic::nullable` +| `bool` | `xref:dynamic:header-variable.adoc#boolean[dynamic::boolean]` +| `signed char` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `signed short int` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `signed int` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `signed long int` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `signed long long int` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `unsigned char` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `unsigned short int` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `unsigned int` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `unsigned long int` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `unsigned long long int` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +| `float` | `xref:dynamic:header-variable.adoc#real[dynamic::real]` +| `double` | `xref:dynamic:header-variable.adoc#real[dynamic::real]` +| `long double` | `xref:dynamic:header-variable.adoc#real[dynamic::real]` +| `dynamic::string_type` | `xref:dynamic:header-variable.adoc#string[dynamic::string]` +| `dynamic::wstring_type` | `xref:dynamic:header-variable.adoc#wstring[dynamic::wstring]` +| `dynamic::u16string_type` | `xref:dynamic:header-variable.adoc#u16string[dynamic::u16string]` +| `dynamic::u32string_type` | `xref:dynamic:header-variable.adoc#u32string[dynamic::u32string]` +| `dynamic::array_type` | `dynamic::array` +| `dynamic::map_type` | `dynamic::map` +|=== + +== Template Parameters + +1. `[.blue]#template#< [.blue]#typename# > [.blue]#class# Allocator` + + Allocator type (defaults to std::allocator) + +== basic_variable public construct/copy/destruct + +1. `basic_variable();` + + Constructs a default variable of type nullable. + + A default constructed variable contains null, which means it has no value. + +2. `basic_variable([.blue]#const# xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & element);` + + Copy-constructs a copy of element. + +3. `basic_variable(xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] && element) [.blue]#noexcept#;` + + Move-constructs a copy of element. + +4. `[.blue]#template#<[.blue]##typename## T> basic_variable(T);` + + Constructs a variable of type T. + + T must be a supported type, otherwise a compilation error occurs. + +5. `basic_variable([.blue]#const# std::initializer_list< xref:dynamic:header-variable.adoc[value_type] > & elements);` + + Constructs a variable from initializer list. + + If the initializer list elements contains an array of pairs then the variable will be of type map, otherwise of type array. + +6. `basic_variable([.blue]#const# nullable &);` + + Constructs a variable of type nullable. + +7. `basic_variable([.blue]#const char# *);` + + Constructs a variable of type string. + +8. `basic_variable([.blue]#const wchar_t# *);` + + Constructs a variable of type wstring. + +9. `basic_variable([.blue]#const char16_t# *);` + + Constructs a variable of type xref:dynamic:header-variable.adoc#u16string[u16string]. + +10. `basic_variable([.blue]#const char32_t# *);` + + Constructs a variable of type xref:dynamic:header-variable.adoc#u32string[u32string]. + +11. `basic_variable([.blue]#const typename# basic_variable::array_type &);` + + Construct a variable from array type. + + Re-construct a variable from the value returned by assume_value. + + Prefer to use factory or initializer-lists when creating arrays. + +12. `basic_variable([.blue]#typename# basic_variable::array_type &&);` + + Construct a variable from array type. + + Re-construct a variable from the value returned by assume_value. + + Prefer to use factory or initializer-lists when creating arrays. + +13. `basic_variable([.blue]#const typename# basic_variable::map_type &);` + + Construct a variable from map type. + + Re-construct a variable from the value returned by assume_value. + + Prefer to use factory or initializer-lists when creating associative arrays. + +14. `basic_variable([.blue]#typename# basic_variable::map_type &&);` + + Construct a variable from map type. + + Re-construct a variable from the value returned by assume_value. + + Prefer to use factory or initializer-lists when creating associative arrays. + +15. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#=([.blue]#const# xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] &);` + + Copy-asigns one variable to another. + +16. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#=(xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] &&);` + + Move-assigns one variable to another. + +17. `[.blue]#template#<[.blue]##typename## T> xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#=(T);` + + Assigns content to a variable. + +18. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#=(nullable);` + + Assigns null to variable. + +19. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#=([.blue]#const char# *);` + + Assigns a string literal to a variable. + +20. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#=([.blue]#const wchar_t# *);` + + Assigns a wide-string literal to a variable. + +21. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#=([.blue]#const char16_t# *);` + + Assigns a UTF-16 encoded string literal to a variable. + +22. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#=([.blue]#const char32_t# *);` + + Assigns a UTF-32 encoded string literal to a variable. + +== basic_variable public member functions + +1. xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#+=([.blue]#const# xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] &); ++ +- Appends values. +- The append operator has different meanings depending on the types. + +* Appending null to any other type has no effect. +* Appending any value to null overwrites the variable. +* Appending an integral value with another integral value adds the two. +* Appending a string to another string concatenates the two. +* Appending a wide string to another wide string concatenates the two. +* Appending a UTF-16 encoded string to another UTF-16 encoded string concatenates the two. +* Appending a UTF-32 encoded string to another UTF-32 encoded string concatenates the two. +* Appending an array to an array concatenates the two. +* Appending any type to an array inserts the value at the end of the array. +* Appending a map to a map merges the two. ++ +|=== +| `*Tag*` | `*nullable*` | `*boolean*` | `*integer*` | `*real*` | `*string*` | `*wstring*` | `xref:dynamic:header-variable.adoc#u16string[*u16string*]` | `xref:dynamic:header-variable.adoc#u32string[*u32string*]` | `*array*` | `*map*` + +| `nullable` | `No effect` | `Assigns` | `Assigns` | `Assigns` | `Assigns` | `Assigns` | `Assigns` | `Assigns` | `Assigns` | `Assigns` +| `boolean` | `No effect` | `Adds` | `Adds` | `Adds` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` +| `integer` | `No effect` | `Adds` | `Adds` | `Adds` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` +| `real` | `No effect` | `Adds` | `Adds` | `Adds` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` +| `string` | `No effect` | `Fails` | `Fails` | `Fails` | `Concats` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` +| `wstring` | `No effect` | `Fails` | `Fails` | `Fails` | `Fails` | `Concats` | `Fails` | `Fails` | `Fails` | `Fails` + +| `xref:dynamic:header-variable.adoc#u16string[*u16string*]` | `No effect` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` | `Concats` | `Fails` | `Fails` | `Fails` + +| `xref:dynamic:header-variable.adoc#u32string[*u32string*]` | `No effect` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` | `Fails` | `Concats` | `Fails` | `Fails` +| `array` | `No effect` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Concats` | `Inserts` +| `map` | `No effect` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Inserts` | `Merges` +|=== + +2. `[.blue]#template#<[.blue]##typename## T> xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#+=([.blue]#const# T &);` + + This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. + +3. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#+=([.blue]#const char# *);` + + This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. + +4. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#+=([.blue]#const wchar_t# *);` + + This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. + +5. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#+=([.blue]#const char16_t# *);` + + This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. + +6. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#+=([.blue]#const char32_t# *);` + + This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. + +7. `[.blue]#template#<[.blue]##typename## T> T value() [.blue]#const#;` + +Returns current value as type T. + +Calls xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable]::value(std::error_code&) and throws an exception if an error occurred. + +*See Also:* + +basic_variable::value(std::error_code&) ++ +[%autowidth, frame=none, grid=none] +|=== +| *Template Parameters:* | *T* {space} Supported type +| *Throws:* | `xref:dynamic:header-error.adoc[dynamic::error]` with `dynamic::incompatible_type` if T is an unsupported or incompatible type. +|=== + + + +8. `[.blue]#template#<[.blue]##typename## T> T value(std::error_code & error) [.blue]#const noexcept#;` + +Returns current value as type T. + +T can either be a type or a tag. + +If current tag is `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` or `xref:dynamic:header-variable.adoc#real[dynamic::real]`, then T can be any arithmetic type. Such a conversion may result in loss of precision. + +If current tag is any other type, then T must be the associated type. + +If T is a tag, then the following types are used. ++ +|=== +| `*tags*` | `*Type*` +| `nullable` | `dynamic::nullable` +| `boolean` | `bool` +| `integer` | `int` +| `real` | `float` +| `string` | `variable::string_type` +| `wstring` | `variable::wstring_type` +| `xref:dynamic:header-variable.adoc#u16string[u16string]` | `variable::u16string_type` +| `xref:dynamic:header-variable.adoc#u32string[u32string]` | `variable::u32string_type` +| `array` | `variable::array_type` +| `map` | `variable::map_type` +|=== ++ +[%autowidth, frame=none, grid=none] +|=== +| *Parameters:* | {space} *error* {space} Set to `dynamic::incompatible_type` if T is an unsupported or incompatible type, otherwise unchanged. +| *Template Parameters:* | {space} *T* {space}{space}  Supported type +|=== + + + + +9. `[.blue]#template#<[.blue]##typename## R> [.blue]#explicit operator# R() [.blue]#const#;` + +Returns current value as type R. + +Calls xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable]::value() + +*See Also:* + +basic_variable::value() + +10. `[.blue]#template#<[.blue]##typename## R> R & assume_value();` + +Returns reference to stored value. + +assume_value() has a narrow contract. Using a type R that does not match the type of the stored value results in undefined behavior. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Template Parameters:* | {space} *R* {space} Supported type. +| *Requires:* | {space} basic_variable::same() is true. +|=== + + +11. `[.blue]#template#<[.blue]##typename## R> [.blue]#const# R & assume_value() [.blue]#const#;` + +Returns constant reference to stored value. + +This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. + +12. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#[](size_type position);` + +Returns reference to element at specified position. + +The stored value must be an array. + +No bounds-checking is performed. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Parameters:* | {space} *position* {space} Index position. +| *Requires:* | {space} basic_variable::is() is true +|=== + + +13. `[.blue]#const# xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#[](size_type position) [.blue]#const#;` + +Returns constant reference to element at specified position. + +This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts. + +14. `xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & [.blue]#operator#[]([.blue]#const typename# map_type::key_type & key);` + +Returns reference to element indexed by key. + +Returns a reference to the value associated with key in an associative array, or inserts a key of none exists. + +If the current tag is nullable, then the current value is changed into an associative array and key is inserted. + ++ +[%autowidth, frame=none, grid=none] +|=== +| *Requires:* | {space} basic_variable::is() is true. +| *Throws:* | {space} xref:dynamic:header-error#class_error[dynamic::error] with dynamic::incompatible_type is thrown if current tag is not `dynamic::map`. +|=== + + +15. `[.blue]#const# xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] &` +`[.blue]#operator#[]([.blue]#const typename# map_type::key_type & key) [.blue]#const#;` +Returns constant reference to element indexed by key. + +Returns a reference to the value associated with key in an associative array. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Requires:* | {space} `basic_variable::is() is true.` + +| *Throws:* | {space} xref:dynamic:header-error#class_error[dynamic::error] with dynamic::incompatible_type is thrown if key does not exist in associative array. + +{space} with dynamic::incompatible_type is thrown if current tag is not `dynamic::map.` +|=== + + +16. `[.blue]#template#<[.blue]##typename## T> [.blue]#bool# is() [.blue]#const noexcept#;` [[is_T]] +Checks if variable has a given tag. + +Converts T into a tag, and returns true if variable has the same tag, otherwise returns false. + +If T is a tag, then this tag is used directly: variable v = true; // Boolean assert(v.is()); + +If T is a C++ type, then this is converted a tag: variable v = true; // Boolean assert(v.is()); // Same as is() + +The type-to-tag conversion means that any type from the same category can be used: variable v = 2; // Integer assert(v.is()); // Same as is() assert(v.is()); // Same as is() + +Checking for an array or a map can either be done by a tag: variable v = { 1, 2, 3}; // Array assert(v.is()); + +or by the unspecified variable::array_type or variable::map_type type aliases: variable v = { 1, 2, 3}; // Array assert(v.is()); + +Using unsupported types will result in a compilation error. + ++ +[%autowidth, frame=none, grid=none] +|=== +| *Template Parameters:* | {space} *T* {space} Type or tag to be checked. +|=== + +17. `[.blue]#template#<[.blue]##typename## T> [.blue]#bool# same() [.blue]#const noexcept#;` + [[same_T]] +Checks if variable has exactly a given type. + +Returns true if variable has the exact same type as T, otherwise returns false. variable v = int(2); // int assert(v.same() == true); // Same as int assert(v.same() == false); // Not same as long int + ++ +[%autowidth, frame=none, grid=none] +|=== +| *Template Parameters:* | {space} *T* {space} Type to be checked. +|=== + + +18. `dynamic::code::value code() [.blue]#const noexcept#;` + [[d_code]] +Returns the current code. + +Code is an enumerator that represents the exact type that is currently stored. ++ +|==== +| `*Type*` | `*Code*` +| `dynamic::nullable` | `dynamic::code::null` +| `bool` | `dynamic::code::boolean` +| `signed char` | `dynamic::code::signed_char` +| `signed short int` | `dynamic::code::signed_short_integer` +| `signed int` | `dynamic::code::signed_integer` +| `signed long int` | `dynamic::code::signed_long_integer` +| `signed long long int` | `dynamic::code::signed_long_long_integer` +| `unsigned char` | `dynamic::code::unsigned_char` +| `unsigned short int` | `dynamic::code::unsigned_short_integer` +| `unsigned int` | `dynamic::code::unsigned_integer` +| `unsigned long int` | `dynamic::code::unsigned_long_integer` +| `unsigned long long int` | `dynamic::code::unsigned_long_long_integer` +| `float` | `dynamic::code::real` +| `double` | `dynamic::code::long_real` +| `long double` | `dynamic::code::long_long_real` +| `dynamic::string_type` | `dynamic::code::string` +| `dynamic::wstring_type` | `dynamic::code::wstring` +| `dynamic::u16string_type` | `dynamic::code::u16string` +| `dynamic::u32string_type` | `dynamic::code::u32string` +| `dynamic::array_type` | `dynamic::code::array` +| `dynamic::map_type` | `dynamic::code::map` +|==== + + +19. `dynamic::symbol::value symbol() [.blue]#const noexcept#;` + [[d_symbol]] +Returns the current symbol. + +Symbol is an enumerator that represents the tag of the type currently stored. ++ +|==== +| `*Type*` | `*Tag*` | `*Symbol*` +| `dynamic::nullable` | `nullable` | `dynamic::symbol::null` +| `bool` | `boolean` | `dynamic::symbol::boolean` +| `signed char` | `integer` | `dynamic::symbol::integer` +| `signed short int` | `integer` | `dynamic::symbol::integer` +| `signed int` | `integer` | `dynamic::symbol::integer` +| `signed long int` | `integer` | `dynamic::symbol::integer` +| `signed long long int` | `integer` | `dynamic::symbol::integer` +| `unsigned char` | `integer` | `dynamic::symbol::integer` +| `unsigned short int` | `integer` | `dynamic::symbol::integer` +| `unsigned int` | `integer` | `dynamic::symbol::integer` +| `unsigned long int` | `integer` | `dynamic::symbol::integer` +| `unsigned long long int` | `integer` | `dynamic::symbol::integer` +| `float` | `real` | `dynamic::symbol::real` +| `double` | `real` | `dynamic::symbol::real` +| `long double` | `real` | `dynamic::symbol::real` +| `dynamic::string_type` | `string` | `dynamic::symbol::string` +| `dynamic::wstring_type` | `wstring` | `dynamic::symbol::wstring` +| `dynamic::u16string_type` | `xref:dynamic:header-variable#u16string[u16string]` | `dynamic::symbol::u16string` +| `dynamic::u32string_type` | `xref:dynamic:header-variable#u32string[u32string]` | `dynamic::symbol::u32string` +| `dynamic::array_type` | `array` | `dynamic::symbol::array` +| `dynamic::map_type` | `map` | `dynamic::symbol::map` +|==== + + +20. `[.blue]#bool# empty() [.blue]#const noexcept#;` + +Checks if variable is empty. + +Same as size() == 0 + +21. `size_type size() [.blue]#const noexcept#;` + +Returns the number of elements in variable. + +The number of elements stored in a variable corresponds to the iteration count, that is v.size() == std::distance(v.begin(), v.end()). + +Notice that strings are regarded as single elements, rather than a sequence of characters. ++ +|==== +| `*Current tag*` | `*Size*` | `*Description*` +| `nullable` | `0` | `null is an empty variable.` +| `boolean` | `1` | `boolean is a single element.` +| `integer` | `1` | `integer is a single element.` +| `real` | `1` | `real is a single element.` +| `string` | `1` | `string is a single element, so variable::string_type::size() is not used.` +| `wstring` | `1` | `wstring is a single element, so variable::wstring_type::size() is not used.` +| `xref:dynamic:header-variable#u16string[u16string]` | `1` | `xref:dynamic:header-variable#u16string[u16string] is a single element, so variable::u16string_type::size() is not used.` + +| `xref:dynamic:header-variable#u32string[u32string]` | `1` | `xref:dynamic:header-variable#u32string[u32string] is a single element, so variable::u32string_type::size() is not used.` + +| `array` | `variable::array_type::size()` | `The number of elements in the array. ` +| `map` | `variable::map_type::size()` | `The number of elements (pairs) in the map.` +|==== + +22. `size_type max_size() [.blue]#const noexcept#;` + +Returns the maximum possible number of elements. ++ +|==== +| `*Current tag*` | `*Size*` | `*Description*` +| `nullable` | `0` | `null cannot contain element.` +| `boolean` | `1` | `Can contain a single boolean element.` +| `integer` | `1` | `Can contain a single integer element.` +| `real` | `1` | `Can contain a single real element.` +| `string` | `1` | `Can contain a single string element, so variable::string_type::max_size() is not used.` +| `wstring` | `1` | `Can contain a single wstring element, so variable::wstring_type::max_size() is not used.` +| `xref:dynamic:header-variable#u16string[u16string]` | `1` | `Can contain a single xref:dynamic:header-variable#u16string[u16string] element, so variable::u16string_type::max_size() is not used.` + +| `xref:dynamic:header-variable#u16string[u32string]` | `1` | `Can contain a single xref:dynamic:header-variable#u32string[u32string] element, so variable::u32string_type::max_size() is not used.` + +| `array` | `variable::array_type::max_size()` | `The maximum possible number of elements in the array.` +| `map` | `variable::map_type::max_size()` | `The maximum possible number of elements (pairs) in the map.` +|==== + +23. `[.blue]#void# clear() [.blue]#noexcept#;` + +Clears the content of the variable. + +Only the value is cleared. The type is not modified. ++ +|==== +| `*Current tag*` | `*Behavior*` +| `nullable` | `No effect.` +| `boolean` | `Sets the default constructed value.` +| `integer` | `Sets the default constructed value.` +| `real` | `Sets the default constructed value. ` +| `string` | `Calls variable::string_type::clear().` +| `wstring` | `Calls variable::wstring_type::clear().` +| `xref:dynamic:header-variable#u16string[u16string]` | `Calls variable::u16string_type::clear().` +| `xref:dynamic:header-variable#u32string[u32string]` | `Calls variable::u32string_type::clear().` +| `array` | `Calls variable::array_type::clear().` +| `map` | `Calls variable::map_type::clear().` +|==== + +24. `xref:dynamic:header-variable#class_iterator[iterator] insert([.blue]#const# xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & element);` + +Inserts element into variable. ++ +|==== +| `*Current tag*` | `*Behavior*` +| `nullable` | `Create array and insert element.` +| `boolean` | `Fails.` +| `integer` | `Fails.` +| `real` | `Fails.` +| `string` | `Fails.` +| `wstring` | `Fails.` +| `xref:dynamic:header-variable#u16string[u16string]` | `Fails.` +| `xref:dynamic:header-variable#u32string[u32string]` | `Fails.` +| `array` | `Insert element at end.` +| `map` | `Insert element, otherwise fails if element is not pair.` +|==== + +25. `[.blue]#template#<[.blue]##typename## InputIterator>` + +[.blue]#void# insert(InputIterator begin, InputIterator end); + ++ +Inserts range into variable. ++ +|==== +| `*Current tag*` | `*Behavior*` +| `nullable` | `Create array and insert range.` +| `boolean` | `Fails.` +| `integer` | `Fails.` +| `real` | `Fails.` +| `string` | `Fails.` +| `wstring` | `Fails.` +| `xref:dynamic:header-variable#u16string[u16string]` | `Fails.` +| `xref:dynamic:header-variable#u32string[u32string]` | `Fails.` +| `array` | `Insert range at end.` +| `map` | `Insert range, otherwise fails if any element in range is not pair.` +|==== + +26. `xref:dynamic:header-variable#class_iterator[iterator] insert(xref:dynamic:header-variable#const_iterator[const_iterator] position, [.blue]#const# xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & element);` + + Inserts element into variable. ++ +|==== +| `*Current tag*` | `*Behavior*` +| `nullable` | `Fails.` +| `boolean` | `Fails.` +| `integer` | `Fails.` +| `real` | `Fails.` +| `string` | `Fails.` +| `wstring` | `Fails.` +| `xref:dynamic:header-variable#u16string[u16string]` | `Fails.` +| `xref:dynamic:header-variable#u32string[u32string]` | `Fails.` +| `array` | `Insert element before position.` +| `map` | `Insert element with position as hint, otherwise fails if element is not pair.` +|==== + +27. `[.blue]#template#<[.blue]##typename## InputIterator>` + +  [.blue]#void# insert(xref:dynamic:header-variable#const_iterator[const_iterator], InputIterator begin, InputIterator end); +Inserts range into variable. ++ +|==== +| `*Current tag*` | `*Behavior*` +| `nullable` | `Fails.` +| `boolean` | `Fails.` +| `integer` | `Fails.` +| `real` | `Fails.` +| `string` | `Fails.` +| `wstring` | `Fails.` +| `xref:dynamic:header-variable#u16string[u16string]` | `Fails.` +| `xref:dynamic:header-variable#u32string[u32string]` | `Fails.` +| `array` | `Insert range before position.` +| `map` | `Insert range with position as hint, otherwise fails if any element in range is not pair.` +|==== + +28. xref:dynamic:header-variable#class_iterator[iterator] erase(xref:dynamic:header-variable#const_iterator[const_iterator] position); + +Erases an element from variable. + +Returns iterator following last erased element. ++ +|==== +| `*Current tag*` | `*Behavior*` +| `nullable` | `No effect.` +| `boolean` | `No effect.` +| `integer` | `No effect.` +| `real` | `No effect.` +| `string` | `No effect.` +| `wstring` | `No effect.` +| `xref:dynamic:header-variable#u16string[u16string]` | `No effect.` +| `xref:dynamic:header-variable#u32string[u32string]` | `No effect.` +| `array` | `Erases element at position.` +| `map` | `Erases element at position.` +|==== + +29. xref:dynamic:header-variable#class_iterator[iterator] erase(xref:dynamic:header-variable#const_iterator[const_iterator] begin, xref:dynamic:header-variable#const_iterator[const_iterator] end); + +Erases range from variable. + +Returns iterator following last erased element. ++ +|==== +| `*Current tag*` | `*Behavior*` +| `nullable` | `No effect.` +| `boolean` | `No effect.` +| `integer` | `No effect.` +| `real` | `No effect.` +| `string` | `No effect.` +| `wstring` | `No effect.` +| `xref:dynamic:header-variable#u16string[u16string]` | `No effect.` +| `xref:dynamic:header-variable#u32string[u32string]` | `No effect` +| `array` | `Erases elements in range.` +| `map` | `Erases elements in range.` +|==== + +30. `[.blue]#void# swap(xref:dynamic:header-variable.adoc#trail_dynamic[basic_variable] & other) [.blue]#noexcept#;` + +Exchange contents of variable and other. + +31. xref:dynamic:header-variable#class_iterator[iterator] begin(); + +Returns an iterator to the beginning. + +32. xref:dynamic:header-variable#const_iterator[const_iterator] begin() [.blue]#const#; + +Returns an iterator to the beginning. + +33. xref:dynamic:header-variable#const_iterator[const_iterator] cbegin() [.blue]#const#; + +Returns an iterator to the beginning. + +34. xref:dynamic:header-variable#class_iterator[iterator] end(); + +Returns an iterator to the end. + +35. xref:dynamic:header-variable#const_iterator[const_iterator] end() [.blue]#const#; + +Returns an iterator to the end. + +36. xref:dynamic:header-variable#const_iterator[const_iterator] cend() [.blue]#const#; + +Returns an iterator to the end. + +37. reverse_iterator rbegin(); + +Returns a reverse iterator to the beginning. + +38. const_reverse_iterator rbegin() [.blue]#const#; + +Returns a reverse iterator to the beginning. + +39. const_reverse_iterator crbegin() [.blue]#const#; + +Returns a reverse iterator to the beginning. + +40. reverse_iterator rend(); + +Returns a reverse iterator to the end. + +41. const_reverse_iterator rend() [.blue]#const#; + +Returns a reverse iterator to the end. + +42. const_reverse_iterator crend() [.blue]#const#; + +Returns a reverse iterator to the end. + +43. xref:dynamic:header-variable#key_iterator[key_iterator] key_begin() [.blue]#const#; + +Returns a key iterator to the beginning. + +44. xref:dynamic:header-variable#key_iterator[key_iterator] key_end() [.blue]#const;# + +Returns a key iterator to the end. + +[[const_iterator]] +== Class const_iterator + +trial::dynamic::basic_variable::const_iterator + +=== Synopsis + +[source,cpp] +---- +// In header: + + + +class const_iterator : + public iterator_base< const_iterator, const basic_variable::value_type > +{ +public: + // construct/copy/destruct + const_iterator(); + const_iterator(const const_iterator &); + const_iterator(const_iterator &&); + const_iterator(const iterator &); + explicit const_iterator(pointer, bool = true); + + // public member functions + const_reference key() const; + const_reference value() const; + const_reference operator*() const; +}; +---- + + +== Description + +=== const_iterator public construct/copy/destruct +. const_iterator(); +. const_iterator([.blue]#const# xref:dynamic:header-variable#const_iterator[const_iterator] & other); +. const_iterator(xref:dynamic:header-variable#const_iterator[const_iterator] && other); +. const_iterator([.blue]#const# xref:dynamic:header-variable#iterator[iterator] & other); +. [.blue]#explicit# const_iterator(pointer p, [.blue]#bool# initialize = [.blue]#true#); + +=== const_iterator public member functions + +. xref:dynamic:header-variable#trail_dynamic[const_reference] key() [.blue]#const#; +. xref:dynamic:header-variable#trail_dynamic[const_reference] value() [.blue]#const#; +. xref:dynamic:header-variable#trail_dynamic[const_reference] [.blue]#operator#*() [.blue]#const#; + +[[class_iterator]] +== Class iterator + +trial::dynamic::basic_variable::iterator + +=== Synopsis + +[source,cpp] +---- +// In header: + + + +class iterator : public iterator_base< iterator, basic_variable::value_type > { +public: + // construct/copy/destruct + iterator(); + iterator(const iterator &); + iterator(iterator &&); + explicit iterator(const const_iterator &); + explicit iterator(pointer, bool = true); + explicit iterator(pointer, typename super::array_iterator); + explicit iterator(pointer, typename super::map_iterator); + iterator & operator=(const iterator &); + iterator & operator=(iterator &&); + + // public member functions + const_reference key() const; + reference value(); + const_reference value() const; + reference operator*(); + const_reference operator*() const; +}; +---- + +== Description + +=== iterator public construct/copy/destruct +. iterator(); +. iterator([.blue]#const# xref:dynamic:header-variable#iterator[iterator] & other); +. iterator(xref:dynamic:header-variable#iterator[iterator] && other); +. [.blue]#explicit# iterator([.blue]#const# xref:dynamic:header-variable#const_iterator[const_iterator] & other); +. [.blue]#explicit# iterator(pointer p, [.blue]#bool# initialize = [.blue]#true#); +. [.blue]#explicit# iterator(pointer p, [.blue]#typename# super::array_iterator); +. [.blue]#explicit# iterator(pointer p, [.blue]#typename# super::map_iterator); +. xref:dynamic:header-variable#iterator[iterator] & [.blue]#operator#=([.blue]#const# xref:dynamic:header-variable#iterator[iterator] & other); +. xref:dynamic:header-variable#iterator[iterator] & [.blue]#operator#=(xref:dynamic:header-variable#iterator[iterator] && other); + +=== iterator public member functions +. xref:dynamic:header-variable#trail_dynamic[const_reference] key() [.blue]#const#; +. xref:dynamic:header-variable#trail_dynamic[reference] value(); +. xref:dynamic:header-variable#trail_dynamic[const_reference] value() [.blue]#const#; +. xref:dynamic:header-variable#trail_dynamic[reference] [.blue]#operator#*(); +. xref:dynamic:header-variable#trail_dynamic[const_reference] [.blue]#operator#*() [.blue]#const#; + +[[key_iterator]] +== Class key_iterator + +trial::dynamic::basic_variable::key_iterator + +=== Synopsis +[source,cpp] +---- +// In header: + + + +class key_iterator : + public iterator_base< key_iterator, const basic_variable::value_type > +{ +public: + // construct/copy/destruct + key_iterator(); + key_iterator(const key_iterator &); + key_iterator(key_iterator &&); + explicit key_iterator(pointer, bool = true); + key_iterator & operator=(const key_iterator &); + key_iterator & operator=(key_iterator &&); + + // public member functions + const_reference key() const; + const_reference value() const; + const_reference operator*(); + key_iterator & operator++(); +}; +---- + +== Description + +=== key_iterator public construct/copy/destruct + +. key_iterator(); +. key_iterator([.blue]#const# xref:dynamic:header-variable#key_iterator[key_iterator] & other); +. key_iterator(xref:dynamic:header-variable#key_iterator[key_iterator] && other); +. [.blue]#explicit# key_iterator(pointer p, [.blue]#bool# initialize = [.blue]#true#); +. xref:dynamic:header-variable#key_iterator[key_iterator] & [.blue]#operator#=([.blue]#const# xref:dynamic:header-variable#key_iterator[key_iterator] & other); +. xref:dynamic:header-variable#key_iterator[key_iterator] & [.blue]#operator#=(xref:dynamic:header-variable#key_iterator[key_iterator] && other); + +=== key_iterator public member functions + +. xref:dynamic:header-variable#trail_dynamic[const_reference] key() [.blue]#const#; +. xref:dynamic:header-variable#trail_dynamic[const_reference] value() [.blue]#const#; +. xref:dynamic:header-variable#trail_dynamic[const_reference] [.blue]#operator#*(); +. xref:dynamic:header-variable#key_iterator[key_iterator] & [.blue]#operator#++(); + + +== Struct template tag_traits + +trial::dynamic::basic_variable::tag_traits + +=== Synopsis +[source,cpp] +---- +// In header: + + +template +struct tag_traits { +}; +---- + + +== Struct template traits + +trial::dynamic::basic_variable::traits + +=== Synopsis +[source,cpp] +---- +// In header: + + +template +struct traits { +}; +---- + +[[boolean]] +== Struct boolean + +trial::dynamic::boolean + +=== Synopsis +[source,cpp] +---- +// In header: + + +struct boolean { +}; +---- + +[[integer]] +== Struct integer + +trial::dynamic::integer + +=== Synopsis +[source,cpp] +---- +// In header: + + +struct integer { +}; +---- + +[[real]] +== Struct real + +trial::dynamic::real + +=== Synopsis +[source,cpp] +---- +// In header: + + +struct real { +}; +---- + +[[string]] +== Struct string + +trial::dynamic::string + +=== Synopsis +[source,cpp] +---- +// In header: + + +struct string { +}; +---- + +[[u16string]] +== Struct u16string + +trial::dynamic::u16string + +=== Synopsis +[source,cpp] +---- +// In header: + + +struct u16string { +}; +---- + +[[u32string]] +== Struct u32string + +trial::dynamic::u32string + +=== Synopsis +[source,cpp] +---- +// In header: + + +struct u32string { +}; +---- +[[wstring]] +== Struct wstring + +trial::dynamic::wstring + +=== Synopsis +[source,cpp] +---- +// In header: + + +struct wstring { +}; +---- diff --git a/doc/modules/dynamic/pages/header-visit.adoc b/doc/modules/dynamic/pages/header-visit.adoc new file mode 100644 index 0000000..a9bfa18 --- /dev/null +++ b/doc/modules/dynamic/pages/header-visit.adoc @@ -0,0 +1,58 @@ +:space:       + +=== Header + +[source,cpp] +---- +namespace trial { + namespace dynamic { + template class Allocator> + auto visit(Visitor &&, basic_variable< Allocator > &); + + // Immutable visitation. + template class Allocator> + auto visit(Visitor && visitor, + const basic_variable< Allocator > & variable); + } +} +---- + + +== Function template visit + +trial::dynamic::visit — Mutable visitation. + +=== Synopsis + +[source,cpp] +---- +// In header: + + +template class Allocator> + auto visit(Visitor && visitor, basic_variable< Allocator > & variable); +---- + +== Description + +Invokes the call operator on the visitor whose type matches the stored type of the dynamic variable. The type is resolved using the normal rules for C++ function overloading. All call operators must use the same return type. + +[source,cpp] +---- +struct visitor +{ + // Matches any stored type; returns nothing. + template void operator()(T); +}; + +dynamic::variable data = 1; +assert(data.same()); +dynamic::visit(visitor{}, data); // Invokes visitor.operator()(int) +---- + +[%autowidth, frame=none, grid=none] +|=== +| *Parameters:* | {space}  *variable* {space} Non-const dynamic variable + + {space}  *visitor* {space}    Visitor object. +| *Returns:* |{space} `Return type of Visitor::operator()(nullable).` +|=== diff --git a/doc/dynamic/rationale.qbk b/doc/modules/dynamic/pages/rationale.adoc similarity index 52% rename from doc/dynamic/rationale.qbk rename to doc/modules/dynamic/pages/rationale.adoc index 92d1897..5617980 100644 --- a/doc/dynamic/rationale.qbk +++ b/doc/modules/dynamic/pages/rationale.adoc @@ -1,36 +1,42 @@ -[/ - 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). +// +// +:C++: {cpp} -[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` -`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::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. +|=== +| `*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 {cpp} arithmetic operations. + +|`dynamic::string`, `dynamic::wstring`, `dynamic::u16string`, `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. + +Adheres to the `AssociativeContainer` concept with a `mapped_type`. +|=== + +`footnote:[The dynamic variable meets the requirements of the Container, ReversibleContainer, and DynamicContainer concepts.]Except [.blue]#signed char# and [.blue]#unsigned char# which are considered small integers.` + + -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`.]] -] 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 +44,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 xref:concept.adoc#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 [link dynamic-container DynamicContainer] concepts. +Meeting the requirements of the Container concept means that the dynamic variable can be used together with {cpp} 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. -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,44 +77,44 @@ 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. +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 {cpp} 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 {cpp} 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. -A consequence of using value iterators is that when sorting an associated array, only the values are sorted. In other words, values are moved between keys. For example, sorting `{ {"alpha", 20}, {"bravo", 10} }` becomes `{ {"alpha", 10}, {"bravo", 20} }`. This may make more sense if we regard an array as an associative array with the index as the key. For example, the array `{20, 10}` can be regarded as `{ {0, 20}, {1, 10} }`. Sorting this arrays results in `{ {0, 10}, {1, 20} }` which corresponds to `{10, 20}`. +A consequence of using value iterators is that when sorting an associated array, only the values are sorted. In other words, values are moved between keys. For example, sorting `{ {[.cyan]#"alpha"#, [.cyan]#20#}, {[.cyan]#"bravo"#, [.cyan]#10#} }` becomes `{ {[.cyan]#"alpha"#, [.cyan]#10#}, {[.cyan]#"bravo"#, [.cyan]#20#} }`. This may make more sense if we regard an array as an associative array with the index as the key. For example, the array `{[.cyan]#20#, [.cyan]#10#}` can be regarded as `{ {[.cyan]#0#, [.cyan]#20#}, {[.cyan]#1#, [.cyan]#10#} }`. Sorting this arrays results in `{ {[.cyan]#0#, [.cyan]#10#}, {[.cyan]#1#, [.cyan]#20#} }` which corresponds to `{[.cyan]#10#, [.cyan]#20#}`. The iterator has explicit methods to obtain both the key and the value by reference. 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. A custom allocator can be specified as a template parameter for the `dynamic::basic_variable` class. This allocator is passed to all string types, as well as `array_type` and `map_type`. -`dynamic::variable` is a convenience alias for `dynamic::basic_variable>`. +`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/type.adoc b/doc/modules/dynamic/pages/type.adoc new file mode 100644 index 0000000..1a56888 --- /dev/null +++ b/doc/modules/dynamic/pages/type.adoc @@ -0,0 +1,208 @@ +// +// 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 +:C++: {cpp} + += 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` | `xref:dynamic:header-variable.adoc#dynamic_variable[dynamic::nullable]` |No value. +|`[.blue]#bool#` | `xref:dynamic:header-variable.adoc#boolean[dynamic::boolean]` |Boolean value. +|`[.blue]#signed char#` | `xref:dynamic:header-variable.adoc#integer[dynamic::integer]` |Very short signed integer. +|`[.blue]#signed short int#` |`dynamic::integer` |Short signed integer. +|`[.blue]#signed int#` |`dynamic::integer` |Signed integer. +|`[.blue]#signed long int#` |`dynamic::integer` |Long signed integer. +|`[.blue]#signed long long int#` |`dynamic::integer` |Very long signed integer. +|`[.blue]#unsigned char#` |`dynamic::integer` |Very short unsigned integer. +|`[.blue]#unsigned short int#` |`dynamic::integer` |Short unsigned integer. +|`[.blue]#unsigned int#` |`dynamic::integer` |Unsigned integer. +|`[.blue]#unsigned long int#` |`dynamic::integer` |Long unsigned integer. +|`[.blue]#unsigned long long int#` |`dynamic::integer` |Very long unsigned integer. +|`[.blue]#float#` |`xref:dynamic:header-variable.adoc#real[dynamic::real]` |Short floating-point number. +|`[.blue]#double#` |`dynamic::real` |Floating-point number. +|`[.blue]#long double#` |`dynamic::real` |Long floating-point number. + +| `xref:dynamic:header-variable.adoc#basic_variable[dynamic::string_type]` | `xref:dynamic:header-variable.adoc#string[dynamic::string]` | Narrow-character string. Same as `std::string` by default. + +| `xref:dynamic:header-variable.adoc#basic_variable[dynamic::wstring_type]` | `xref:dynamic:header-variable.adoc#wstring[dynamic::wstring]` |Wide-character string. Same as `std::wstring` by default. + +| `xref:dynamic:header-variable.adoc#basic_variable[dynamic::u16string_type]` | `xref:dynamic:header-variable.adoc#u16string[dynamic::u16string]` |UTF-16 character string. Same as `std::u16string` by default. + +| `xref:dynamic:header-variable.adoc#basic_variable[dynamic::u32string_type]` | `xref:dynamic:header-variable.adoc#u32string[dynamic::u32string]` |UTF-32 character string. Same as `std::u32string` by default. + +| `xref:dynamic:header-variable.adoc#basic_variable[dynamic::array_type]` | `xref:dynamic:header-variable.adoc#dynamic_variable[dynamic::array]` |Sequence of zero or more `dynamic::variables`. + +| `xref:dynamic:header-variable.adoc#basic_variable[dynamic::map_type]` | `xref:dynamic:header-variable.adoc#dynamic_variable#[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 `xref:dynamic:header-variable#same_T[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 `xref:dynamic:header-variable#is_T[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 `xref:dynamic:header-variable#d_code[code()]` or `xref:dynamic:header-variable#d_symbol[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` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::null]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::null]` + +|`[.blue]#bool#` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::boolean]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::boolean]` + +|`[.blue]#signed char#` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::signed_char]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#signed short int#` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::signed_short_integer]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#signed int#` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::signed_integer]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#signed long int#` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::signed_long_integer]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#signed long long int#` |`xref:dynamic:header-token.adoc#struct_code[dynamic::code::signed_long_long_integer]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#unsigned char#` |`xref:dynamic:header-token.adoc#struct_code[dynamic::code::unsigned_char]` |`xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#unsigned short int#` |`xref:dynamic:header-token.adoc#struct_code[dynamic::code::unsigned_short_integer]` |`xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#unsigned int#` |`xref:dynamic:header-token.adoc#struct_code[dynamic::code::unsigned_integer]` |`xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#unsigned long int#` |`xref:dynamic:header-token.adoc#struct_code[dynamic::code::unsigned_long_integer]` |`xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#unsigned long long int#`| `xref:dynamic:header-token.adoc#struct_code[dynamic::code::unsigned_long_long_integer]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::integer]` + +|`[.blue]#float#` |`xref:dynamic:header-token.adoc#struct_code[dynamic::code::real]` |`xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::real]` + +|`[.blue]#double#` |`xref:dynamic:header-token.adoc#struct_code[dynamic::code::long_real]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::real]` + +|`[.blue]#long double#`| `xref:dynamic:header-token.adoc#struct_code[dynamic::code::long_long_real]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::real]` + +|`dynamic::string_type` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::string]` |`xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::string]` + +|`dynamic::wstring_type` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::wstring]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::wstring]` + +|`dynamic::u16string_type` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::u16string]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::u16string]` + +|`dynamic::u32string_type` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::u32string]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::u32string]` + +|`dynamic::array_type` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::array]` | `xref:dynamic:header-token.adoc#struct_symbol[dynamic::symbol::array]` + +|`dynamic::map_type` | `xref:dynamic:header-token.adoc#struct_code[dynamic::code::map]` | `xref:dynamic:header-token.adoc#struct_symbol[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 | `xref:dynamic:header-variable.adoc#dynamic_variable[dynamic::nullable]` |0 +|Arithmetic | `xref:dynamic:header-variable.adoc#boolean[dynamic::boolean]` +`xref:dynamic:header-variable.adoc#integer[dynamic::integer]` +`xref:dynamic:header-variable.adoc#real[dynamic::real]` |1 +|Narrow string | `xref:dynamic:header-variable.adoc#string[dynamic::string]` |2 +|Wide string | `xref:dynamic:header-variable.adoc#wstring[dynamic::wstring]` |3 +|UTF-16 string | `xref:dynamic:header-variable.adoc#u16string[dynamic::u16string]` |4 +|UTF-32 string | `xref:dynamic:header-variable.adoc#u32string[dynamic::u32string]` |5 +|Sequenced array | `xref:dynamic:header-variable.adoc#dynamic_variable[dynamic::array]` |6 +|Associative array | `xref:dynamic:header-variable.adoc#dynamic_variable[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 {cpp} 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 {cpp} 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/json/design.qbk b/doc/modules/json/pages/design-rationale.adoc similarity index 52% rename from doc/json/design.qbk rename to doc/modules/json/pages/design-rationale.adoc index 9982743..e1b768f 100644 --- a/doc/json/design.qbk +++ b/doc/modules/json/pages/design-rationale.adoc @@ -1,53 +1,53 @@ -[/ - 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). -] +:C++: {cpp} -[#protocol.json.design] -[section Design Rationale] += 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 +on it. If the JSON input should end up in your own {cpp} data structures, then the parse tree becomes an unnecessary intermediate step. In this case your program 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 xref:json:reader.adoc[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. +described in the Gang-of-Four book, instead of {cpp} 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`] +{cpp} does make that distinction, so xref:json:reader.adoc[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) if they contain a decimal-point or an exponent. Regardless of how a number was identified, it can be converted using `json::reader::value()` -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 +as either a {cpp} integer or floating-point number. +This means that integer numbers that are too big to fit into a {cpp} 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..e5ce922 --- /dev/null +++ b/doc/modules/json/pages/error.adoc @@ -0,0 +1,74 @@ +//// +// 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 https://en.cppreference.com/w/cpp/header/system_error.html[] +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/modules/json/pages/header-error.adoc b/doc/modules/json/pages/header-error.adoc new file mode 100644 index 0000000..a340a35 --- /dev/null +++ b/doc/modules/json/pages/header-error.adoc @@ -0,0 +1,66 @@ + +=== Header + +[source,cpp] +---- +namespace trial { + namespace protocol { + namespace json { + class error; + + 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 }; + const std::error_category & error_category(); + enum errc to_errc(token::code::value); + std::error_code make_error_code(json::errc e = no_error); + } + } +}namespace std { + template<> struct is_error_code_enum; +} +---- + +== Class error + +trial::protocol::json::error + +=== Synopsis + +[source,cpp] +---- +// In header: + + +class error : public system_error { +public: + // construct/copy/destruct + error(std::error_code); + error(enum errc); +}; +---- + +== Description + +=== error *public construct/copy/destruct* + +1. `error(std::error_code ec);` +2. `error([.blue]#enum# errc e);` + + +== Struct is_error_code_enum + +std::is_error_code_enum + +=== Synopsis + +[source,cpp] +---- +// In header: + + +struct is_error_code_enum : public true_type { +}; +---- + diff --git a/doc/modules/json/pages/header-iarchive.adoc b/doc/modules/json/pages/header-iarchive.adoc new file mode 100644 index 0000000..0b0173c --- /dev/null +++ b/doc/modules/json/pages/header-iarchive.adoc @@ -0,0 +1,68 @@ + +=== Header + +[source,cpp] +---- +namespace trial { + namespace protocol { + namespace json { + [.blue]#template#<[.blue]##typename## CharT> class basic_iarchive; + + typedef basic_iarchive< char > iarchive; + } + } +} +---- + +== Class template basic_iarchive + +trial::protocol::json::basic_iarchive + +== Synopsis + +[source,cpp] +---- +// In header: + +[.blue]#template#<[.blue]##typename## CharT> +class basic_iarchive { +public: + // types + typedef CharT value_type; + + // construct/copy/destruct + basic_iarchive(const json::reader &); + basic_iarchive(const json::reader::view_type &); + [.blue]#template#<[.blue]##typename## Iterator> basic_iarchive(Iterator, Iterator); + + // public member functions + [.blue]#template#<[.blue]##typename## T> void load_override(T &); + [.blue]#template#<[.blue]##typename## T> void load_override(T &, long); + [.blue]#template#<[.blue]##typename## Tag> void load(); + [.blue]#template#<[.blue]##typename## T> void load(T &); + [.blue]#template#<[.blue]##typename## Tag> bool at() const; + token::code::value code() const; + token::symbol::value symbol() const; + token::category::value category() const; +}; +---- + + +== Description + +=== basic_iarchive public construct/copy/destruct + +1. `basic_iarchive([.blue]#const# xref:header-reader.adoc[json::reader] &);` +2. `basic_iarchive([.blue]#const# json::reader::view_type &);` +3. `[.blue]#template#<[.blue]##typename## Iterator> basic_iarchive(Iterator begin, Iterator end);` + +=== basic_iarchive public member functions + +1. `[.blue]#template#<[.blue]##typename## T> [.blue]#void# load_override(T & data);` +2. `[.blue]#template#<[.blue]##typename## T> [.blue]#void# load_override(T & data, [.blue]#long#);` +3. `[.blue]#template#<[.blue]##typename## Tag> [.blue]#void# load();` +4. `[.blue]#template#<[.blue]##typename## T> [.blue]#void# load(T &);` +5. `[.blue]#template#<[.blue]##typename## Tag> [.blue]#bool# at() [.blue]#const#;` +6. `token::code::value code() [.blue]#const#;` +7. `token::symbol::value symbol() [.blue]#const#;` +8. `token::category::value category() [.blue]#const#;` diff --git a/doc/modules/json/pages/header-reader.adoc b/doc/modules/json/pages/header-reader.adoc new file mode 100644 index 0000000..b5c8d3c --- /dev/null +++ b/doc/modules/json/pages/header-reader.adoc @@ -0,0 +1,171 @@ +:cpp: C++ +:space:       + +=== Header + +[source,cpp] +---- +namespace trial { + namespace protocol { + namespace json { + template class basic_reader; + + typedef basic_reader< char > reader; + } + } +} +---- + +[[basic_reader]] +== Class template basic_reader + +trial::protocol::json::basic_reader — Incremental JSON reader. + + +== Synopsis + +[source,cpp] +---- +// In header: + +template +class basic_reader { +public: + // types + typedef unspecified value_type; + typedef unspecified size_type; + typedef unspecified view_type; + + // construct/copy/destruct + basic_reader(const view_type &); + basic_reader(const basic_reader &); + + // public member functions + bool next(); + bool next(token::code::value); + size_type level() const noexcept; + token::code::value code() const noexcept; + token::symbol::value symbol() const noexcept; + token::category::value category() const noexcept; + std::error_code error() const noexcept; + template ReturnType value() const; + const view_type & literal() const noexcept; + const view_type & tail() const noexcept; +}; +---- + +== Description + +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. + +=== basic_reader public construct/copy/destruct + +1. `basic_reader([.blue]#const# view_type & view);` + +Construct an incremental JSON reader. + +The first token is automatically parsed. + +The reader does not assume ownership of the view. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Parameters:* | {space} *view* {space} A string view of a JSON formatted buffer. +|=== + +2. `basic_reader([.blue]#const# xref:json:header-reader.adoc#basic_reader[basic_reader] & other);` + +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. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Parameters:* | {space} *other* {space} The reader that is copied. +|=== + +=== basic_reader public member functions + +1. `[.blue]#bool# next();` + +Parse the next token. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} false if an error occurred or end-of-input was reached, true otherwise. +|=== + +2. `[.blue]#bool# next(token::code::value expect);` + +Parse the next token if current token has a given value. + ++ +[%autowidth, frame=none, grid=none] +|=== +| *Parameters:* | {space} *expect* {space} Expected value of current token. +| *Returns:* | {space} false if current token does not have the expected value. +|=== + +3. `size_type level() [.blue]#const noexcept#`; + +Get the current nesting level. + +Keep track of the nesting level of containers. The outermost root level is 0. + ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} The current nesting level. +|=== + +4. `token::code::value code() [.blue]#const noexcept#;` + +Get the current token as a detailed code. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} The code of the current token. +|=== + +5. `token::symbol::value symbol() [.blue]#const noexcept;#` + +Get the current token as a symbol. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} The symbol of the current token. +|=== + +6. `token::category::value category() [.blue]#const noexcept#;` + +Get the current token as a category. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} The category of the current token. +|=== + +7. `std::error_code error() [.blue]#const noexcept#`; + +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. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} The current error code. +|=== + + +8. `[.blue]#template#<[.blue]##typename## ReturnType> ReturnType value() [.blue]#const#;` + +Converts the current value into ReturnType. + +The following conversions are valid: +a. Convert a symbol::boolean token into bool. +b. Convert a symbol::integer token into an integral {cpp} type (expect bool.) +c. Convert a symbol::real token into a floating-point {cpp} type. +d. Convert a symbol::string token into `std::string.` ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} The converted value. +| *Throws:* | {space} xref:json:error.adoc[json::error] If requested type is incompatible with the current token. +|=== + +9. `[.blue]#const# view_type & literal() [.blue]#const noexcept#`; ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} A view of the current value before it is converted into its type. +|=== + + +10. `[.blue]#const# view_type & tail() [.blue]#const noexcept#`; ++ +[%autowidth, frame=none, grid=none] +|=== +| *Returns:* | {space} A view of the remaining buffer. +|=== diff --git a/doc/modules/json/pages/header-writer.adoc b/doc/modules/json/pages/header-writer.adoc new file mode 100644 index 0000000..f4afbaa --- /dev/null +++ b/doc/modules/json/pages/header-writer.adoc @@ -0,0 +1,48 @@ +:space:       + +=== Header + +[source,cpp] +---- +namespace trial { + namespace protocol { + namespace json { + template + class basic_writer; + + typedef basic_writer< char > writer; + } + } +} +---- + + +== Class template basic_writer + +trial::protocol::json::basic_writer — Incremental JSON writer. + +== Description + +Generate JSON output incrementally by appending C++ data. + +== basic_writer public construct/copy/destruct + +1. `[.blue]#template#<[.blue]##typename## T> basic_writer(T & buffer);` + +Construct an incremental JSON writer. + +The buffer type can be any for which a buffer wrapper exists. ++ +[%autowidth, frame=none, grid=none] +|=== +| *Parameters:* | {space} *buffer* {space} A buffer where the JSON formatted output is stored. +|=== + +== basic_writer public member functions + +1. `std::error_code error() [.blue]#const noexcept#;` +2. `size_type level() [.blue]#const noexcept#;` +3. `[.blue]#template#<[.blue]##typename## T> size_type value();` + +Write structural output. +4. `[.blue]#template#<[.blue]##typename## T> size_type value(T && value);` + +Write data output. +5. `size_type literal([.blue]#const# view_type &) [.blue]#noexcept#;` + +Write raw output. \ No newline at end of file diff --git a/doc/json/iarchive.qbk b/doc/modules/json/pages/iarchive.adoc similarity index 53% rename from doc/json/iarchive.qbk rename to doc/modules/json/pages/iarchive.adoc index 477a817..460df9a 100644 --- a/doc/json/iarchive.qbk +++ b/doc/modules/json/pages/iarchive.adoc @@ -1,26 +1,27 @@ -[/ - 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.iarchive] -[section Input Archive] - -Deserialization of JSON input directly into C++ data structures is done with +// +// 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). +// +:C++: {cpp} + += Input Archive + +Deserialization of JSON input directly into {cpp} data structures is done with the help of Boost.Serialization. -The input archive transforms the entire JSON input into C++ data structures all +The input archive transforms the entire JSON input into {cpp} data structures all 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++. +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 {cpp}. -[/ Intrusive vs non-intrusive] +// Intrusive vs non-intrusive -[heading Simple types] +== Simple types -``` +[source,cpp] +---- #include #include @@ -30,13 +31,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 +48,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-overview.adoc b/doc/modules/json/pages/json-overview.adoc new file mode 100644 index 0000000..86082db --- /dev/null +++ b/doc/modules/json/pages/json-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). +// + += 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 | xref:json:reader.adoc[json::reader] | xref:json:writer.adoc[json::writer] +|Serialization| xref:json:iarchive.adoc[json::iarchive] | xref:json:oarchive.adoc[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..bb18b98 --- /dev/null +++ b/doc/modules/json/pages/overview.adoc @@ -0,0 +1,28 @@ +// +// 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). +// +:C++: {cpp} + += 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 xref:json:user-guide.adoc[incremental generator]. +|*Serialization* |The JSON input can be deserialized directly into our {cpp} data structures with an xref:json:iarchive.adoc[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 {cpp} data structures can be serialized directly into a JSON output with an xref:json:oarchive.adoc[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 68% rename from doc/json/reader.qbk rename to doc/modules/json/pages/reader.adoc index 5ae96c1..98fb118 100644 --- a/doc/json/reader.qbk +++ b/doc/modules/json/pages/reader.adoc @@ -1,34 +1,34 @@ -[/ - 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). +// +:C++: {cpp} - 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. +JSON input into a sequence of {cpp} 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 xref:json:token.adoc[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. +|`[blue]#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 +36,30 @@ 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 +75,124 @@ 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()`. -``` +requested by `reader::value<[blue]##bool##>()`. + +[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] +---- -While JSON does not distinguish between integer and floating-point numbers, C++ -does make this distinction and therefore __protocol__ does too. +== Number + +While JSON does not distinguish between integer and floating-point numbers, {cpp} +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 -it may result in loss of information.] + +[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 +206,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++. +can be thought of as a `std::map` in {cpp}. -[endsect] diff --git a/doc/modules/json/pages/token.adoc b/doc/modules/json/pages/token.adoc new file mode 100644 index 0000000..a1f628d --- /dev/null +++ b/doc/modules/json/pages/token.adoc @@ -0,0 +1,95 @@ +// +// 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. +|`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,{empty}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 +xref:json:error.adoc[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` +|`null`|`nullable` +|`begin_array`|`structural` +|`end_array`|`structural` +|`begin_object`|`structural` +|`end_object`|`structural` +|`separator`|`structural` +|`end`|`status` +|`error`|`status` +|=== + diff --git a/doc/modules/json/pages/tree-processing.adoc b/doc/modules/json/pages/tree-processing.adoc new file mode 100644 index 0000000..bf6c390 --- /dev/null +++ b/doc/modules/json/pages/tree-processing.adoc @@ -0,0 +1 @@ += Tree processing diff --git a/doc/json/tutorial.qbk b/doc/modules/json/pages/tutorials.adoc similarity index 72% rename from doc/json/tutorial.qbk rename to doc/modules/json/pages/tutorials.adoc index 5461a71..23a51e4 100644 --- a/doc/json/tutorial.qbk +++ b/doc/modules/json/pages/tutorials.adoc @@ -1,49 +1,65 @@ -[/ - 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). -] +:cpp: C++ -[section:tutorial Tutorials] += 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 xref:json:oarchive.adoc[JSON output archive] and the xref:json:iarchive.adoc[JSON input archive] can be used to serialize {cpp} data into JSON and deserialize JSON into {cpp} 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 xref:core:adapter.adoc[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,l_lines="1"] +---- + #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 xref:json:oarchive.adoc[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 +67,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 xref:json:iarchive.adoc[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, +There is also built-in support for serialization of certain standard {cpp} 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 +110,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,61 +123,69 @@ 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 such as searching for keys in a JSON file without having to convert strings or -from JSON into C++ types. +from JSON into {cpp} 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 xref:json:writer.adoc[json::writer]. +Afterwards we will use the xref:json:reader.adoc[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 xref:json:writer.adoc[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 +209,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. xref:json:reader.adoc[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 +223,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 +243,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 +251,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 +278,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 xref:json:reader.adoc[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 xref:json:reader.adoc[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 xref:json:reader.adoc[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 +294,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.{empty}footnote:[We could also have used a polymorphic interface for the callback functions.] -``` +[source,cpp] +---- #include template @@ -291,11 +319,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 +342,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 +409,15 @@ void push_parser::parse() } while (reader.next()); } -``` +---- Finally, we use the above push parser as follows: -``` + +[source,cpp] +---- json::reader reader("[null,true,42]"); // Replace with actual JSON input push_parser parser(reader); parser.parse(); -``` +---- -[endsect] -[endsect] diff --git a/doc/modules/json/pages/user-guide.adoc b/doc/modules/json/pages/user-guide.adoc new file mode 100644 index 0000000..c8f7a59 --- /dev/null +++ b/doc/modules/json/pages/user-guide.adoc @@ -0,0 +1,18 @@ +// +// 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). +// + += User Guide + +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 xref:json:token.adoc[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 document processing, but even though incremental processing is more laborious to use, it covers more use cases and can be used more efficiently. Some examples of these use cases are: + +* The input and output serialization archives are build on top of incremental parser and generators. +* Certain operations on JSON buffers does not require that individual JSON elements, such as JSON escaped strings, are converted into in-memory data structures. For instance, searching for data in a JSON file, or pretty-printing the content of a JSON file. +* We can build other parser types from an incremental parser, such as the push parser in the tutorials. + diff --git a/doc/json/writer.qbk b/doc/modules/json/pages/writer.adoc similarity index 60% rename from doc/json/writer.qbk rename to doc/modules/json/pages/writer.adoc index 500dd3d..b321caa 100644 --- a/doc/json/writer.qbk +++ b/doc/modules/json/pages/writer.adoc @@ -1,27 +1,28 @@ -[/ - 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.writer] -[section 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. +// +// 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). +// +:C++: {cpp} + += Writer + +Writer is an incremental generator that outputs {cpp} data types in a JSON format. +The output is generated piece by piece as {cpp} 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([.blue]#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 +36,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{empty}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)`. -``` +Boolean values are output via `writer::value([blue]#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 +107,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 +123,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/modules/nav.adoc b/doc/modules/nav.adoc new file mode 100644 index 0000000..3ae5caa --- /dev/null +++ b/doc/modules/nav.adoc @@ -0,0 +1,40 @@ +* Core +** xref:core:adapter.adoc[Adapter] +** xref:core:adapter-traits.adoc[Adapter Traits] +** xref:core:serialization.adoc[Serialization] +* JSON +** xref:json:json-overview.adoc[Overview] +** xref:json:tutorials.adoc[Tutorials] +** Guide +*** xref:json:user-guide.adoc[User Guide] +*** 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:tree-processing.adoc[] +** xref:json:design-rationale.adoc[] +** JSON Reference +*** xref:json:header-error.adoc[error.hpp] +*** xref:json:header-reader.adoc[reader.hpp] +*** xref:json:header-iarchive.adoc[iarchive.hpp] +*** xref:json:header-writer.adoc[write.hpp] +* Dynamic Variable +** xref:dynamic:dynamic.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[] +** Dynamic Reference +*** xref:dynamic:header-visit.adoc[visit.hpp] +*** xref:dynamic:header-error.adoc[error.hpp] +*** xref:dynamic:header-functional.adoc[functional.hpp] +*** xref:dynamic:header-token.adoc[token.hpp] +*** xref:dynamic:header-variable.adoc[variable.hpp] 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..b51828c --- /dev/null +++ b/supplemental-ui/partials/header-content.hbs @@ -0,0 +1,102 @@ + + + +
+
+ + + + +{{#if (eq page.url "/Trial.Protocol/v1.0/dynamic/header-variable.html")}} + +{{/if}} + + +{{#if (eq page.url "/Trial.Protocol/v1.0/dynamic/type.html")}} + +{{/if}} + + + + + + + + + + + + + + +