mirror of
https://github.com/biojppm/rapidyaml.git
synced 2026-01-18 13:31:19 +01:00
Add ReadTheDocs documentation
This commit is contained in:
2
.github/workflows/clang.yml
vendored
2
.github/workflows/clang.yml
vendored
@@ -201,7 +201,7 @@ jobs:
|
|||||||
- {std: 20, cxx: clang++-10, bt: Release, vg: ON, san: ALL, bitlinks: static32, os: ubuntu-20.04}
|
- {std: 20, cxx: clang++-10, bt: Release, vg: ON, san: ALL, bitlinks: static32, os: ubuntu-20.04}
|
||||||
env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"}
|
env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"}
|
||||||
steps:
|
steps:
|
||||||
- {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}}
|
- {name: checkout, uses: actions/checkout@v4, with: {submodules: recursive}}
|
||||||
- {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS}
|
- {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS}
|
||||||
- {name: show info, run: source .github/setenv.sh && c4_show_info}
|
- {name: show info, run: source .github/setenv.sh && c4_show_info}
|
||||||
- name: shared64-configure---------------------------------------------------
|
- name: shared64-configure---------------------------------------------------
|
||||||
|
|||||||
41
.readthedocs.yaml
Normal file
41
.readthedocs.yaml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# .readthedocs.yaml
|
||||||
|
# Read the Docs configuration file
|
||||||
|
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||||
|
|
||||||
|
# Required
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
# Set the OS, Python version and other tools you might need
|
||||||
|
build:
|
||||||
|
os: ubuntu-22.04
|
||||||
|
tools:
|
||||||
|
python: "3.12"
|
||||||
|
# You can also specify other tool versions:
|
||||||
|
# nodejs: "19"
|
||||||
|
# rust: "1.64"
|
||||||
|
# golang: "1.19"
|
||||||
|
commands:
|
||||||
|
- pip install -r doc/requirements.txt
|
||||||
|
- make -C doc html
|
||||||
|
- mkdir -p _readthedocs
|
||||||
|
- mv -v doc/_build/html _readthedocs/
|
||||||
|
|
||||||
|
# Build documentation in the "docs/" directory with Sphinx
|
||||||
|
#sphinx:
|
||||||
|
# configuration: doc/conf.py
|
||||||
|
|
||||||
|
# Optionally build your docs in additional formats such as PDF and ePub
|
||||||
|
# formats:
|
||||||
|
# - pdf
|
||||||
|
# - epub
|
||||||
|
|
||||||
|
# Optional but recommended, declare the Python requirements required
|
||||||
|
# to build your documentation
|
||||||
|
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||||
|
python:
|
||||||
|
install:
|
||||||
|
- requirements: doc/requirements.txt
|
||||||
|
|
||||||
|
submodules:
|
||||||
|
include: all
|
||||||
|
recursive: true
|
||||||
456
README.md
456
README.md
@@ -1,10 +1,11 @@
|
|||||||
# Rapid YAML
|
# Rapid YAML
|
||||||
[](https://github.com/biojppm/rapidyaml/blob/master/LICENSE.txt)
|
[](https://github.com/biojppm/rapidyaml/blob/master/LICENSE.txt)
|
||||||
[](https://github.com/biojppm/rapidyaml/releases)
|
[](https://github.com/biojppm/rapidyaml/releases)
|
||||||
|
[](https://rapidyaml.readthedocs.io/en/latest/?badge=latest)
|
||||||
|
|
||||||
[](https://pypi.org/project/rapidyaml/)
|
[](https://pypi.org/project/rapidyaml/)
|
||||||
[](https://gitter.im/rapidyaml/community)
|
[](https://gitter.im/rapidyaml/community)
|
||||||
|
|
||||||
[](https://github.com/biojppm/rapidyaml/actions)
|
|
||||||
<!-- [](https://coveralls.io/github/biojppm/rapidyaml) -->
|
<!-- [](https://coveralls.io/github/biojppm/rapidyaml) -->
|
||||||
[](https://codecov.io/gh/biojppm/rapidyaml)
|
[](https://codecov.io/gh/biojppm/rapidyaml)
|
||||||
|
|
||||||
@@ -33,9 +34,9 @@ build->install->`find_package()`) or together with your project (ie with
|
|||||||
ryml can use custom global and per-tree memory allocators and error
|
ryml can use custom global and per-tree memory allocators and error
|
||||||
handler callbacks, and is exception-agnostic. ryml provides a default
|
handler callbacks, and is exception-agnostic. ryml provides a default
|
||||||
implementation for the allocator (using `std::malloc()`) and error
|
implementation for the allocator (using `std::malloc()`) and error
|
||||||
handlers (using using `std::abort()` is provided, but you can opt out
|
handlers (using using either exceptions, `longjmp()` or
|
||||||
and provide your own memory allocation and eg, exception-throwing
|
`std::abort()`), but you can opt out and provide your own memory
|
||||||
callbacks.
|
allocation and eg, exception-throwing callbacks.
|
||||||
|
|
||||||
ryml does not depend on the STL, ie, it does not use any std container
|
ryml does not depend on the STL, ie, it does not use any std container
|
||||||
as part of its data structures), but it can serialize and deserialize
|
as part of its data structures), but it can serialize and deserialize
|
||||||
@@ -49,18 +50,19 @@ ryml is written in C++11, and compiles cleanly with:
|
|||||||
* g++ 4.8 and later
|
* g++ 4.8 and later
|
||||||
* Intel Compiler
|
* Intel Compiler
|
||||||
|
|
||||||
|
ryml's API documentation is [available at
|
||||||
|
ReadTheDocs](https://rapidyaml.readthedocs.io/en/latest/).
|
||||||
|
|
||||||
ryml is [extensively unit-tested in Linux, Windows and
|
ryml is [extensively unit-tested in Linux, Windows and
|
||||||
MacOS](https://github.com/biojppm/rapidyaml/actions). The tests cover
|
MacOS](https://github.com/biojppm/rapidyaml/actions). The tests cover
|
||||||
x64, x86, wasm (emscripten), arm, aarch64, ppc64le and s390x
|
x64, x86, wasm (emscripten), arm, aarch64, ppc64le and s390x
|
||||||
architectures, and include analysing ryml with:
|
architectures, and include analysing ryml with:
|
||||||
* valgrind
|
* valgrind
|
||||||
* clang-tidy
|
* clang-tidy
|
||||||
* clang sanitizers:
|
* gcc/clang sanitizers:
|
||||||
* memory
|
* memory
|
||||||
* address
|
* address
|
||||||
* undefined behavior
|
* undefined behavior
|
||||||
* thread
|
|
||||||
* [LGTM.com](https://lgtm.com/projects/g/biojppm/rapidyaml)
|
|
||||||
|
|
||||||
ryml also [runs in
|
ryml also [runs in
|
||||||
bare-metal](https://github.com/biojppm/rapidyaml/issues/193), and
|
bare-metal](https://github.com/biojppm/rapidyaml/issues/193), and
|
||||||
@@ -219,9 +221,10 @@ workflow in the
|
|||||||
CI](https://github.com/biojppm/rapidyaml/actions/workflows/benchmarks.yml)
|
CI](https://github.com/biojppm/rapidyaml/actions/workflows/benchmarks.yml)
|
||||||
to scroll through the results for yourself.
|
to scroll through the results for yourself.
|
||||||
|
|
||||||
Also, if you have a case where ryml behaves very nicely or not as nicely as
|
Also, if you have a case where ryml behaves very nicely or not as
|
||||||
claimed above, we would definitely like to see it! Please submit a pull request
|
nicely as claimed above, we would definitely like to see it! Please
|
||||||
adding the file to [bm/cases](bm/cases), or just send us the files.
|
open an issue, or submit a pull request adding the file to
|
||||||
|
[bm/cases](bm/cases), or just send us the files.
|
||||||
|
|
||||||
|
|
||||||
------
|
------
|
||||||
@@ -234,43 +237,19 @@ rapid is definitely NOT the same as being unpractical, so ryml was
|
|||||||
written with easy AND efficient usage in mind, and comes with a two
|
written with easy AND efficient usage in mind, and comes with a two
|
||||||
level API for accessing and traversing the data tree.
|
level API for accessing and traversing the data tree.
|
||||||
|
|
||||||
The following snippet is a quick overview taken from [the quickstart
|
The following snippet is a very quick overview taken from quickstart
|
||||||
sample](samples/quickstart.cpp). After cloning ryml (don't forget the
|
sample ([see on
|
||||||
`--recursive` flag for git), you can very
|
doxygen](https://rapidyaml.readthedocs.io/en/latest/group__doc__quickstart.html)/[see
|
||||||
easily build and run this executable using any of the build samples,
|
on github](samples/quickstart.cpp). After cloning ryml
|
||||||
eg the [`add_subdirectory()` sample](samples/add_subdirectory/).
|
(don't forget the `--recursive` flag for git), you can very easily
|
||||||
|
build and run this executable using any of the build samples, eg the
|
||||||
|
[`add_subdirectory()` sample](samples/add_subdirectory/) (see [the relevant section](#quickstart-samples)).
|
||||||
|
|
||||||
```c++
|
```cpp
|
||||||
// Parse YAML code in place, potentially mutating the buffer:
|
// Parse YAML code in place, potentially mutating the buffer:
|
||||||
char yml_buf[] = "{foo: 1, bar: [2, 3], john: doe}";
|
char yml_buf[] = "{foo: 1, bar: [2, 3], john: doe}";
|
||||||
ryml::Tree tree = ryml::parse_in_place(yml_buf);
|
ryml::Tree tree = ryml::parse_in_place(yml_buf);
|
||||||
|
|
||||||
// The resulting tree contains only views to the parsed string. If
|
|
||||||
// the string was parsed in place, then the string must outlive
|
|
||||||
// the tree! This works in this case because `yml_buf` and `tree`
|
|
||||||
// live on the same scope, so have the same lifetime.
|
|
||||||
|
|
||||||
// It is also possible to:
|
|
||||||
//
|
|
||||||
// - parse a read-only buffer using parse_in_arena(). This
|
|
||||||
// copies the YAML buffer to the tree's arena, and spares the
|
|
||||||
// headache of the string's lifetime.
|
|
||||||
//
|
|
||||||
// - reuse an existing tree (advised)
|
|
||||||
//
|
|
||||||
// - reuse an existing parser (advised)
|
|
||||||
//
|
|
||||||
// Note: it will always be significantly faster to parse in place
|
|
||||||
// and reuse tree+parser.
|
|
||||||
//
|
|
||||||
// Below you will find samples that show how to achieve reuse; but
|
|
||||||
// please note that for brevity and clarity, many of the examples
|
|
||||||
// here are parsing in the arena, and not reusing tree or parser.
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
|
||||||
// API overview
|
|
||||||
|
|
||||||
// ryml has a two-level API:
|
// ryml has a two-level API:
|
||||||
//
|
//
|
||||||
// The lower level index API is based on the indices of nodes,
|
// The lower level index API is based on the indices of nodes,
|
||||||
@@ -288,191 +267,14 @@ ryml::ConstNodeRef bar = tree["bar"];
|
|||||||
CHECK(root.is_map());
|
CHECK(root.is_map());
|
||||||
CHECK(bar.is_seq());
|
CHECK(bar.is_seq());
|
||||||
|
|
||||||
// A node ref is a lightweight handle to the tree and associated id:
|
// The resulting tree stores only string views to the YAML source buffer.
|
||||||
CHECK(root.tree() == &tree); // a node ref points at its tree, WITHOUT refcount
|
CHECK(root["foo"] == "1");
|
||||||
CHECK(root.id() == root_id); // a node ref's id is the index of the node
|
CHECK(root["foo"].key().str == yml_buf + 1);
|
||||||
CHECK(bar.id() == bar_id); // a node ref's id is the index of the node
|
CHECK(bar[0] == "2");
|
||||||
|
CHECK(root["john"] == "doe");
|
||||||
// The node API translates very cleanly to the index API, so most
|
|
||||||
// of the code examples below are using the node API.
|
|
||||||
|
|
||||||
// WARNING. A node ref holds a raw pointer to the tree. Care must
|
|
||||||
// be taken to ensure the lifetimes match, so that a node will
|
|
||||||
// never access the tree after the goes out of scope.
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
|
||||||
// To read the parsed tree
|
|
||||||
|
|
||||||
// ConstNodeRef::operator[] does a lookup, is O(num_children[node]).
|
|
||||||
CHECK(tree["foo"].is_keyval());
|
|
||||||
CHECK(tree["foo"].val() == "1"); // get the val of a node (must be leaf node, otherwise it is a container and has no val)
|
|
||||||
CHECK(tree["foo"].key() == "foo"); // get the key of a node (must be child of a map, otherwise it has no key)
|
|
||||||
CHECK(tree["bar"].is_seq());
|
|
||||||
CHECK(tree["bar"].has_key());
|
|
||||||
CHECK(tree["bar"].key() == "bar");
|
|
||||||
// maps use string keys, seqs use index keys:
|
|
||||||
CHECK(tree["bar"][0].val() == "2");
|
|
||||||
CHECK(tree["bar"][1].val() == "3");
|
|
||||||
CHECK(tree["john"].val() == "doe");
|
|
||||||
// An index key is the position of the child within its parent,
|
|
||||||
// so even maps can also use int keys, if the key position is
|
|
||||||
// known.
|
|
||||||
CHECK(tree[0].id() == tree["foo"].id());
|
|
||||||
CHECK(tree[1].id() == tree["bar"].id());
|
|
||||||
CHECK(tree[2].id() == tree["john"].id());
|
|
||||||
// Tree::operator[](int) searches a ***root*** child by its position.
|
|
||||||
CHECK(tree[0].id() == tree["foo"].id()); // 0: first child of root
|
|
||||||
CHECK(tree[1].id() == tree["bar"].id()); // 1: second child of root
|
|
||||||
CHECK(tree[2].id() == tree["john"].id()); // 2: third child of root
|
|
||||||
// NodeRef::operator[](int) searches a ***node*** child by its position:
|
|
||||||
CHECK(bar[0].val() == "2"); // 0 means first child of bar
|
|
||||||
CHECK(bar[1].val() == "3"); // 1 means second child of bar
|
|
||||||
// NodeRef::operator[](string):
|
|
||||||
// A string key is the key of the node: lookup is by name. So it
|
|
||||||
// is only available for maps, and it is NOT available for seqs,
|
|
||||||
// since seq members do not have keys.
|
|
||||||
CHECK(tree["foo"].key() == "foo");
|
|
||||||
CHECK(tree["bar"].key() == "bar");
|
|
||||||
CHECK(tree["john"].key() == "john");
|
|
||||||
CHECK(bar.is_seq());
|
|
||||||
// CHECK(bar["BOOM!"].is_seed()); // error, seqs do not have key lookup
|
|
||||||
|
|
||||||
// Note that maps can also use index keys as well as string keys:
|
|
||||||
CHECK(root["foo"].id() == root[0].id());
|
|
||||||
CHECK(root["bar"].id() == root[1].id());
|
|
||||||
CHECK(root["john"].id() == root[2].id());
|
|
||||||
|
|
||||||
// IMPORTANT. The ryml tree uses an index-based linked list for
|
|
||||||
// storing children, so the complexity of
|
|
||||||
// `Tree::operator[csubstr]` and `Tree::operator[size_t]` is O(n),
|
|
||||||
// linear on the number of root children. If you use
|
|
||||||
// `Tree::operator[]` with a large tree where the root has many
|
|
||||||
// children, you will see a performance hit.
|
|
||||||
//
|
|
||||||
// To avoid this hit, you can create your own accelerator
|
|
||||||
// structure. For example, before doing a lookup, do a single
|
|
||||||
// traverse at the root level to fill an `map<csubstr,size_t>`
|
|
||||||
// mapping key names to node indices; with a node index, a lookup
|
|
||||||
// (via `Tree::get()`) is O(1), so this way you can get O(log n)
|
|
||||||
// lookup from a key. (But please do not use `std::map` if you
|
|
||||||
// care about performance; use something else like a flat map or
|
|
||||||
// sorted vector).
|
|
||||||
//
|
|
||||||
// As for node refs, the difference from `NodeRef::operator[]` and
|
|
||||||
// `ConstNodeRef::operator[]` to `Tree::operator[]` is that the
|
|
||||||
// latter refers to the root node, whereas the former are invoked
|
|
||||||
// on their target node. But the lookup process works the same for
|
|
||||||
// both and their algorithmic complexity is the same: they are
|
|
||||||
// both linear in the number of direct children. But of course,
|
|
||||||
// depending on the data, that number may be very different from
|
|
||||||
// one to another.
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
|
||||||
// Hierarchy:
|
|
||||||
|
|
||||||
{
|
|
||||||
ryml::ConstNodeRef foo = root.first_child();
|
|
||||||
ryml::ConstNodeRef john = root.last_child();
|
|
||||||
CHECK(tree.size() == 6); // O(1) number of nodes in the tree
|
|
||||||
CHECK(root.num_children() == 3); // O(num_children[root])
|
|
||||||
CHECK(foo.num_siblings() == 3); // O(num_children[parent(foo)])
|
|
||||||
CHECK(foo.parent().id() == root.id()); // parent() is O(1)
|
|
||||||
CHECK(root.first_child().id() == root["foo"].id()); // first_child() is O(1)
|
|
||||||
CHECK(root.last_child().id() == root["john"].id()); // last_child() is O(1)
|
|
||||||
CHECK(john.first_sibling().id() == foo.id());
|
|
||||||
CHECK(foo.last_sibling().id() == john.id());
|
|
||||||
// prev_sibling(), next_sibling(): (both are O(1))
|
|
||||||
CHECK(foo.num_siblings() == root.num_children());
|
|
||||||
CHECK(foo.prev_sibling().id() == ryml::NONE); // foo is the first_child()
|
|
||||||
CHECK(foo.next_sibling().key() == "bar");
|
|
||||||
CHECK(foo.next_sibling().next_sibling().key() == "john");
|
|
||||||
CHECK(foo.next_sibling().next_sibling().next_sibling().id() == ryml::NONE); // john is the last_child()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
|
||||||
// Iterating:
|
|
||||||
{
|
|
||||||
ryml::csubstr expected_keys[] = {"foo", "bar", "john"};
|
|
||||||
// iterate children using the high-level node API:
|
|
||||||
{
|
|
||||||
size_t count = 0;
|
|
||||||
for(ryml::ConstNodeRef const& child : root.children())
|
|
||||||
CHECK(child.key() == expected_keys[count++]);
|
|
||||||
}
|
|
||||||
// iterate siblings using the high-level node API:
|
|
||||||
{
|
|
||||||
size_t count = 0;
|
|
||||||
for(ryml::ConstNodeRef const& child : root["foo"].siblings())
|
|
||||||
CHECK(child.key() == expected_keys[count++]);
|
|
||||||
}
|
|
||||||
// iterate children using the lower-level tree index API:
|
|
||||||
{
|
|
||||||
size_t count = 0;
|
|
||||||
for(size_t child_id = tree.first_child(root_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
|
|
||||||
CHECK(tree.key(child_id) == expected_keys[count++]);
|
|
||||||
}
|
|
||||||
// iterate siblings using the lower-level tree index API:
|
|
||||||
// (notice the only difference from above is in the loop
|
|
||||||
// preamble, which calls tree.first_sibling(bar_id) instead of
|
|
||||||
// tree.first_child(root_id))
|
|
||||||
{
|
|
||||||
size_t count = 0;
|
|
||||||
for(size_t child_id = tree.first_sibling(bar_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
|
|
||||||
CHECK(tree.key(child_id) == expected_keys[count++]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
|
||||||
// Gotchas:
|
|
||||||
|
|
||||||
// ryml uses assertions to prevent you from trying to obtain
|
|
||||||
// things that do not exist. For example:
|
|
||||||
|
|
||||||
{
|
|
||||||
ryml::ConstNodeRef seq_node = tree["bar"];
|
|
||||||
ryml::ConstNodeRef val_node = seq_node[0];
|
|
||||||
|
|
||||||
CHECK(seq_node.is_seq()); // seq is a container
|
|
||||||
CHECK(!seq_node.has_val()); // ... so it has no val
|
|
||||||
//CHECK(seq_node.val() == BOOM!); // ... so attempting to get a val is undefined behavior
|
|
||||||
|
|
||||||
CHECK(val_node.parent() == seq_node); // belongs to a seq
|
|
||||||
CHECK(!val_node.has_key()); // ... so it has no key
|
|
||||||
//CHECK(val_node.key() == BOOM!); // ... so attempting to get a key is undefined behavior
|
|
||||||
|
|
||||||
CHECK(val_node.is_val()); // this node is a val
|
|
||||||
//CHECK(val_node.first_child() == BOOM!); // ... so attempting to get a child is undefined behavior
|
|
||||||
|
|
||||||
// assertions are also present in methods that /may/ read the val:
|
|
||||||
CHECK(seq_node.is_seq()); // seq is a container
|
|
||||||
//CHECK(seq_node.val_is_null() BOOM!); // so cannot get the val to check
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// By default, assertions are enabled unless the NDEBUG macro is
|
|
||||||
// defined (which happens in release builds).
|
|
||||||
//
|
|
||||||
// This adheres to the pay-only-for-what-you-use philosophy: if
|
|
||||||
// you are sure that your intent is correct, why would you need to
|
|
||||||
// pay the runtime cost for the assertions?
|
|
||||||
//
|
|
||||||
// The downside, of course, is that when you are not sure, release
|
|
||||||
// builds may be doing something crazy.
|
|
||||||
//
|
|
||||||
// So you can override this behavior and enable/disable
|
|
||||||
// assertions, by defining the macro RYML_USE_ASSERT to a proper
|
|
||||||
// value (see c4/yml/common.hpp).
|
|
||||||
//
|
|
||||||
// Also, to be clear, this does not apply to parse errors
|
|
||||||
// happening when the YAML is parsed. Checking for these errors is
|
|
||||||
// always enabled and cannot be switched off.
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
// To get actual values, you need to deserialize the nodes.
|
||||||
// Deserializing: use operator>>
|
// Deserializing: use operator>>
|
||||||
{
|
{
|
||||||
int foo = 0, bar0 = 0, bar1 = 0;
|
int foo = 0, bar0 = 0, bar1 = 0;
|
||||||
@@ -481,7 +283,7 @@ CHECK(root["john"].id() == root[2].id());
|
|||||||
root["foo"] >> foo;
|
root["foo"] >> foo;
|
||||||
root["bar"][0] >> bar0;
|
root["bar"][0] >> bar0;
|
||||||
root["bar"][1] >> bar1;
|
root["bar"][1] >> bar1;
|
||||||
root["john"] >> john_str; // requires from_chars(std::string). see serialization samples below.
|
root["john"] >> john_str; // requires from_chars(std::string). see API doc.
|
||||||
root["bar"] >> ryml::key(bar_str); // to deserialize the key, use the tag function ryml::key()
|
root["bar"] >> ryml::key(bar_str); // to deserialize the key, use the tag function ryml::key()
|
||||||
CHECK(foo == 1);
|
CHECK(foo == 1);
|
||||||
CHECK(bar0 == 2);
|
CHECK(bar0 == 2);
|
||||||
@@ -490,15 +292,8 @@ CHECK(root["john"].id() == root[2].id());
|
|||||||
CHECK(bar_str == "bar");
|
CHECK(bar_str == "bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// Modifying existing nodes: operator= vs operator<<
|
// To modify existing nodes, use operator= or operator<<.
|
||||||
|
|
||||||
// As implied by its name, ConstNodeRef is a reference to a const
|
|
||||||
// node. It can be used to read from the node, but not write to it
|
|
||||||
// or modify the hierarchy of the node. If any modification is
|
|
||||||
// desired then a NodeRef must be used instead:
|
|
||||||
ryml::NodeRef wroot = tree.rootref();
|
|
||||||
|
|
||||||
// operator= assigns an existing string to the receiving node.
|
// operator= assigns an existing string to the receiving node.
|
||||||
// The contents are NOT copied, and this pointer will be in effect
|
// The contents are NOT copied, and this pointer will be in effect
|
||||||
@@ -535,19 +330,6 @@ CHECK(root["bar"][0].val() == "20");
|
|||||||
CHECK(root["bar"][1].val() == "30");
|
CHECK(root["bar"][1].val() == "30");
|
||||||
CHECK(root["john"].val() == "deere");
|
CHECK(root["john"].val() == "deere");
|
||||||
CHECK(tree.arena() == "says who2030deere"); // the result of serializations to the tree arena
|
CHECK(tree.arena() == "says who2030deere"); // the result of serializations to the tree arena
|
||||||
// using operator<< instead of operator=, the crash above is avoided:
|
|
||||||
{
|
|
||||||
std::string ok("in_scope");
|
|
||||||
// root["john"] = ryml::to_csubstr(ok); // don't, will dangle
|
|
||||||
wroot["john"] << ryml::to_csubstr(ok); // OK, copy to the tree's arena
|
|
||||||
}
|
|
||||||
CHECK(root["john"].val() == "in_scope"); // OK!
|
|
||||||
// serializing floating points:
|
|
||||||
wroot["float"] << 2.4;
|
|
||||||
// to force a particular precision or float format:
|
|
||||||
// (see sample_float_precision() and sample_formatting())
|
|
||||||
wroot["digits"] << ryml::fmt::real(2.4, /*num_digits*/6, ryml::FTOA_FLOAT);
|
|
||||||
CHECK(tree.arena() == "says who2030deerein_scope2.42.400000"); // the result of serializations to the tree arena
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
@@ -562,117 +344,6 @@ CHECK(root["newkeyval"].key() == "newkeyval");
|
|||||||
CHECK(root["newkeyval"].val() == "shiny and new");
|
CHECK(root["newkeyval"].val() == "shiny and new");
|
||||||
CHECK(root["newkeyval (serialized)"].key() == "newkeyval (serialized)");
|
CHECK(root["newkeyval (serialized)"].key() == "newkeyval (serialized)");
|
||||||
CHECK(root["newkeyval (serialized)"].val() == "shiny and new (serialized)");
|
CHECK(root["newkeyval (serialized)"].val() == "shiny and new (serialized)");
|
||||||
CHECK( ! tree.in_arena(root["newkeyval"].key())); // it's using directly the static string above
|
|
||||||
CHECK( ! tree.in_arena(root["newkeyval"].val())); // it's using directly the static string above
|
|
||||||
CHECK( tree.in_arena(root["newkeyval (serialized)"].key())); // it's using a serialization of the string above
|
|
||||||
CHECK( tree.in_arena(root["newkeyval (serialized)"].val())); // it's using a serialization of the string above
|
|
||||||
// adding a val node to a seq:
|
|
||||||
CHECK(root["bar"].num_children() == 2);
|
|
||||||
wroot["bar"][2] = "oh so nice";
|
|
||||||
wroot["bar"][3] << "oh so nice (serialized)";
|
|
||||||
CHECK(root["bar"].num_children() == 4);
|
|
||||||
CHECK(root["bar"][2].val() == "oh so nice");
|
|
||||||
CHECK(root["bar"][3].val() == "oh so nice (serialized)");
|
|
||||||
// adding a seq node:
|
|
||||||
CHECK(root.num_children() == 7);
|
|
||||||
wroot["newseq"] |= ryml::SEQ;
|
|
||||||
wroot.append_child() << ryml::key("newseq (serialized)") |= ryml::SEQ;
|
|
||||||
CHECK(root.num_children() == 9);
|
|
||||||
CHECK(root["newseq"].num_children() == 0);
|
|
||||||
CHECK(root["newseq"].is_seq());
|
|
||||||
CHECK(root["newseq (serialized)"].num_children() == 0);
|
|
||||||
CHECK(root["newseq (serialized)"].is_seq());
|
|
||||||
// adding a map node:
|
|
||||||
CHECK(root.num_children() == 9);
|
|
||||||
wroot["newmap"] |= ryml::MAP;
|
|
||||||
wroot.append_child() << ryml::key("newmap (serialized)") |= ryml::MAP;
|
|
||||||
CHECK(root.num_children() == 11);
|
|
||||||
CHECK(root["newmap"].num_children() == 0);
|
|
||||||
CHECK(root["newmap"].is_map());
|
|
||||||
CHECK(root["newmap (serialized)"].num_children() == 0);
|
|
||||||
CHECK(root["newmap (serialized)"].is_map());
|
|
||||||
//
|
|
||||||
// When the tree is mutable, operator[] first searches the tree
|
|
||||||
// for the does not mutate the tree until the returned node is
|
|
||||||
// written to.
|
|
||||||
//
|
|
||||||
// Until such time, the NodeRef object keeps in itself the required
|
|
||||||
// information to write to the proper place in the tree. This is
|
|
||||||
// called being in a "seed" state.
|
|
||||||
//
|
|
||||||
// This means that passing a key/index which does not exist will
|
|
||||||
// not mutate the tree, but will instead store (in the node) the
|
|
||||||
// proper place of the tree to be able to do so, if and when it is
|
|
||||||
// required. This is why the node is said to be in "seed" state -
|
|
||||||
// it allows creating the entry in the tree in the future.
|
|
||||||
//
|
|
||||||
// This is a significant difference from eg, the behavior of
|
|
||||||
// std::map, which mutates the map immediately within the call to
|
|
||||||
// operator[].
|
|
||||||
//
|
|
||||||
// All of the points above apply only if the tree is mutable. If
|
|
||||||
// the tree is const, then a NodeRef cannot be obtained from it;
|
|
||||||
// only a ConstNodeRef, which can never be used to mutate the
|
|
||||||
// tree.
|
|
||||||
//
|
|
||||||
CHECK(!root.has_child("I am not nothing"));
|
|
||||||
ryml::NodeRef nothing;
|
|
||||||
CHECK(nothing.invalid()); // invalid because it points at nothing
|
|
||||||
nothing = wroot["I am nothing"];
|
|
||||||
CHECK(!nothing.invalid()); // points at the tree, and a specific place in the tree
|
|
||||||
CHECK(nothing.is_seed()); // ... but nothing is there yet.
|
|
||||||
CHECK(!root.has_child("I am nothing")); // same as above
|
|
||||||
CHECK(!nothing.readable()); // ... and this node cannot be used to
|
|
||||||
// read anything from the tree
|
|
||||||
ryml::NodeRef something = wroot["I am something"];
|
|
||||||
ryml::ConstNodeRef constsomething = wroot["I am something"];
|
|
||||||
CHECK(!root.has_child("I am something")); // same as above
|
|
||||||
CHECK(!something.invalid());
|
|
||||||
CHECK(something.is_seed()); // same as above
|
|
||||||
CHECK(!something.readable()); // same as above
|
|
||||||
CHECK(constsomething.invalid()); // NOTE: because a ConstNodeRef cannot be
|
|
||||||
// used to mutate a tree, it is only valid()
|
|
||||||
// if it is pointing at an existing node.
|
|
||||||
something = "indeed"; // this will commit the seed to the tree, mutating at the proper place
|
|
||||||
CHECK(root.has_child("I am something"));
|
|
||||||
CHECK(root["I am something"].val() == "indeed");
|
|
||||||
CHECK(!something.invalid()); // it was already valid
|
|
||||||
CHECK(!something.is_seed()); // now the tree has this node, so the
|
|
||||||
// ref is no longer a seed
|
|
||||||
CHECK(something.readable()); // and it is now readable
|
|
||||||
//
|
|
||||||
// now the constref is also valid (but it needs to be reassigned):
|
|
||||||
ryml::ConstNodeRef constsomethingnew = wroot["I am something"];
|
|
||||||
CHECK(!constsomethingnew.invalid());
|
|
||||||
CHECK(constsomethingnew.readable());
|
|
||||||
// note that the old constref is now stale, because it only keeps
|
|
||||||
// the state at creation:
|
|
||||||
CHECK(constsomething.invalid());
|
|
||||||
CHECK(!constsomething.readable());
|
|
||||||
//
|
|
||||||
// -----------------------------------------------------------
|
|
||||||
// Remember: a seed node cannot be used to read from the tree!
|
|
||||||
// -----------------------------------------------------------
|
|
||||||
//
|
|
||||||
// The seed node needs to be created and become readable first.
|
|
||||||
//
|
|
||||||
// Trying to invoke any tree-reading method on a node that is not
|
|
||||||
// readable will cause an assertion (see RYML_USE_ASSERT).
|
|
||||||
//
|
|
||||||
// It is your responsibility to verify that the preconditions are
|
|
||||||
// met. If you are not sure about the structure of your data,
|
|
||||||
// write your code defensively to signify your full intent:
|
|
||||||
//
|
|
||||||
ryml::NodeRef wbar = wroot["bar"];
|
|
||||||
if(wbar.readable() && wbar.is_seq()) // .is_seq() requires .readable()
|
|
||||||
{
|
|
||||||
CHECK(wbar[0].readable() && wbar[0].val() == "20");
|
|
||||||
CHECK( ! wbar[100].readable());
|
|
||||||
CHECK( ! wbar[100].readable() || wbar[100].val() == "100"); // <- no crash because it is not .readable(), so never tries to call .val()
|
|
||||||
// this would work as well:
|
|
||||||
CHECK( ! wbar[0].is_seed() && wbar[0].val() == "20");
|
|
||||||
CHECK(wbar[100].is_seed() || wbar[100].val() == "100");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
@@ -711,45 +382,9 @@ ryml::csubstr buf_result = ryml::emit_yaml(tree, buf);
|
|||||||
CHECK(buf_result == expected_result);
|
CHECK(buf_result == expected_result);
|
||||||
CHECK(str_result == expected_result);
|
CHECK(str_result == expected_result);
|
||||||
CHECK(stream_result == expected_result);
|
CHECK(stream_result == expected_result);
|
||||||
// There are many possibilities to emit to buffer;
|
|
||||||
// please look at the emit sample functions below.
|
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// ConstNodeRef vs NodeRef
|
// UTF8
|
||||||
|
|
||||||
ryml::NodeRef noderef = tree["bar"][0];
|
|
||||||
ryml::ConstNodeRef constnoderef = tree["bar"][0];
|
|
||||||
|
|
||||||
// ConstNodeRef cannot be used to mutate the tree:
|
|
||||||
//constnoderef = "21"; // compile error
|
|
||||||
//constnoderef << "22"; // compile error
|
|
||||||
// ... but a NodeRef can:
|
|
||||||
noderef = "21"; // ok, can assign because it's not const
|
|
||||||
CHECK(tree["bar"][0].val() == "21");
|
|
||||||
noderef << "22"; // ok, can serialize and assign because it's not const
|
|
||||||
CHECK(tree["bar"][0].val() == "22");
|
|
||||||
|
|
||||||
// it is not possible to obtain a NodeRef from a ConstNodeRef:
|
|
||||||
// noderef = constnoderef; // compile error
|
|
||||||
|
|
||||||
// it is always possible to obtain a ConstNodeRef from a NodeRef:
|
|
||||||
constnoderef = noderef; // ok can assign const <- nonconst
|
|
||||||
|
|
||||||
// If a tree is const, then only ConstNodeRef's can be
|
|
||||||
// obtained from that tree:
|
|
||||||
ryml::Tree const& consttree = tree;
|
|
||||||
//noderef = consttree["bar"][0]; // compile error
|
|
||||||
noderef = tree["bar"][0]; // ok
|
|
||||||
constnoderef = consttree["bar"][0]; // ok
|
|
||||||
|
|
||||||
// ConstNodeRef and NodeRef can be compared for equality.
|
|
||||||
// Equality means they point at the same node.
|
|
||||||
CHECK(constnoderef == noderef);
|
|
||||||
CHECK(!(constnoderef != noderef));
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
|
||||||
// Dealing with UTF8
|
|
||||||
ryml::Tree langs = ryml::parse_in_arena(R"(
|
ryml::Tree langs = ryml::parse_in_arena(R"(
|
||||||
en: Planet (Gas)
|
en: Planet (Gas)
|
||||||
fr: Planète (Gazeuse)
|
fr: Planète (Gazeuse)
|
||||||
@@ -777,6 +412,7 @@ CHECK(langs["and this as well"].val() == "✅ 𝄞");
|
|||||||
CHECK(langs["not decoded"].val() == "\\u263A \\xE2\\x98\\xBA");
|
CHECK(langs["not decoded"].val() == "\\u263A \\xE2\\x98\\xBA");
|
||||||
CHECK(langs["neither this"].val() == "\\u2705 \\U0001D11E");
|
CHECK(langs["neither this"].val() == "\\u2705 \\U0001D11E");
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// Getting the location of nodes in the source:
|
// Getting the location of nodes in the source:
|
||||||
//
|
//
|
||||||
@@ -798,15 +434,29 @@ CHECK(loc.col == 4u);
|
|||||||
|
|
||||||
### Package managers
|
### Package managers
|
||||||
|
|
||||||
If you opt for package managers, here's where ryml is available so far
|
ryml is available in most package managers (thanks to all the
|
||||||
(thanks to all the contributors!):
|
contributors!) and linux distributions. But please be aware: those
|
||||||
|
packages are maintained downstream of this repository, so if you have
|
||||||
|
issues with the package, file a report with the respective maintainer.
|
||||||
|
|
||||||
|
Here's a quick roundup (not maintained):
|
||||||
|
* Package managers:
|
||||||
|
* [conan](https://conan.io/center/recipes/rapidyaml)
|
||||||
* [vcpkg](https://vcpkg.io/en/packages.html): `vcpkg install ryml`
|
* [vcpkg](https://vcpkg.io/en/packages.html): `vcpkg install ryml`
|
||||||
|
* [PyPI](https://pypi.org/project/rapidyaml/)
|
||||||
|
* Linux distributions:
|
||||||
* Arch Linux/Manjaro:
|
* Arch Linux/Manjaro:
|
||||||
|
* [rapidyaml (aarch64)](https://archlinuxarm.org/packages/aarch64/rapidyaml)
|
||||||
* [rapidyaml-git (AUR)](https://aur.archlinux.org/packages/rapidyaml-git/)
|
* [rapidyaml-git (AUR)](https://aur.archlinux.org/packages/rapidyaml-git/)
|
||||||
* [python-rapidyaml-git (AUR)](https://aur.archlinux.org/packages/python-rapidyaml-git/)
|
* [python-rapidyaml-git (AUR)](https://aur.archlinux.org/packages/python-rapidyaml-git/)
|
||||||
* [Fedora Linux](https://getfedora.org/)/[EPEL](https://docs.fedoraproject.org/en-US/epel/): `dnf install rapidyaml-devel`, `dnf install python3-rapidyaml`
|
* [Fedora Linux](https://getfedora.org/)/[EPEL](https://docs.fedoraproject.org/en-US/epel/):
|
||||||
* [PyPI](https://pypi.org/project/rapidyaml/)
|
* `dnf install rapidyaml-devel`
|
||||||
|
* `dnf install python3-rapidyaml`
|
||||||
|
* [Gentoo](https://packages.gentoo.org/packages/dev-cpp/rapidyaml)
|
||||||
|
* [OpenSuse](https://build.openbuildservice.org/package/show/Emulators/rapidyaml)
|
||||||
|
* [Slackbuilds](https://slackbuilds.org/repository/15.0/libraries/rapidyaml/)
|
||||||
|
* [AltLinux](https://packages.altlinux.org/en/sisyphus/srpms/rapidyaml/3006055151670528141)
|
||||||
|
|
||||||
Although package managers are very useful for quickly getting up to
|
Although package managers are very useful for quickly getting up to
|
||||||
speed, the advised way is still to bring ryml as a submodule of your
|
speed, the advised way is still to bring ryml as a submodule of your
|
||||||
project, building both together. This makes it easy to track any
|
project, building both together. This makes it easy to track any
|
||||||
@@ -1061,9 +711,9 @@ As for emitting, the improvement can be as high as 3000x:
|
|||||||
|
|
||||||
## YAML standard conformance
|
## YAML standard conformance
|
||||||
|
|
||||||
ryml is close to feature complete. Most of the YAML features are well
|
ryml is feature complete with regards to the YAML specification. All
|
||||||
covered in the unit tests, and expected to work, unless in the
|
the YAML features are well covered in the unit tests, and expected to
|
||||||
exceptions noted below.
|
work, unless in the exceptions noted below.
|
||||||
|
|
||||||
Of course, there are many dark corners in YAML, and there certainly
|
Of course, there are many dark corners in YAML, and there certainly
|
||||||
can appear cases which ryml fails to parse. Your [bug reports or pull
|
can appear cases which ryml fails to parse. Your [bug reports or pull
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
### Add API documentation
|
||||||
|
|
||||||
|
- [PR#423](https://github.com/biojppm/rapidyaml/pull/423): **add Doxygen-based API documentation, now hosted in [https://rapidyaml.readthedocs.io/]!**.
|
||||||
|
- It uses the base doxygen docs, as I couldn't get doxyrest or breathe or exhale to produce anything meaningful using the groups already established in the source code.
|
||||||
|
|
||||||
### Error handling
|
### Error handling
|
||||||
|
|
||||||
Fix major error handling problem reported in [#389](https://github.com/biojppm/rapidyaml/issues/389) ([PR#411](https://github.com/biojppm/rapidyaml/pull/411)):
|
Fix major error handling problem reported in [#389](https://github.com/biojppm/rapidyaml/issues/389) ([PR#411](https://github.com/biojppm/rapidyaml/pull/411)):
|
||||||
@@ -54,6 +59,9 @@ Fix major error handling problem reported in [#389](https://github.com/biojppm/r
|
|||||||
- `RYML_USE_ASSERT` - enable assertions regardless of build type. This is disabled by default. This macro was already defined; the current PR adds the cmake option.
|
- `RYML_USE_ASSERT` - enable assertions regardless of build type. This is disabled by default. This macro was already defined; the current PR adds the cmake option.
|
||||||
- `RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS` - make the default error handler provided by ryml throw exceptions instead of calling `std::abort()`. This is disabled by default.
|
- `RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS` - make the default error handler provided by ryml throw exceptions instead of calling `std::abort()`. This is disabled by default.
|
||||||
- Also, `RYML_DEBUG_BREAK()` is now enabled only if `RYML_DBG` is defined, as reported in [#362](https://github.com/biojppm/rapidyaml/issues/362).
|
- Also, `RYML_DEBUG_BREAK()` is now enabled only if `RYML_DBG` is defined, as reported in [#362](https://github.com/biojppm/rapidyaml/issues/362).
|
||||||
|
- As part of [PR#423](https://github.com/biojppm/rapidyaml/pull/423), to improve linters and codegen:
|
||||||
|
- annotate the error handlers with `[[noreturn]]`/`C4_NORETURN`
|
||||||
|
- annotate some error sites with `C4_UNREACHABLE_AFTER_ERR()`
|
||||||
|
|
||||||
|
|
||||||
### More fixes
|
### More fixes
|
||||||
|
|||||||
2
doc/.gitignore
vendored
Normal file
2
doc/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
_build/*
|
||||||
|
doxygen/*
|
||||||
2948
doc/Doxyfile
Normal file
2948
doc/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
269
doc/DoxygenLayout.xml
Normal file
269
doc/DoxygenLayout.xml
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<doxygenlayout version="1.0">
|
||||||
|
<!-- Generated by doxygen 1.10.0 -->
|
||||||
|
<!-- Navigation index tabs for HTML output -->
|
||||||
|
<navindex>
|
||||||
|
<tab type="mainpage" visible="yes" title=""/>
|
||||||
|
<tab type="pages" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="topics" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="modules" visible="yes" title="" intro="">
|
||||||
|
<tab type="modulelist" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="modulemembers" visible="yes" title="" intro=""/>
|
||||||
|
</tab>
|
||||||
|
<tab type="namespaces" visible="yes" title="">
|
||||||
|
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
||||||
|
</tab>
|
||||||
|
<tab type="concepts" visible="yes" title="">
|
||||||
|
</tab>
|
||||||
|
<tab type="interfaces" visible="yes" title="">
|
||||||
|
<tab type="interfacelist" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="interfaceindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||||
|
<tab type="interfacehierarchy" visible="yes" title="" intro=""/>
|
||||||
|
</tab>
|
||||||
|
<tab type="classes" visible="yes" title="">
|
||||||
|
<tab type="classlist" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||||
|
<tab type="hierarchy" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="classmembers" visible="yes" title="" intro=""/>
|
||||||
|
</tab>
|
||||||
|
<tab type="structs" visible="yes" title="">
|
||||||
|
<tab type="structlist" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="structindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||||
|
</tab>
|
||||||
|
<tab type="exceptions" visible="yes" title="">
|
||||||
|
<tab type="exceptionlist" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="exceptionindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||||
|
<tab type="exceptionhierarchy" visible="yes" title="" intro=""/>
|
||||||
|
</tab>
|
||||||
|
<tab type="files" visible="yes" title="">
|
||||||
|
<tab type="filelist" visible="yes" title="" intro=""/>
|
||||||
|
<tab type="globals" visible="yes" title="" intro=""/>
|
||||||
|
</tab>
|
||||||
|
<tab type="examples" visible="yes" title="" intro=""/>
|
||||||
|
</navindex>
|
||||||
|
|
||||||
|
<!-- Layout definition for a class page -->
|
||||||
|
<class>
|
||||||
|
<briefdescription visible="yes"/>
|
||||||
|
<includes visible="$SHOW_HEADERFILE"/>
|
||||||
|
<inheritancegraph visible="yes"/>
|
||||||
|
<collaborationgraph visible="yes"/>
|
||||||
|
<memberdecl>
|
||||||
|
<nestedclasses visible="yes" title=""/>
|
||||||
|
<publictypes title=""/>
|
||||||
|
<services title=""/>
|
||||||
|
<interfaces title=""/>
|
||||||
|
<publicslots title=""/>
|
||||||
|
<signals title=""/>
|
||||||
|
<publicmethods title=""/>
|
||||||
|
<publicstaticmethods title=""/>
|
||||||
|
<publicattributes title=""/>
|
||||||
|
<publicstaticattributes title=""/>
|
||||||
|
<protectedtypes title=""/>
|
||||||
|
<protectedslots title=""/>
|
||||||
|
<protectedmethods title=""/>
|
||||||
|
<protectedstaticmethods title=""/>
|
||||||
|
<protectedattributes title=""/>
|
||||||
|
<protectedstaticattributes title=""/>
|
||||||
|
<packagetypes title=""/>
|
||||||
|
<packagemethods title=""/>
|
||||||
|
<packagestaticmethods title=""/>
|
||||||
|
<packageattributes title=""/>
|
||||||
|
<packagestaticattributes title=""/>
|
||||||
|
<properties title=""/>
|
||||||
|
<events title=""/>
|
||||||
|
<privatetypes title=""/>
|
||||||
|
<privateslots title=""/>
|
||||||
|
<privatemethods title=""/>
|
||||||
|
<privatestaticmethods title=""/>
|
||||||
|
<privateattributes title=""/>
|
||||||
|
<privatestaticattributes title=""/>
|
||||||
|
<friends title=""/>
|
||||||
|
<related title="" subtitle=""/>
|
||||||
|
<membergroups visible="yes"/>
|
||||||
|
</memberdecl>
|
||||||
|
<detaileddescription title=""/>
|
||||||
|
<memberdef>
|
||||||
|
<inlineclasses title=""/>
|
||||||
|
<typedefs title=""/>
|
||||||
|
<enums title=""/>
|
||||||
|
<services title=""/>
|
||||||
|
<interfaces title=""/>
|
||||||
|
<constructors title=""/>
|
||||||
|
<functions title=""/>
|
||||||
|
<related title=""/>
|
||||||
|
<variables title=""/>
|
||||||
|
<properties title=""/>
|
||||||
|
<events title=""/>
|
||||||
|
</memberdef>
|
||||||
|
<allmemberslink visible="yes"/>
|
||||||
|
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||||
|
<authorsection visible="yes"/>
|
||||||
|
</class>
|
||||||
|
|
||||||
|
<!-- Layout definition for a namespace page -->
|
||||||
|
<namespace>
|
||||||
|
<briefdescription visible="yes"/>
|
||||||
|
<memberdecl>
|
||||||
|
<nestednamespaces visible="yes" title=""/>
|
||||||
|
<constantgroups visible="yes" title=""/>
|
||||||
|
<interfaces visible="yes" title=""/>
|
||||||
|
<classes visible="yes" title=""/>
|
||||||
|
<concepts visible="yes" title=""/>
|
||||||
|
<structs visible="yes" title=""/>
|
||||||
|
<exceptions visible="yes" title=""/>
|
||||||
|
<typedefs title=""/>
|
||||||
|
<sequences title=""/>
|
||||||
|
<dictionaries title=""/>
|
||||||
|
<enums title=""/>
|
||||||
|
<functions title=""/>
|
||||||
|
<variables title=""/>
|
||||||
|
<properties title=""/>
|
||||||
|
<membergroups visible="yes"/>
|
||||||
|
</memberdecl>
|
||||||
|
<detaileddescription title=""/>
|
||||||
|
<memberdef>
|
||||||
|
<inlineclasses title=""/>
|
||||||
|
<typedefs title=""/>
|
||||||
|
<sequences title=""/>
|
||||||
|
<dictionaries title=""/>
|
||||||
|
<enums title=""/>
|
||||||
|
<functions title=""/>
|
||||||
|
<variables title=""/>
|
||||||
|
<properties title=""/>
|
||||||
|
</memberdef>
|
||||||
|
<authorsection visible="yes"/>
|
||||||
|
</namespace>
|
||||||
|
|
||||||
|
<!-- Layout definition for a concept page -->
|
||||||
|
<concept>
|
||||||
|
<briefdescription visible="yes"/>
|
||||||
|
<includes visible="$SHOW_HEADERFILE"/>
|
||||||
|
<definition visible="yes" title=""/>
|
||||||
|
<detaileddescription title=""/>
|
||||||
|
<authorsection visible="yes"/>
|
||||||
|
</concept>
|
||||||
|
|
||||||
|
<!-- Layout definition for a file page -->
|
||||||
|
<file>
|
||||||
|
<briefdescription visible="yes"/>
|
||||||
|
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||||
|
<includegraph visible="yes"/>
|
||||||
|
<includedbygraph visible="yes"/>
|
||||||
|
<sourcelink visible="yes"/>
|
||||||
|
<memberdecl>
|
||||||
|
<interfaces visible="yes" title=""/>
|
||||||
|
<classes visible="yes" title=""/>
|
||||||
|
<structs visible="yes" title=""/>
|
||||||
|
<exceptions visible="yes" title=""/>
|
||||||
|
<namespaces visible="yes" title=""/>
|
||||||
|
<concepts visible="yes" title=""/>
|
||||||
|
<constantgroups visible="yes" title=""/>
|
||||||
|
<defines title=""/>
|
||||||
|
<typedefs title=""/>
|
||||||
|
<sequences title=""/>
|
||||||
|
<dictionaries title=""/>
|
||||||
|
<enums title=""/>
|
||||||
|
<functions title=""/>
|
||||||
|
<variables title=""/>
|
||||||
|
<properties title=""/>
|
||||||
|
<membergroups visible="yes"/>
|
||||||
|
</memberdecl>
|
||||||
|
<detaileddescription title=""/>
|
||||||
|
<memberdef>
|
||||||
|
<inlineclasses title=""/>
|
||||||
|
<defines title=""/>
|
||||||
|
<typedefs title=""/>
|
||||||
|
<sequences title=""/>
|
||||||
|
<dictionaries title=""/>
|
||||||
|
<enums title=""/>
|
||||||
|
<functions title=""/>
|
||||||
|
<variables title=""/>
|
||||||
|
<properties title=""/>
|
||||||
|
</memberdef>
|
||||||
|
<authorsection/>
|
||||||
|
</file>
|
||||||
|
|
||||||
|
<!-- Layout definition for a group page -->
|
||||||
|
<group>
|
||||||
|
<briefdescription visible="yes"/>
|
||||||
|
<groupgraph visible="yes"/>
|
||||||
|
<memberdecl>
|
||||||
|
<nestedgroups visible="yes" title=""/>
|
||||||
|
<modules visible="yes" title=""/>
|
||||||
|
<dirs visible="yes" title=""/>
|
||||||
|
<files visible="yes" title=""/>
|
||||||
|
<namespaces visible="yes" title=""/>
|
||||||
|
<concepts visible="yes" title=""/>
|
||||||
|
<classes visible="yes" title=""/>
|
||||||
|
<defines title=""/>
|
||||||
|
<typedefs title=""/>
|
||||||
|
<sequences title=""/>
|
||||||
|
<dictionaries title=""/>
|
||||||
|
<enums title=""/>
|
||||||
|
<enumvalues title=""/>
|
||||||
|
<functions title=""/>
|
||||||
|
<variables title=""/>
|
||||||
|
<signals title=""/>
|
||||||
|
<publicslots title=""/>
|
||||||
|
<protectedslots title=""/>
|
||||||
|
<privateslots title=""/>
|
||||||
|
<events title=""/>
|
||||||
|
<properties title=""/>
|
||||||
|
<friends title=""/>
|
||||||
|
<membergroups visible="yes"/>
|
||||||
|
</memberdecl>
|
||||||
|
<detaileddescription title=""/>
|
||||||
|
<memberdef>
|
||||||
|
<pagedocs/>
|
||||||
|
<inlineclasses title=""/>
|
||||||
|
<defines title=""/>
|
||||||
|
<typedefs title=""/>
|
||||||
|
<sequences title=""/>
|
||||||
|
<dictionaries title=""/>
|
||||||
|
<enums title=""/>
|
||||||
|
<enumvalues title=""/>
|
||||||
|
<functions title=""/>
|
||||||
|
<variables title=""/>
|
||||||
|
<signals title=""/>
|
||||||
|
<publicslots title=""/>
|
||||||
|
<protectedslots title=""/>
|
||||||
|
<privateslots title=""/>
|
||||||
|
<events title=""/>
|
||||||
|
<properties title=""/>
|
||||||
|
<friends title=""/>
|
||||||
|
</memberdef>
|
||||||
|
<authorsection visible="yes"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<!-- Layout definition for a C++20 module page -->
|
||||||
|
<module>
|
||||||
|
<briefdescription visible="yes"/>
|
||||||
|
<exportedmodules visible="yes"/>
|
||||||
|
<memberdecl>
|
||||||
|
<concepts visible="yes" title=""/>
|
||||||
|
<classes visible="yes" title=""/>
|
||||||
|
<enums title=""/>
|
||||||
|
<typedefs title=""/>
|
||||||
|
<functions title=""/>
|
||||||
|
<variables title=""/>
|
||||||
|
<membergroups title=""/>
|
||||||
|
</memberdecl>
|
||||||
|
<detaileddescription title=""/>
|
||||||
|
<memberdecl>
|
||||||
|
<files visible="yes"/>
|
||||||
|
</memberdecl>
|
||||||
|
</module>
|
||||||
|
|
||||||
|
<!-- Layout definition for a directory page -->
|
||||||
|
<directory>
|
||||||
|
<briefdescription visible="yes"/>
|
||||||
|
<directorygraph visible="yes"/>
|
||||||
|
<memberdecl>
|
||||||
|
<dirs visible="yes"/>
|
||||||
|
<files visible="yes"/>
|
||||||
|
</memberdecl>
|
||||||
|
<detaileddescription title=""/>
|
||||||
|
</directory>
|
||||||
|
</doxygenlayout>
|
||||||
35
doc/Makefile
Normal file
35
doc/Makefile
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line, and also
|
||||||
|
# from the environment for the first two.
|
||||||
|
SPHINXOPTS ?=
|
||||||
|
SPHINXBUILD ?= sphinx-build
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile doxy
|
||||||
|
@echo "building from the makefile"
|
||||||
|
$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
@echo "overwrite doxygen files"
|
||||||
|
mkdir -p "$(BUILDDIR)"
|
||||||
|
if [ -d "$(BUILDDIR)/html/doxygen" ] ; then rm -rf "$(BUILDDIR)/html/doxygen" ; fi
|
||||||
|
cp -favr doxygen/html "$(BUILDDIR)/html/doxygen"
|
||||||
|
|
||||||
|
doxy:
|
||||||
|
# ensure submodules are checked out
|
||||||
|
if [ ! -f $(shell pwd)/../ext/c4core/cmake/c4Project.cmake ] ; then echo "ERROR: submodules not present" ; exit 1 ; fi
|
||||||
|
if [ ! -f $(shell pwd)/../ext/c4core/src/c4/charconv.hpp ] ; then echo "ERROR: submodules not present" ; exit 1 ; fi
|
||||||
|
doxygen Doxyfile
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf doxygen/
|
||||||
|
if [ ! -z "$(BUILDDIR)" ] ; then rm -rf "$(BUILDDIR)" ; fi
|
||||||
129
doc/conf.py
Normal file
129
doc/conf.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# For the full list of built-in configuration values, see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
import os
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||||
|
|
||||||
|
project = 'rapidyaml'
|
||||||
|
copyright = '2018-2024 Joao Paulo Magalhaes <dev@jpmag.me>'
|
||||||
|
author = 'Joao Paulo Magalhaes <dev@jpmag.me>'
|
||||||
|
release = '0.5.0'
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
|
extensions = [
|
||||||
|
"myst_parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
templates_path = ['_templates']
|
||||||
|
exclude_patterns = ['_build', 'doxygen', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
# Tell sphinx what the primary language being documented is.
|
||||||
|
primary_domain = 'cpp'
|
||||||
|
|
||||||
|
# Tell sphinx what the pygments highlight language should be.
|
||||||
|
highlight_language = 'cpp'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||||
|
|
||||||
|
#html_theme = 'alabaster'
|
||||||
|
html_theme = 'sphinx_rtd_theme'
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
exclude_patterns += ["doxy_*.md"]
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Overwrite with the Doxygen documentation
|
||||||
|
# https://stackoverflow.com/a/41199722
|
||||||
|
|
||||||
|
"""
|
||||||
|
exclude_patterns += ["main.md", "include/*rst"]
|
||||||
|
import subprocess
|
||||||
|
this_dir = os.path.dirname(__file__)
|
||||||
|
subprocess.check_call(['doxygen', 'Doxyfile'], cwd=this_dir)
|
||||||
|
html_extra_path = ['./doxygen/html']
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Setup the breathe extension
|
||||||
|
# https://breathe.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
# disabled as it was not working well with the groups
|
||||||
|
"""
|
||||||
|
extensions.append("breathe")
|
||||||
|
this_dir = os.path.dirname(__file__)
|
||||||
|
xml_dir = os.path.join(this_dir, "doxygen/xml")
|
||||||
|
assert os.path.exists(xml_dir)
|
||||||
|
breathe_projects = {
|
||||||
|
"rapidyaml": xml_dir
|
||||||
|
}
|
||||||
|
breathe_default_project = "rapidyaml"
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Setup the doxyrest extension
|
||||||
|
# https://vovkos.github.io/doxyrest/manual/basic.html
|
||||||
|
# https://github.com/vovkos/doxyrest
|
||||||
|
|
||||||
|
'''
|
||||||
|
# To get the binary:
|
||||||
|
# git clone --recursive https://github.com/vovkos/doxyrest_b
|
||||||
|
# cd doxyrest_b
|
||||||
|
# cmany i
|
||||||
|
|
||||||
|
# put the doxyrest dir in the path
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
doxyrest_exe = shutil.which('doxyrest')
|
||||||
|
doxyrest_dir = os.path.abspath(os.path.dirname(os.path.dirname(doxyrest_exe)))
|
||||||
|
doxyrest_sphinx_dir = os.path.join(doxyrest_dir, "share/doxyrest/sphinx")
|
||||||
|
sys.path.insert(1, doxyrest_sphinx_dir)
|
||||||
|
|
||||||
|
extensions += ['doxyrest', 'cpplexer']
|
||||||
|
|
||||||
|
# commands to run
|
||||||
|
"""
|
||||||
|
set -xe ; \
|
||||||
|
cd ~/proj/rapidyaml ; \
|
||||||
|
cmany c -t Debug -c clang++ -V RYML_DEV=OFF -V RYML_BUILD_TESTS=ON ; \
|
||||||
|
cp -fav build/linux-x86_64-clangxx16.0-Debug/compile_commands.json doc ; \
|
||||||
|
cd doc ; \
|
||||||
|
make doxy ; \
|
||||||
|
export PATH=/home/jpmag/proj/doxyrest_b/install/linux-x86_64-gxx13.2-Release/bin:$PATH ; \
|
||||||
|
doxyrest -h ; \
|
||||||
|
doxyrest -c doxyrest-config.lua ; \
|
||||||
|
make html
|
||||||
|
"""
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Setup the exhale extension
|
||||||
|
# https://exhale.readthedocs.io/en/latest/quickstart.html
|
||||||
|
|
||||||
|
# disabled as it was just not working will
|
||||||
|
"""
|
||||||
|
#extensions.append("exhale")
|
||||||
|
exhale_args = {
|
||||||
|
# These arguments are required
|
||||||
|
"containmentFolder": "./api",
|
||||||
|
"rootFileName": "library_root.rst",
|
||||||
|
"doxygenStripFromPath": "..",
|
||||||
|
# Heavily encouraged optional argument (see docs)
|
||||||
|
"rootFileTitle": "Library API",
|
||||||
|
# Suggested optional arguments
|
||||||
|
"createTreeView": True,
|
||||||
|
# TIP: if using the sphinx-bootstrap-theme, you need
|
||||||
|
# "treeViewIsBootstrap": True,
|
||||||
|
"exhaleExecutesDoxygen": True,
|
||||||
|
"exhaleDoxygenStdin": "INPUT = ../src"
|
||||||
|
}
|
||||||
|
"""
|
||||||
55
doc/doxy_main.md
Normal file
55
doc/doxy_main.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# rapidyaml
|
||||||
|
|
||||||
|
* Begin by looking at the [project's README](https://github.com/biojppm/rapidyaml/blob/v0.5.0/README.md)
|
||||||
|
* [Documentation page](https://rapidyaml.readthedocs.org)
|
||||||
|
* Next, skim the docs for the @ref doc_quickstart sample.
|
||||||
|
* Good! Now the main ryml topics:
|
||||||
|
* @ref doc_parse - how to parse YAML/JSON into a tree
|
||||||
|
* @ref doc_emit - how to emit YAML/JSON from an existing tree
|
||||||
|
* How to visit and create ryml trees:
|
||||||
|
* @ref doc_node_type
|
||||||
|
* @ref doc_tree
|
||||||
|
* @ref doc_node_classes
|
||||||
|
* For serialization/deserialization:
|
||||||
|
* See @ref sample_scalar_types for when the type is scalar (a leaf node in the YAML tree, containing a string representation):
|
||||||
|
* See examples on how to @ref sample_to_chars_scalar
|
||||||
|
* See examples on how to @ref sample_from_chars_scalar
|
||||||
|
* See the sample @ref sample::sample_user_scalar_types
|
||||||
|
* When serializing floating point values in C++ earlier than 17,
|
||||||
|
be aware that there may be a truncation of the precision with
|
||||||
|
the default to_chars implementation. To enforce a particular
|
||||||
|
precision, use for example @ref c4::fmt::real, or call
|
||||||
|
directly @ref c4::ftoa or @ref c4::dtoa, or any other method
|
||||||
|
(remember that ryml only stores the final string, so nothing
|
||||||
|
prevents you from creating it). See the relevant sample: @ref
|
||||||
|
sample::sample_float_precision.
|
||||||
|
* See @ref sample_container_types for when the type is a container (ie, a node which has children, which may themselves be containers).
|
||||||
|
* See the sample @ref sample::sample_user_container_types
|
||||||
|
* ryml does not use any STL containers internally, but it can be
|
||||||
|
used to serialize and deserialize these containers. See @ref
|
||||||
|
sample::sample_std_types for an example. See the header @ref
|
||||||
|
ryml_std.hpp and also the headers it includes.
|
||||||
|
* @ref doc_tag_utils - how to resolve tags
|
||||||
|
* @ref doc_callbacks - how to set up error/allocation/deallocation
|
||||||
|
callbacks either globally for the library, or for specific objects
|
||||||
|
such as @ref c4::yml::Tree or @ref c4::yml::Parser
|
||||||
|
* rapidyaml uses these facilities from [c4core](https://github.com/biojppm/c4core), so their documentation
|
||||||
|
is also shown here:
|
||||||
|
* @ref doc_substr
|
||||||
|
* @ref doc_charconv
|
||||||
|
* @ref doc_format_utils
|
||||||
|
* @ref doc_base64
|
||||||
|
|
||||||
|
You should also be aware that the @ref doc_charconv facilities in
|
||||||
|
[c4core](https://github.com/biojppm/c4core) are extremely fast, and
|
||||||
|
generally outperform the fastest equivalent facilities in the standard
|
||||||
|
library by a significant margin; refer to the documentation of @ref
|
||||||
|
doc_charconv for further details. For example:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<caption id="xtoa-i64">xtoa,int64_t</caption>
|
||||||
|
<tr><th>g++12, linux <th>Visual Studio 2019
|
||||||
|
<tr><td> \image html linux-x86_64-gxx12.1-Release-c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png <td> \image html windows-x86_64-vs2019-Release-c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
463
doc/doxyrest-config.lua
Normal file
463
doc/doxyrest-config.lua
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
--------------------------------------------------------------------------------
|
||||||
|
--
|
||||||
|
-- This file is part of the Doxyrest toolkit.
|
||||||
|
--
|
||||||
|
-- Doxyrest is distributed under the MIT license.
|
||||||
|
-- For details see accompanying license.txt file,
|
||||||
|
-- the public copy of which is also available at:
|
||||||
|
-- http://tibbo.com/downloads/archive/doxyrest/license.txt
|
||||||
|
--
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! \defgroup frame-config
|
||||||
|
--! \grouporder 2
|
||||||
|
--! \title Frame Settings
|
||||||
|
--!
|
||||||
|
--! This section describes frame settings controlling input and output paths,
|
||||||
|
--! titles, force-includes, declaration coding style, etc.
|
||||||
|
--!
|
||||||
|
--! A default ``doxyrest-config.lua`` file for standard frames can be found at
|
||||||
|
--! ``$DOXYREST_FRAME_DIR/doxyrest-config.lua``. Copy it to your project
|
||||||
|
--! directory and then adjust all the necessary parameters.
|
||||||
|
--!
|
||||||
|
--! @{
|
||||||
|
--!
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! Table containing a list of frame directories. All frame files will be
|
||||||
|
--! searched in directories -- and in the sequence -- specified here.
|
||||||
|
--!
|
||||||
|
|
||||||
|
FRAME_DIR_LIST = {"include"}
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! The output master (index) reStructuredText file. Usually, the index frame
|
||||||
|
--! also generates auxillary files -- they will be placed next to the master
|
||||||
|
--! file. The command line option ``-f`` *overrides* this value.
|
||||||
|
--! If neither ``FRAME_FILE`` nor ``-f`` is specified, ``index.rst.in`` will be
|
||||||
|
--! used as the default frame file.
|
||||||
|
--!
|
||||||
|
|
||||||
|
FRAME_FILE = "index.rst"
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! The input master (index) XML file. Specifying it here allows calling
|
||||||
|
--! ``doxyrest`` without parameters; otherwise, the master XML *must* be passed
|
||||||
|
--! via the command line. If both ``INPUT_FILE`` and command line parameter are
|
||||||
|
--! specified, the command line takes precedence.
|
||||||
|
--!
|
||||||
|
|
||||||
|
INPUT_FILE = "doxygen/xml/index.xml"
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! The output master (index) reStructuredText file. Usually, the index frame
|
||||||
|
--! also generates auxillary files -- they will be placed next to the master
|
||||||
|
--! file. The command line option ``-o`` *overrides* this value. If neither
|
||||||
|
--! ``OUTPUT_FILE`` nor ``-o`` is specified, ``index.rst`` will be used as
|
||||||
|
--! the default output master file.
|
||||||
|
--!
|
||||||
|
|
||||||
|
OUTPUT_FILE = nil
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! File with project-specific reStructuredText definitions. When non``nil``,
|
||||||
|
--! this file will be included at the top of every generated ``.rst`` file.
|
||||||
|
--!
|
||||||
|
|
||||||
|
FORCE_INCLUDE_FILE = nil
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! If you want to add extra reStructuredText documentation pages, do so
|
||||||
|
--! by adding them to this list.
|
||||||
|
--!
|
||||||
|
|
||||||
|
EXTRA_PAGE_LIST = {}
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! The title of the main (index) page. This only is used when ``INTRO_FILE``
|
||||||
|
--! is not set (otherwise, the title of intro file will be used).
|
||||||
|
--!
|
||||||
|
|
||||||
|
INDEX_TITLE = "My Project Documentation asdasdasdxs"
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! File with project introduction (reStructuredText). When non-nil, this file
|
||||||
|
--! will be included into ``index.rst`` file and NOT added to the list of other
|
||||||
|
--! documentation pages.
|
||||||
|
--!
|
||||||
|
|
||||||
|
INTRO_FILE = nil
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! Specify whether to sort groups lexicographically (by ``title``) or
|
||||||
|
--! logically (by ``id``). To maintain the original order (in which a group has
|
||||||
|
--! been seen in the XML database), use ``originalIdx``.
|
||||||
|
--!
|
||||||
|
--! Omitting ``SORT_GROUPS_BY`` (or setting it to ``nil``) results in groups
|
||||||
|
--! being sorted by ``title``.
|
||||||
|
--!
|
||||||
|
|
||||||
|
SORT_GROUPS_BY = "title"
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
By default, the page for the global namespace page will be called
|
||||||
|
"Global Namespace" and will contain no description except that for the
|
||||||
|
global compounds and members.
|
||||||
|
|
||||||
|
It's possible to override this behaviour by defining an auxillary compound
|
||||||
|
(page or group) with a special ``id``; this page/group may contain a
|
||||||
|
user-defined title, a brief description and a detailed description. Use
|
||||||
|
``GLOBAL_AUX_COMPOUND_ID`` to define this special id.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
To make sure you use the correct **Doxygen** XML ID of the group/page,
|
||||||
|
find the definition of the group in one of ``.xml`` files and copy
|
||||||
|
the value of ``id`` attribute.
|
||||||
|
|
||||||
|
For example, if the group was declared as ``\defgroup global`` then
|
||||||
|
the its ``id`` will probably be either ``group_<your-group-name>`` or
|
||||||
|
``group__<your-group-name>``.
|
||||||
|
]]
|
||||||
|
|
||||||
|
GLOBAL_AUX_COMPOUND_ID = "group_global"
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Doxyrest offers a workaround for the lack of footnotes in Doxygen by
|
||||||
|
getting documentation blocks for specially named pseudo-members and
|
||||||
|
converting those into footnotes.
|
||||||
|
|
||||||
|
``FOOTNOTE_MEMBER_PREFIX`` specifies the name prefix for such
|
||||||
|
pseudo-members. If it is set to ``nil`` or an empty string, Doxyrest
|
||||||
|
will not attempt to convert any members to footnotes.
|
||||||
|
|
||||||
|
\sa :ref:`footnotes`
|
||||||
|
]]
|
||||||
|
|
||||||
|
FOOTNOTE_MEMBER_PREFIX = nil
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! Specify the main language of your project; this string will be used for
|
||||||
|
--! the reStructuredText ``.. code-block::`` sections and for conditional
|
||||||
|
--! formatting of module item declarations.
|
||||||
|
--!
|
||||||
|
|
||||||
|
LANGUAGE = cpp
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! Convert ``\verbatim`` sections in doxy-comments to ``.. code-block::``
|
||||||
|
--! sections in the output reStructuredText. The string value of
|
||||||
|
--! ``VERBATIM_TO_CODE_BLOCK`` will be used as the language of
|
||||||
|
--! ``.. code-block::`` section. By default, it's ``"none"`` which results in
|
||||||
|
--! no syntax highlighting. To disable conversion at all, use ``nil``.
|
||||||
|
--!
|
||||||
|
|
||||||
|
VERBATIM_TO_CODE_BLOCK = "none"
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! If the original doxy comments contain asterisks, they have to be escaped in
|
||||||
|
--! reStructuredText (asterisks are used to mark **bold** or *italic* blocks).
|
||||||
|
--!
|
||||||
|
|
||||||
|
ESCAPE_ASTERISKS = false
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! If the original doxy comments contain pipe characters ``|``, they have to be
|
||||||
|
--! escaped in reStructuredText (pipes are used for substitution references).
|
||||||
|
--!
|
||||||
|
|
||||||
|
ESCAPE_PIPES = false
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! If the original doxy comments contain trailingasterisks, they have to be
|
||||||
|
--! escaped in reStructuredText (trailing underscores are used for internal
|
||||||
|
--! links).
|
||||||
|
--!
|
||||||
|
|
||||||
|
ESCAPE_TRAILING_UNDERSCORES = false
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! Exclude items declared in specific locations. Use a regular expression to
|
||||||
|
--! define a mask of directories/source files to completely exclude from the
|
||||||
|
--! final documentation. For example, ``.*/impl/.*lua$`` will exclude all
|
||||||
|
--! ``.lua`` files located in ``impl/`` directory.
|
||||||
|
--!
|
||||||
|
|
||||||
|
EXCLUDE_LOCATION_PATTERN = nil
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! Exclude variables and functions without any documentation (no doxy-comments).
|
||||||
|
--!
|
||||||
|
|
||||||
|
EXCLUDE_UNDOCUMENTED_ITEMS = false
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! \subgroup
|
||||||
|
--!
|
||||||
|
--! By default, Doxyrest tries to translate Doxygen ``\section``,
|
||||||
|
--! ``\subsection``, and ``\subsubsection`` commands (as well as the
|
||||||
|
--! ``<heading level="n">`` blocks generated by Doxygen from Markdown titles
|
||||||
|
--! inside comments) into reStructuredText titles.
|
||||||
|
--!
|
||||||
|
--! This sometimes leads to problems because Doxygen headings may appear outside
|
||||||
|
--! of the global scope (e.g. inside lists) while reStructuredText titles are
|
||||||
|
--! only allowed at the global scope. Another issue is Doxygen headings may
|
||||||
|
--! yield inconsistent title structure (e.g. a title level 1 followed by level
|
||||||
|
--! 3).
|
||||||
|
--!
|
||||||
|
--! If you run into these issues, use ``SECTION_TO_RUBRIC`` or
|
||||||
|
--! ``HEADING_TO_RUBRIC`` to always convert Doxygen sections or ``<heading>``
|
||||||
|
--! blocks or into reStructuredText ``.. rubric::`` directives. This yields
|
||||||
|
--! uni-level headings, but solves both aforementioned problems.
|
||||||
|
--!
|
||||||
|
|
||||||
|
SECTION_TO_RUBRIC = false
|
||||||
|
HEADING_TO_RUBRIC = false
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
By default, Doxyrest frames build a Python dictionary to be used later on by
|
||||||
|
the ``:cref:`` (code-reference) role. This database maps language-specific
|
||||||
|
qualified names of items to their IDs.
|
||||||
|
|
||||||
|
This, together with setting the Sphinx ``default_role`` to ``cref``, allows
|
||||||
|
to conveniently reference items from Doxy-comments or regular ``.rst`` files
|
||||||
|
as such:
|
||||||
|
|
||||||
|
.. code-block:: none
|
||||||
|
|
||||||
|
The `ui::Dialog` class is a base to the `ui::FileDialog` class.
|
||||||
|
|
||||||
|
However, if this facility is not used, building (and loading) of the
|
||||||
|
cref-database can be omitted to save both space and time.
|
||||||
|
]]
|
||||||
|
|
||||||
|
CREF_DB = false
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Exclude items with higher protection level than ``PROTECTION_FILTER``:
|
||||||
|
|
||||||
|
1. ``public``
|
||||||
|
2. ``protected``
|
||||||
|
3. ``private``
|
||||||
|
4. ``package``
|
||||||
|
|
||||||
|
By default, only public items are included into documentation.
|
||||||
|
]]
|
||||||
|
|
||||||
|
PROTECTION_FILTER = "public"
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! In many projects empty defines are *only* used as include-guards (and as
|
||||||
|
--! such, should be excluded from the documentation). If this is not the case
|
||||||
|
--! and empty defines should be kept in the final documentation, change this
|
||||||
|
--! setting to ``false``.
|
||||||
|
--!
|
||||||
|
|
||||||
|
EXCLUDE_EMPTY_DEFINES = true
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! If non-``nil``, each define will be checked using this regular expression
|
||||||
|
--! and if its name matches, this define will be excluded from the documentation.
|
||||||
|
--!
|
||||||
|
|
||||||
|
EXCLUDE_DEFINE_PATTERN = nil
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! Usually providing documentation blocks for default constructors is
|
||||||
|
--! not necessary (as to avoid redundant meaningless "Constructs a new object"
|
||||||
|
--! paragraphs). Change this to ``false`` if default constructors *should* be
|
||||||
|
--! included.
|
||||||
|
--!
|
||||||
|
|
||||||
|
EXCLUDE_DEFAULT_CONSTRUCTORS = true
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! Usually providing documentation blocks for a destructors is
|
||||||
|
--! not necessary (as to avoid redundant meaningless "Destructs an object"
|
||||||
|
--! paragraphs). Change this to ``false`` if destructors *should* be
|
||||||
|
--! included.
|
||||||
|
--!
|
||||||
|
|
||||||
|
EXCLUDE_DESTRUCTORS = true
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Usually providing documentation blocks for primitive C typedefs such as:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
|
||||||
|
typedef struct S S;
|
||||||
|
|
||||||
|
is not necessary. Change this to ``false`` if such typedefs *should* be
|
||||||
|
included.
|
||||||
|
]]
|
||||||
|
|
||||||
|
EXCLUDE_PRIMITIVE_TYPEDEFS = true
|
||||||
|
|
||||||
|
--!
|
||||||
|
--! For a base class/struct, show all the types directly derived from it.
|
||||||
|
--!
|
||||||
|
|
||||||
|
SHOW_DIRECT_DESCENDANTS = true
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
\subgroup
|
||||||
|
|
||||||
|
Insert space between function name and parameter list like this:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
|
||||||
|
void foo ();
|
||||||
|
|
||||||
|
By default, ``PRE_PARAM_LIST_SPACE`` is ``false`` which yields:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
|
||||||
|
void foo();
|
||||||
|
]]
|
||||||
|
|
||||||
|
PRE_PARAM_LIST_SPACE = false
|
||||||
|
PRE_OPERATOR_NAME_SPACE = true
|
||||||
|
PRE_OPERATOR_PARAM_LIST_SPACE = true
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Insert a new line before the body of a enum, struct, class, etc.
|
||||||
|
|
||||||
|
When ``PRE_BODY_NL`` is ``false``:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
class MyClass {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
When ``PRE_BODY_NL`` is ``true``:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
class MyClass
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]]
|
||||||
|
|
||||||
|
PRE_BODY_NL = false
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Use multi-line parameter lists in function declarations if parameter count is
|
||||||
|
greater than this threshold. ``nil`` means don't use parameter count
|
||||||
|
threshold.
|
||||||
|
|
||||||
|
For example, when ``ML_PARAM_LIST_COUNT_THRESHOLD`` is ``2``, the function
|
||||||
|
declarations will look as such:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
|
||||||
|
void fn0();
|
||||||
|
void fn1(int a);
|
||||||
|
void fn2(int a, int b);
|
||||||
|
|
||||||
|
void fn3(
|
||||||
|
int a,
|
||||||
|
int b
|
||||||
|
int c
|
||||||
|
);
|
||||||
|
]]
|
||||||
|
|
||||||
|
ML_PARAM_LIST_COUNT_THRESHOLD = nil
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Use multi-line parameter lists in function declarations if single-line
|
||||||
|
declaration length parameter count is greater than this threshold.
|
||||||
|
``nil`` means don't use length threshold.
|
||||||
|
|
||||||
|
Similar to ``ML_PARAM_LIST_COUNT_THRESHOLD``, but the threshold parameter
|
||||||
|
here is *declaration length* and not *declaration parameter count*.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
|
||||||
|
void foo(int a, int b, int c);
|
||||||
|
|
||||||
|
void bar(
|
||||||
|
int veryLongParameterName,
|
||||||
|
int anotherVeryLongParameterName
|
||||||
|
);
|
||||||
|
|
||||||
|
]]
|
||||||
|
|
||||||
|
ML_PARAM_LIST_LENGTH_THRESHOLD = 80
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Use multi-line specifier-modifier lists in function declarations, i.e
|
||||||
|
allocate a dedicated line for each type specifier/morifier.
|
||||||
|
|
||||||
|
For example, when ``ML_SPECIFIER_MODIFIER_LIST`` is ``true``, the function
|
||||||
|
declarations will look as such:
|
||||||
|
|
||||||
|
.. code-block:: C
|
||||||
|
|
||||||
|
void
|
||||||
|
foo();
|
||||||
|
|
||||||
|
static
|
||||||
|
bool
|
||||||
|
__cdecl
|
||||||
|
bar(int a);
|
||||||
|
]]
|
||||||
|
|
||||||
|
ML_SPECIFIER_MODIFIER_LIST = false
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Use the new C++11 syntax for ``typedef``-s:
|
||||||
|
|
||||||
|
When ``TYPEDEF_TO_USING`` is ``false``:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
typedef unsigned int uint_t;
|
||||||
|
typedef void Foo(int);
|
||||||
|
typedef void (*FooPtr)(int);
|
||||||
|
|
||||||
|
When ``TYPEDEF_TO_USING`` is ``true``:
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
using uint_t = unsigned int;
|
||||||
|
using Foo typedef void (int);
|
||||||
|
using FooPtr typedef void (*)(int);
|
||||||
|
]]
|
||||||
|
|
||||||
|
TYPEDEF_TO_USING = false
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Sometimes, it's required to redirect a Doxygen link to some external location.
|
||||||
|
In this case, add an entry to ``EXTERNAL_CREF_DB`` with the target URL, e.g.:
|
||||||
|
|
||||||
|
.. code-block:: lua
|
||||||
|
|
||||||
|
EXTERNAL_CREF_DB =
|
||||||
|
{
|
||||||
|
[ "jnc.Scheduler" ] = "https://vovkos.github.io/jancy/stdlib/class_jnc_Scheduler.html",
|
||||||
|
[ "strlen"] = "https://vovkos.github.io/jancy/stdlib/group_crt.html#doxid-function-strlen"
|
||||||
|
|
||||||
|
-- ...
|
||||||
|
}
|
||||||
|
|
||||||
|
The key of the map is an ``importid`` attribute. This is a non-standard Doxygen
|
||||||
|
attribute; Jancy compiler generates is when a referenced item is contained in an
|
||||||
|
imported extensions library (``.jncx``)
|
||||||
|
]]
|
||||||
|
|
||||||
|
EXTERNAL_CREF_DB = {}
|
||||||
|
|
||||||
|
--[[!
|
||||||
|
Usually providing documentation blocks for Lua ``local`` variables or
|
||||||
|
functions is not desired. Change this to ``false`` if local items
|
||||||
|
*should* be included in the final documentation.
|
||||||
|
]]
|
||||||
|
|
||||||
|
EXCLUDE_LUA_LOCALS = true
|
||||||
|
|
||||||
|
--! @}
|
||||||
62
doc/index.rst
Normal file
62
doc/index.rst
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
rapidyaml
|
||||||
|
=========
|
||||||
|
|
||||||
|
|
||||||
|
Or ryml, for short. ryml is a C++ library to parse and emit YAML,
|
||||||
|
and do it fast, on everything from x64 to bare-metal chips without
|
||||||
|
operating system.
|
||||||
|
|
||||||
|
And if you're wondering whether ryml's speed comes at the cost of
|
||||||
|
usability, you need not: with ryml, you can have your cake
|
||||||
|
and eat it too. Being rapid is definitely NOT the same as being
|
||||||
|
unpractical, so ryml was written with easy AND efficient usage in
|
||||||
|
mind.
|
||||||
|
|
||||||
|
ryml is available as a single header file, or it can be used as a
|
||||||
|
simple library with cmake -- both separately (ie
|
||||||
|
build -> install -> ``find_package()``) or together with your project (ie with
|
||||||
|
``add_subdirectory()``). (See below for examples).
|
||||||
|
|
||||||
|
ryml can use custom global and per-tree memory allocators and error
|
||||||
|
handler callbacks, and is exception-agnostic. ryml provides a default
|
||||||
|
implementation for the allocator (using ``std::malloc()``) and error
|
||||||
|
handlers (using either exceptions, ``longjmp()`` or ``std::abort()``),
|
||||||
|
but you can opt out of and provide your own memory allocation and
|
||||||
|
error callbacks.
|
||||||
|
|
||||||
|
For maximum portability, ryml does not depend on the STL, ie, it does
|
||||||
|
not use any std container as part of its data structures. But ryml can
|
||||||
|
serialize and deserialize these containers into the data tree, with
|
||||||
|
the use of optional headers. ryml ships with `c4core
|
||||||
|
<https://github.com/biojppm/c4core>`_, a small C++ utilities
|
||||||
|
multiplatform library.
|
||||||
|
|
||||||
|
ryml is written in C++11, and compiles cleanly with:
|
||||||
|
|
||||||
|
* Visual Studio 2015 and later
|
||||||
|
|
||||||
|
* clang++ 3.9 and later
|
||||||
|
|
||||||
|
* g++ 4.8 and later
|
||||||
|
|
||||||
|
* Intel Compiler
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
If you are looking to use an YAML tree with override facilities
|
||||||
|
as the configuration for your programs, take a look at `c4conf
|
||||||
|
<https://github.com/biojppm/c4conf>`_.
|
||||||
|
|
||||||
|
|
||||||
|
Table of contents
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 3
|
||||||
|
|
||||||
|
./sphinx_api_documentation
|
||||||
|
./sphinx_quicklinks
|
||||||
|
./sphinx_is_it_rapid
|
||||||
|
./sphinx_yaml_standard
|
||||||
|
./sphinx_using
|
||||||
|
./sphinx_other_languages
|
||||||
|
./sphinx_alternative_libraries
|
||||||
5
doc/requirements.txt
Normal file
5
doc/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
sphinx-rtd-theme
|
||||||
|
sphinx-mdinclude
|
||||||
|
myst_parser
|
||||||
|
breathe
|
||||||
|
exhale
|
||||||
36
doc/sphinx_alternative_libraries.rst
Normal file
36
doc/sphinx_alternative_libraries.rst
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
Alternative libraries
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Why this library? Because none of the existing libraries was quite what
|
||||||
|
I wanted. When I started this project in 2018, I was aware of these two
|
||||||
|
alternative C/C++ libraries:
|
||||||
|
|
||||||
|
- `libyaml <https://github.com/yaml/libyaml>`__. This is a bare C
|
||||||
|
library. It does not create a representation of the data tree, so I
|
||||||
|
don’t see it as practical. My initial idea was to wrap parsing and
|
||||||
|
emitting around libyaml’s convenient event handling, but to my
|
||||||
|
surprise I found out it makes heavy use of allocations and string
|
||||||
|
duplications when parsing. I briefly pondered on sending PRs to
|
||||||
|
reduce these allocation needs, but not having a permanent tree to
|
||||||
|
store the parsed data was too much of a downside.
|
||||||
|
- `yaml-cpp <https://github.com/jbeder/yaml-cpp>`__. This library may
|
||||||
|
be full of functionality, but is heavy on the use of
|
||||||
|
node-pointer-based structures like ``std::map``, allocations, string
|
||||||
|
copies, polymorphism and slow C++ stream serializations. This is
|
||||||
|
generally a sure way of making your code slower, and strong evidence
|
||||||
|
of this can be seen in the benchmark results above.
|
||||||
|
|
||||||
|
Recently `libfyaml <https://github.com/pantoniou/libfyaml>`__ appeared.
|
||||||
|
This is a newer C library, fully conformant to the YAML standard with an
|
||||||
|
amazing 100% success in the test suite; it also offers the tree as a
|
||||||
|
data structure. As a downside, it does not work in Windows, and it is
|
||||||
|
also multiple times slower parsing and emitting.
|
||||||
|
|
||||||
|
When performance and low latency are important, using contiguous
|
||||||
|
structures for better cache behavior and to prevent the library from
|
||||||
|
trampling caches, parsing in place and using non-owning strings is of
|
||||||
|
central importance. Hence this Rapid YAML library which, with minimal
|
||||||
|
compromise, bridges the gap from efficiency to usability. This library
|
||||||
|
takes inspiration from
|
||||||
|
`RapidJSON <https://github.com/Tencent/rapidjson>`__ and
|
||||||
|
`RapidXML <http://rapidxml.sourceforge.net/>`__.
|
||||||
52
doc/sphinx_api_documentation.rst
Normal file
52
doc/sphinx_api_documentation.rst
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
API documentation
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Please refer to the `Doxygen documentation
|
||||||
|
<./doxygen/index.html>`_, and please read the `overview sample
|
||||||
|
<doxygen/group__doc__quickstart.html#ga1118e0fb55403d0e061626cf8a07cc0c>`_;
|
||||||
|
it will quickly teach you the basic concepts and caveats, which
|
||||||
|
will save you a lot of time.
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: sphinx_try_quickstart.rst
|
||||||
|
|
||||||
|
Here's a short selection from the quickstart overview (`see on
|
||||||
|
doxygen <doxygen/group__doc__quickstart.html>`_ / `see full code
|
||||||
|
on github <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/quickstart.cpp>`_):
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
// Parse YAML code in place, potentially mutating the buffer:
|
||||||
|
char yml_buf[] = "{foo: 1, bar: [2, 3], john: doe}";
|
||||||
|
ryml::Tree tree = ryml::parse_in_place(yml_buf);
|
||||||
|
|
||||||
|
// read from the tree:
|
||||||
|
ryml::NodeRef bar = tree["bar"];
|
||||||
|
CHECK(bar[0].val() == "2");
|
||||||
|
CHECK(bar[1].val() == "3");
|
||||||
|
CHECK(bar[0].val().str == yml_buf + 15); // points at the source buffer
|
||||||
|
CHECK(bar[1].val().str == yml_buf + 17);
|
||||||
|
|
||||||
|
// deserializing:
|
||||||
|
int bar0 = 0, bar1 = 0;
|
||||||
|
bar[0] >> bar0;
|
||||||
|
bar[1] >> bar1;
|
||||||
|
CHECK(bar0 == 2);
|
||||||
|
CHECK(bar1 == 3);
|
||||||
|
|
||||||
|
// serializing:
|
||||||
|
bar[0] << 10; // creates a string in the tree's arena
|
||||||
|
bar[1] << 11;
|
||||||
|
CHECK(bar[0].val() == "10");
|
||||||
|
CHECK(bar[1].val() == "11");
|
||||||
|
|
||||||
|
// add nodes
|
||||||
|
bar.append_child() << 12;
|
||||||
|
CHECK(bar[1].val() == "12");
|
||||||
|
|
||||||
|
// emit to stdout
|
||||||
|
std::cout << tree;
|
||||||
|
|
||||||
|
// emit to std::string
|
||||||
|
CHECK(emitrs_yaml<std::string>(tree) == "{foo: 1,bar: [10,11,12],john: doe}");
|
||||||
160
doc/sphinx_is_it_rapid.rst
Normal file
160
doc/sphinx_is_it_rapid.rst
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
Is it rapid?
|
||||||
|
============
|
||||||
|
|
||||||
|
You bet!
|
||||||
|
|
||||||
|
Compared against the other existing YAML libraries for
|
||||||
|
C/C++
|
||||||
|
- ryml is in general between 2 and 3 times faster than
|
||||||
|
`libyaml <https://github.com/yaml/libyaml>`__
|
||||||
|
- ryml is in general between 10 and 70 times faster than
|
||||||
|
`yaml-cpp <https://github.com/jbeder/yaml-cpp>`__, and in some cases as
|
||||||
|
much as 100x and `even
|
||||||
|
200x <https://github.com/biojppm/c4core/pull/16#issuecomment-700972614>`__
|
||||||
|
faster.
|
||||||
|
|
||||||
|
.. Note::
|
||||||
|
|
||||||
|
While a more effective way of showing the benchmark results is not
|
||||||
|
available yet, you can browse through the `runs of the benchmark
|
||||||
|
workflow in the CI
|
||||||
|
<https://github.com/biojppm/rapidyaml/actions/workflows/benchmarks.yml>`__
|
||||||
|
to scroll through the results for yourself.
|
||||||
|
|
||||||
|
Also, if you have a case where ryml behaves very nicely or not as
|
||||||
|
nicely as claimed here, we would definitely like to see it! Please
|
||||||
|
open an issue, or submit a pull request adding the file to
|
||||||
|
`bm/cases
|
||||||
|
<https://github.com/biojppm/rapidyaml/blob/v0.5.0/bm/cases>`__, or
|
||||||
|
just send us the files.
|
||||||
|
|
||||||
|
`Here’s a parsing benchmark
|
||||||
|
<https://github.com/biojppm/rapidyaml/blob/v0.5.0/bm/bm_parse.cpp>`__. Using
|
||||||
|
different approaches within ryml (in-situ/read-only vs. with/without
|
||||||
|
reuse), a YAML / JSON buffer is repeatedly parsed, and compared
|
||||||
|
against other libraries.
|
||||||
|
|
||||||
|
|
||||||
|
Comparison with yaml-cpp
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
The first result set is for Windows, and is using a `appveyor.yml config
|
||||||
|
file <https://github.com/biojppm/rapidyaml/blob/v0.5.0/bm/cases/appveyor.yml>`__. A comparison of these results is
|
||||||
|
summarized on the table below:
|
||||||
|
|
||||||
|
=========================== ===== ======= ==========
|
||||||
|
Read rates (MB/s) ryml yamlcpp compared
|
||||||
|
=========================== ===== ======= ==========
|
||||||
|
appveyor / vs2017 / Release 101.5 5.3 20x / 5.2%
|
||||||
|
appveyor / vs2017 / Debug 6.4 0.0844 76x / 1.3%
|
||||||
|
=========================== ===== ======= ==========
|
||||||
|
|
||||||
|
The next set of results is taken in Linux, comparing g++ 8.2 and clang++
|
||||||
|
7.0.1 in parsing a YAML buffer from a `travis.yml config
|
||||||
|
file <https://github.com/biojppm/rapidyaml/blob/v0.5.0/bm/cases/travis.yml>`__ or a JSON buffer from a
|
||||||
|
`compile_commands.json file <https://github.com/biojppm/rapidyaml/blob/v0.5.0/bm/cases/compile_commands.json>`__. You
|
||||||
|
can `see the full results
|
||||||
|
here <https://github.com/biojppm/rapidyaml/blob/v0.5.0/results/parse.linux.i7_6800K.md>`__. Summarizing:
|
||||||
|
|
||||||
|
========================== ===== ======= ========
|
||||||
|
Read rates (MB/s) ryml yamlcpp compared
|
||||||
|
========================== ===== ======= ========
|
||||||
|
json / clang++ / Release 453.5 15.1 30x / 3%
|
||||||
|
json / g++ / Release 430.5 16.3 26x / 4%
|
||||||
|
json / clang++ / Debug 61.9 1.63 38x / 3%
|
||||||
|
json / g++ / Debug 72.6 1.53 47x / 2%
|
||||||
|
travis / clang++ / Release 131.6 8.08 16x / 6%
|
||||||
|
travis / g++ / Release 176.4 8.23 21x / 5%
|
||||||
|
travis / clang++ / Debug 10.2 1.08 9x / 1%
|
||||||
|
travis / g++ / Debug 12.5 1.01 12x / 8%
|
||||||
|
========================== ===== ======= ========
|
||||||
|
|
||||||
|
The 450MB/s read rate for JSON puts ryml squarely in the same ballpark
|
||||||
|
as `RapidJSON <https://github.com/Tencent/rapidjson>`__ and other fast
|
||||||
|
json readers (`data from
|
||||||
|
here <https://lemire.me/blog/2018/05/03/how-fast-can-you-parse-json/>`__).
|
||||||
|
Even parsing full YAML is at ~150MB/s, which is still in that
|
||||||
|
performance ballpark, albeit at its lower end. This is something to be
|
||||||
|
proud of, as the YAML specification is much more complex than JSON:
|
||||||
|
`23449 vs 1969
|
||||||
|
words <https://www.arp242.net/yaml-config.html#its-pretty-complex>`__.
|
||||||
|
|
||||||
|
|
||||||
|
Performance reading JSON
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
So how does ryml compare against other JSON readers? Well, it may not
|
||||||
|
be the fastest, but it's definitely ahead of the pack!
|
||||||
|
|
||||||
|
The benchmark is the `same as above <https://github.com/biojppm/rapidyaml/blob/v0.5.0/bm/bm_parse.cpp>`__, and it is
|
||||||
|
reading the
|
||||||
|
`compile_commands.json <https://github.com/biojppm/rapidyaml/blob/v0.5.0/bm/cases/compile_commands.json>`__, The
|
||||||
|
``_arena`` suffix notes parsing a read-only buffer (so buffer copies are
|
||||||
|
performed), while the ``_inplace`` suffix means that the source buffer
|
||||||
|
can be parsed in place. The ``_reuse`` means the data tree and/or parser
|
||||||
|
are reused on each benchmark repeat.
|
||||||
|
|
||||||
|
Here’s what we get with g++ 8.2:
|
||||||
|
|
||||||
|
=================== ============ ==========
|
||||||
|
Benchmark Release,MB/s Debug,MB/s
|
||||||
|
=================== ============ ==========
|
||||||
|
rapidjson_arena 509.9 43.4
|
||||||
|
rapidjson_inplace 1329.4 68.2
|
||||||
|
sajson_inplace 434.2 176.5
|
||||||
|
sajson_arena 430.7 175.6
|
||||||
|
jsoncpp_arena 183.6 ? 187.9
|
||||||
|
nlohmann_json_arena 115.8 21.5
|
||||||
|
yamlcpp_arena 16.6 1.6
|
||||||
|
libyaml_arena 113.9 35.7
|
||||||
|
libyaml_arena_reuse 114.6 35.9
|
||||||
|
ryml_arena 388.6 36.9
|
||||||
|
ryml_inplace 393.7 36.9
|
||||||
|
ryml_arena_reuse 446.2 74.6
|
||||||
|
ryml_inplace_reuse 457.1 74.9
|
||||||
|
=================== ============ ==========
|
||||||
|
|
||||||
|
You can verify that (at least for this test) ryml beats most json
|
||||||
|
parsers at their own game, with the only exception of
|
||||||
|
`rapidjson <https://github.com/Tencent/rapidjson>`__. And actually, in
|
||||||
|
Debug, `rapidjson <https://github.com/Tencent/rapidjson>`__ is slower
|
||||||
|
than ryml, and `sajson <https://github.com/chadaustin/sajson>`__ manages
|
||||||
|
to be faster (but not sure about jsoncpp; need to scrutinize there the
|
||||||
|
suspicious fact that the Debug result is faster than the Release
|
||||||
|
result).
|
||||||
|
|
||||||
|
|
||||||
|
Performance emitting
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
`Emitting benchmarks <https://github.com/biojppm/rapidyaml/blob/v0.5.0/bm/bm_emit.cpp>`__ also show similar speedups from
|
||||||
|
the existing libraries, also anecdotally reported by some users `(eg,
|
||||||
|
here’s a user reporting 25x speedup from
|
||||||
|
yaml-cpp) <https://github.com/biojppm/rapidyaml/issues/28#issue-553855608>`__.
|
||||||
|
Also, in some cases (eg, block folded multiline scalars), the speedup is
|
||||||
|
as high as 200x (eg, 7.3MB/s -> 1.416MG/s).
|
||||||
|
|
||||||
|
|
||||||
|
Serialization performance
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
ryml uses the `charconv facilities
|
||||||
|
<doxygen/group__doc__charconv.html>`__ from `c4core
|
||||||
|
<https://github.com/biojppm/c4core>`__; these functions are blazing
|
||||||
|
fast, and generally outperform the fastest equivalent facilities in
|
||||||
|
the standard library by a significant margin; refer to the
|
||||||
|
documentation of `charconv facilities
|
||||||
|
<doxygen/group__doc__charconv.html>`__ for further details. For
|
||||||
|
example, here are some results for `ryml::xtoa<int64>` (`documentation
|
||||||
|
<doxygen/group__doc__xtoa.html>`__):
|
||||||
|
|
||||||
|
.. |xtoa64_linux| image:: doxygen/linux-x86_64-gxx12.1-Release-c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png
|
||||||
|
:target: doxygen/linux-x86_64-gxx12.1-Release-c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png
|
||||||
|
.. |xtoa64_windows| image:: doxygen/windows-x86_64-vs2019-Release-c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png
|
||||||
|
:target: doxygen/windows-x86_64-vs2019-Release-c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png
|
||||||
|
|
||||||
|
+----------------+--------------------+
|
||||||
|
| g++12, linux | Visual Studio 2019 |
|
||||||
|
+================+====================+
|
||||||
|
| |xtoa64_linux| | |xtoa64_windows| |
|
||||||
|
+----------------+--------------------+
|
||||||
118
doc/sphinx_other_languages.rst
Normal file
118
doc/sphinx_other_languages.rst
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
Other languages
|
||||||
|
===============
|
||||||
|
|
||||||
|
One of the aims of ryml is to provide an efficient YAML API for other
|
||||||
|
languages. JavaScript is fully available, and there is already a cursory
|
||||||
|
implementation for Python using only the low-level API. After ironing
|
||||||
|
out the general approach, other languages are likely to follow (all of
|
||||||
|
this is possible because we’re using `SWIG <http://www.swig.org/>`__,
|
||||||
|
which makes it easy to do so).
|
||||||
|
|
||||||
|
JavaScript
|
||||||
|
----------
|
||||||
|
|
||||||
|
A JavaScript+WebAssembly port is available, compiled through
|
||||||
|
`emscripten <https://emscripten.org/>`__.
|
||||||
|
|
||||||
|
|
||||||
|
Python
|
||||||
|
------
|
||||||
|
|
||||||
|
(Note that this is a work in progress. Additions will be made and things
|
||||||
|
will be changed.) With that said, here’s an example of the Python API:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
import ryml
|
||||||
|
|
||||||
|
# ryml cannot accept strings because it does not take ownership of the
|
||||||
|
# source buffer; only bytes or bytearrays are accepted.
|
||||||
|
src = b"{HELLO: a, foo: b, bar: c, baz: d, seq: [0, 1, 2, 3]}"
|
||||||
|
|
||||||
|
def check(tree):
|
||||||
|
# for now, only the index-based low-level API is implemented
|
||||||
|
assert tree.size() == 10
|
||||||
|
assert tree.root_id() == 0
|
||||||
|
assert tree.first_child(0) == 1
|
||||||
|
assert tree.next_sibling(1) == 2
|
||||||
|
assert tree.first_sibling(5) == 2
|
||||||
|
assert tree.last_sibling(1) == 5
|
||||||
|
# use bytes objects for queries
|
||||||
|
assert tree.find_child(0, b"foo") == 1
|
||||||
|
assert tree.key(1) == b"foo")
|
||||||
|
assert tree.val(1) == b"b")
|
||||||
|
assert tree.find_child(0, b"seq") == 5
|
||||||
|
assert tree.is_seq(5)
|
||||||
|
# to loop over children:
|
||||||
|
for i, ch in enumerate(ryml.children(tree, 5)):
|
||||||
|
assert tree.val(ch) == [b"0", b"1", b"2", b"3"][i]
|
||||||
|
# to loop over siblings:
|
||||||
|
for i, sib in enumerate(ryml.siblings(tree, 5)):
|
||||||
|
assert tree.key(sib) == [b"HELLO", b"foo", b"bar", b"baz", b"seq"][i]
|
||||||
|
# to walk over all elements
|
||||||
|
visited = [False] * tree.size()
|
||||||
|
for n, indentation_level in ryml.walk(tree):
|
||||||
|
# just a dumb emitter
|
||||||
|
left = " " * indentation_level
|
||||||
|
if tree.is_keyval(n):
|
||||||
|
print("{}{}: {}".format(left, tree.key(n), tree.val(n))
|
||||||
|
elif tree.is_val(n):
|
||||||
|
print("- {}".format(left, tree.val(n))
|
||||||
|
elif tree.is_keyseq(n):
|
||||||
|
print("{}{}:".format(left, tree.key(n))
|
||||||
|
visited[inode] = True
|
||||||
|
assert False not in visited
|
||||||
|
# NOTE about encoding!
|
||||||
|
k = tree.get_key(5)
|
||||||
|
print(k) # '<memory at 0x7f80d5b93f48>'
|
||||||
|
assert k == b"seq" # ok, as expected
|
||||||
|
assert k != "seq" # not ok - NOTE THIS!
|
||||||
|
assert str(k) != "seq" # not ok
|
||||||
|
assert str(k, "utf8") == "seq" # ok again
|
||||||
|
|
||||||
|
# parse immutable buffer
|
||||||
|
tree = ryml.parse_in_arena(src)
|
||||||
|
check(tree) # OK
|
||||||
|
|
||||||
|
# parse mutable buffer.
|
||||||
|
# requires bytearrays or objects offering writeable memory
|
||||||
|
mutable = bytearray(src)
|
||||||
|
tree = ryml.parse_in_place(mutable)
|
||||||
|
check(tree) # OK
|
||||||
|
|
||||||
|
As expected, the performance results so far are encouraging. In a
|
||||||
|
`timeit benchmark <api/python/parse_bm.py>`__ compared against
|
||||||
|
`PyYaml <https://pyyaml.org/>`__ and
|
||||||
|
`ruamel.yaml <https://yaml.readthedocs.io/en/latest/>`__, ryml parses
|
||||||
|
quicker by generally 100x and up to 400x:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
+----------------------------------------+-------+----------+----------+-----------+
|
||||||
|
| style_seqs_blck_outer1000_inner100.yml | count | time(ms) | avg(ms) | avg(MB/s) |
|
||||||
|
+----------------------------------------+-------+----------+----------+-----------+
|
||||||
|
| parse:RuamelYamlParse | 1 | 4564.812 | 4564.812 | 0.173 |
|
||||||
|
| parse:PyYamlParse | 1 | 2815.426 | 2815.426 | 0.280 |
|
||||||
|
| parse:RymlParseInArena | 38 | 588.024 | 15.474 | 50.988 |
|
||||||
|
| parse:RymlParseInArenaReuse | 38 | 466.997 | 12.289 | 64.202 |
|
||||||
|
| parse:RymlParseInPlace | 38 | 579.770 | 15.257 | 51.714 |
|
||||||
|
| parse:RymlParseInPlaceReuse | 38 | 462.932 | 12.182 | 64.765 |
|
||||||
|
+----------------------------------------+-------+----------+----------+-----------+
|
||||||
|
|
||||||
|
(Note that the parse timings above are somewhat biased towards ryml,
|
||||||
|
because it does not perform any type conversions in Python-land: return
|
||||||
|
types are merely ``memoryviews`` to the source buffer, possibly copied
|
||||||
|
to the tree’s arena).
|
||||||
|
|
||||||
|
As for emitting, the improvement can be as high as 3000x:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
+----------------------------------------+-------+-----------+-----------+-----------+
|
||||||
|
| style_maps_blck_outer1000_inner100.yml | count | time(ms) | avg(ms) | avg(MB/s) |
|
||||||
|
+----------------------------------------+-------+-----------+-----------+-----------+
|
||||||
|
| emit_yaml:RuamelYamlEmit | 1 | 18149.288 | 18149.288 | 0.054 |
|
||||||
|
| emit_yaml:PyYamlEmit | 1 | 2683.380 | 2683.380 | 0.365 |
|
||||||
|
| emit_yaml:RymlEmitToNewBuffer | 88 | 861.726 | 9.792 | 99.976 |
|
||||||
|
| emit_yaml:RymlEmitReuse | 88 | 437.931 | 4.976 | 196.725 |
|
||||||
|
+----------------------------------------+-------+-----------+-----------+-----------+
|
||||||
23
doc/sphinx_quicklinks.rst
Normal file
23
doc/sphinx_quicklinks.rst
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Quick links
|
||||||
|
===========
|
||||||
|
|
||||||
|
* `API documentation (Doxygen) <./doxygen/index.html>`_
|
||||||
|
|
||||||
|
* `Github repo <https://github.com/biojppm/rapidyaml>`_
|
||||||
|
|
||||||
|
* `Issues <https://github.com/biojppm/rapidyaml/issues>`_
|
||||||
|
|
||||||
|
* `Pull Requests <https://github.com/biojppm/rapidyaml/pull>`_
|
||||||
|
|
||||||
|
* Latest release: `0.5.0 <https://github.com/biojppm/rapidyaml/releases/tag/v0.5.0>`_
|
||||||
|
|
||||||
|
* `Release page [0.5.0] <https://github.com/biojppm/rapidyaml/releases/tag/v0.5.0>`_
|
||||||
|
|
||||||
|
* `README [0.5.0] <https://github.com/biojppm/rapidyaml/blob/v0.5.0/README.md>`_
|
||||||
|
|
||||||
|
|
||||||
|
* Since latest release (master branch):
|
||||||
|
|
||||||
|
* `README [master] <https://github.com/biojppm/rapidyaml/blob/master/README.md>`_
|
||||||
|
|
||||||
|
* `Changelog [master] <https://github.com/biojppm/rapidyaml/blob/master/changelog/current.md>`_
|
||||||
33
doc/sphinx_try_quickstart.rst
Normal file
33
doc/sphinx_try_quickstart.rst
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
.. note::
|
||||||
|
Try the quickstart!
|
||||||
|
|
||||||
|
First create this ``CMakeLists.txt``:
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(my-quickstart LANGUAGES CXX)
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(ryml
|
||||||
|
GIT_REPOSITORY https://github.com/biojppm/rapidyaml.git
|
||||||
|
GIT_TAG v0.5.0
|
||||||
|
GIT_SHALLOW FALSE # ensure submodules are checked out
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(ryml)
|
||||||
|
add_executable(my-quickstart ${ryml_SOURCE_DIR}/samples/quickstart.cpp)
|
||||||
|
target_link_libraries(my-quickstart ryml::ryml)
|
||||||
|
add_custom_target(run my-quickstart
|
||||||
|
COMMAND $<TARGET_FILE:my-quickstart>
|
||||||
|
DEPENDS my-quickstart)
|
||||||
|
|
||||||
|
Now run the following commands in the same folder:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# configure the project
|
||||||
|
cmake -S . -B build
|
||||||
|
# build and run
|
||||||
|
cmake --build build --target ryml-quickstart -j
|
||||||
|
# optionally, open in your IDE
|
||||||
|
cmake --open build
|
||||||
221
doc/sphinx_using.rst
Normal file
221
doc/sphinx_using.rst
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
Using ryml in your project
|
||||||
|
==========================
|
||||||
|
|
||||||
|
|
||||||
|
Quickstart build samples
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
These samples show different ways of getting ryml into your application.
|
||||||
|
All the samples use `the same quickstart executable
|
||||||
|
source <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/quickstart.cpp>`__, but are built in different ways,
|
||||||
|
showing several alternatives to integrate ryml into your project. We
|
||||||
|
also encourage you to refer to the `quickstart
|
||||||
|
docs <doxygen/group__doc__quickstart.html>`__, which extensively cover
|
||||||
|
the functionality that you may want out of ryml.
|
||||||
|
|
||||||
|
If you're in a rush to start, try this:
|
||||||
|
|
||||||
|
.. include:: sphinx_try_quickstart.rst
|
||||||
|
|
||||||
|
For alternative ways, continue reading this section.
|
||||||
|
|
||||||
|
Each sample brings a ``run.sh`` script with the sequence of commands
|
||||||
|
required to successfully build and run the application (this is a bash
|
||||||
|
script and runs in Linux and MacOS, but it is also possible to run in
|
||||||
|
Windows via Git Bash or the WSL). Click on the links below to find out
|
||||||
|
more about each sample:
|
||||||
|
|
||||||
|
|
||||||
|
+-------------------------------------------------------------------------------------------------+----------------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||||
|
| Sample name | ryml is part of build? | cmake file | commands |
|
||||||
|
+=================================================================================================+==================================+==============================================================================================================+=============================================================================================================+
|
||||||
|
| `singleheader <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/singleheader>`_ | | **yes** | `CMakeLists.txt <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/singleheader/CMakeLists.txt>`_ | `run.sh <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/singleheader/run.sh>`_ |
|
||||||
|
| | | ryml brought as a single | | |
|
||||||
|
| | | header, not as a library | | |
|
||||||
|
+-------------------------------------------------------------------------------------------------+----------------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||||
|
| `singleheaderlib <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/singleheaderlib>`_ | | **yes** | `CMakeLists.txt <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/singleheaderlib/CMakeLists.txt>`_ | | `run_shared.sh <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/singleheaderlib/run_shared.sh>`_ |
|
||||||
|
| | | ryml brought as library | | | `run_static.sh <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/singleheaderlib/run_static.sh>`_ |
|
||||||
|
| | | but from the single header | | |
|
||||||
|
+-------------------------------------------------------------------------------------------------+----------------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||||
|
| `add_subdirectory <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/add_subdirectory>`_ | **yes** | `CMakeLists.txt <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/add_subdirectory/CMakeLists.txt>`_ | `run.sh <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/add_subdirectory/run.sh>`_ |
|
||||||
|
+-------------------------------------------------------------------------------------------------+----------------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||||
|
| `fetch_content <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/fetch_content>`_ | **yes** | `CMakeLists.txt <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/fetch_content/CMakeLists.txt>`_ | `run.sh <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/fetch_content/run.sh>`_ |
|
||||||
|
+-------------------------------------------------------------------------------------------------+----------------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||||
|
| `find_package <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/find_package>`_ | | **no** | `CMakeLists.txt <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/find_package/CMakeLists.txt>`_ | `run.sh <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/find_package/run.sh>`_ |
|
||||||
|
| | | needs prior install or package | | |
|
||||||
|
+-------------------------------------------------------------------------------------------------+----------------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
As a single-header
|
||||||
|
------------------
|
||||||
|
|
||||||
|
ryml is provided chiefly as a cmake library project, but it can also
|
||||||
|
be used as a single header file, and there is a `tool to amalgamate
|
||||||
|
<https://github.com/biojppm/rapidyaml/blob/v0.5.0/tools/amalgamate.py>`__
|
||||||
|
the code into a single header file. The amalgamated header file is
|
||||||
|
provided with each release, but you can also generate a customized
|
||||||
|
file suiting your particular needs (or commit):
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
[user@host rapidyaml]$ python3 tools/amalgamate.py -h
|
||||||
|
usage: amalgamate.py [-h] [--c4core | --no-c4core] [--fastfloat | --no-fastfloat] [--stl | --no-stl] [output]
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
output output file. defaults to stdout
|
||||||
|
|
||||||
|
optional arguments:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
--c4core amalgamate c4core together with ryml. this is the default.
|
||||||
|
--no-c4core amalgamate c4core together with ryml. the default is --c4core.
|
||||||
|
--fastfloat enable fastfloat library. this is the default.
|
||||||
|
--no-fastfloat enable fastfloat library. the default is --fastfloat.
|
||||||
|
--stl enable stl interop. this is the default.
|
||||||
|
--no-stl enable stl interop. the default is --stl.
|
||||||
|
|
||||||
|
|
||||||
|
The amalgamated header file contains all the function declarations and
|
||||||
|
definitions. To use it in the project, ``#include`` the header at will
|
||||||
|
in any header or source file in the project, but in one source file,
|
||||||
|
and only in that one source file, ``#define`` the macro
|
||||||
|
``RYML_SINGLE_HDR_DEFINE_NOW`` **before including the header**. This
|
||||||
|
will enable the function definitions. For example:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
// foo.h
|
||||||
|
#include <ryml_all.hpp>
|
||||||
|
|
||||||
|
// foo.cpp
|
||||||
|
// ensure that foo.h is not included before this define!
|
||||||
|
#define RYML_SINGLE_HDR_DEFINE_NOW
|
||||||
|
#include <foo.h>
|
||||||
|
|
||||||
|
If you wish to package the single header into a shared library, then
|
||||||
|
you will need to define the preprocessor symbol ``RYML_SHARED`` during
|
||||||
|
compilation.
|
||||||
|
|
||||||
|
|
||||||
|
From a package manager
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
ryml is available in most package managers (thanks to all the
|
||||||
|
contributors!) and linux distributions.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
These packages are maintained downstream of this repository, so if
|
||||||
|
you have issues with the package, file a report with the respective
|
||||||
|
package maintainer.
|
||||||
|
|
||||||
|
Here's a quick roundup (possibly outdated):
|
||||||
|
|
||||||
|
* Package managers
|
||||||
|
|
||||||
|
* `conan <https://conan.io/center/recipes/rapidyaml>`_
|
||||||
|
|
||||||
|
* `vcpkg <https://vcpkg.io/en/packages.html>`_: ``vcpkg install ryml``
|
||||||
|
|
||||||
|
* `PyPI <https://pypi.org/project/rapidyaml/>`_
|
||||||
|
|
||||||
|
* Linux distributions:
|
||||||
|
|
||||||
|
* Arch Linux/Manjaro:
|
||||||
|
|
||||||
|
* `rapidyaml (aarch64) <https://archlinuxarm.org/packages/aarch64/rapidyaml>`_
|
||||||
|
|
||||||
|
* `rapidyaml-git (AUR) <https://aur.archlinux.org/packages/rapidyaml-git/>`_
|
||||||
|
|
||||||
|
* `python-rapidyaml-git (AUR) <https://aur.archlinux.org/packages/python-rapidyaml-git/>`_
|
||||||
|
|
||||||
|
* `Fedora Linux <https://getfedora.org/>`_/`EPEL <https://docs.fedoraproject.org/en-US/epel/>`_:
|
||||||
|
|
||||||
|
* ``dnf install rapidyaml-devel``
|
||||||
|
|
||||||
|
* ``dnf install python3-rapidyaml``
|
||||||
|
|
||||||
|
* `Gentoo <https://packages.gentoo.org/packages/dev-cpp/rapidyaml>`_
|
||||||
|
|
||||||
|
* `OpenSuse <https://build.openbuildservice.org/package/show/Emulators/rapidyaml>`_
|
||||||
|
|
||||||
|
* `Slackbuilds <https://slackbuilds.org/repository/15.0/libraries/rapidyaml/>`_
|
||||||
|
|
||||||
|
* `AltLinux <https://packages.altlinux.org/en/sisyphus/srpms/rapidyaml/3006055151670528141>`_
|
||||||
|
|
||||||
|
Although package managers are very useful for quickly getting up to
|
||||||
|
speed, the advised way is still to bring ryml as a submodule of your
|
||||||
|
project, building both together. This makes it easy to track any
|
||||||
|
upstream changes in ryml. Also, ryml is small and quick to build, so
|
||||||
|
there's not much of a cost for building it with your project.
|
||||||
|
|
||||||
|
|
||||||
|
As a library
|
||||||
|
------------
|
||||||
|
|
||||||
|
The single header file is a good approach to quickly try the library,
|
||||||
|
but if you wish to make good use of CMake and its tooling ecosystem,
|
||||||
|
(and get better compile times), then ryml has you covered.
|
||||||
|
|
||||||
|
As with any other cmake library, you have the option to integrate ryml into
|
||||||
|
your project's build setup, thereby building ryml together with your
|
||||||
|
project, or -- prior to configuring your project -- you can have ryml
|
||||||
|
installed either manually or through package managers.
|
||||||
|
|
||||||
|
Currently `cmake <https://cmake.org/>`_ is required to build ryml; we
|
||||||
|
recommend a recent cmake version, at least 3.13.
|
||||||
|
|
||||||
|
Note that ryml uses submodules. Take care to use the `--recursive` flag
|
||||||
|
when cloning the repo, to ensure ryml's submodules are checked out as well:
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
git clone --recursive https://github.com/biojppm/rapidyaml
|
||||||
|
|
||||||
|
If you omit `--recursive`, after cloning you
|
||||||
|
will have to do `git submodule update --init --recursive`
|
||||||
|
to ensure ryml's submodules are checked out.
|
||||||
|
|
||||||
|
|
||||||
|
CMake build settings for ryml
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
The following cmake variables can be used to control the build behavior
|
||||||
|
of ryml:
|
||||||
|
|
||||||
|
- ``RYML_WITH_TAB_TOKENS=ON/OFF``. Enable/disable support for tabs as
|
||||||
|
valid container tokens after ``:`` and ``-``. Defaults to ``OFF``,
|
||||||
|
because this may cost up to 10% in processing time.
|
||||||
|
- ``RYML_DEFAULT_CALLBACKS=ON/OFF``. Enable/disable ryml’s default
|
||||||
|
implementation of error and allocation callbacks. Defaults to ``ON``.
|
||||||
|
- ``RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS=ON/OFF`` - Enable/disable the
|
||||||
|
same-named macro, which will make the default error handler provided
|
||||||
|
by ryml throw a ``std::runtime_error`` exception.
|
||||||
|
- ``RYML_USE_ASSERT`` - enable assertions in the code regardless of
|
||||||
|
build type. This is disabled by default. Failed assertions will
|
||||||
|
trigger a call to the error callback.
|
||||||
|
- ``RYML_STANDALONE=ON/OFF``. ryml uses
|
||||||
|
`c4core <https://github.com/biojppm/c4core>`__, a C++ library with
|
||||||
|
low-level multi-platform utilities for C++. When
|
||||||
|
``RYML_STANDALONE=ON``, c4core is incorporated into ryml as if it is
|
||||||
|
the same library. Defaults to ``ON``.
|
||||||
|
|
||||||
|
If you’re developing ryml or just debugging problems with ryml itself,
|
||||||
|
the following cmake variables can be helpful:
|
||||||
|
|
||||||
|
- ``RYML_DEV=ON/OFF``: a bool variable which enables development targets
|
||||||
|
such as unit tests, benchmarks, etc. Defaults to ``OFF``.
|
||||||
|
- ``RYML_DBG=ON/OFF``: a bool variable which enables verbose prints from
|
||||||
|
parsing code; can be useful to figure out parsing problems. Defaults
|
||||||
|
to ``OFF``.
|
||||||
|
|
||||||
|
|
||||||
|
Forcing ryml to use a different c4core version
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
ryml is strongly coupled to c4core, and this is reinforced by the fact
|
||||||
|
that c4core is a submodule of the current repo. However, it is still
|
||||||
|
possible to use a c4core version different from the one in the repo (of
|
||||||
|
course, only if there are no incompatibilities between the versions).
|
||||||
|
You can find out how to achieve this by looking at the
|
||||||
|
`custom_c4core sample <https://github.com/biojppm/rapidyaml/blob/v0.5.0/samples/custom_c4core/CMakeLists.txt>`__.
|
||||||
112
doc/sphinx_yaml_standard.rst
Normal file
112
doc/sphinx_yaml_standard.rst
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
YAML standard conformance
|
||||||
|
=========================
|
||||||
|
|
||||||
|
ryml is feature complete with regards to the YAML specification. All the
|
||||||
|
YAML features are well covered in the unit tests, and expected to work,
|
||||||
|
unless in the exceptions noted below.
|
||||||
|
|
||||||
|
Of course, there are many dark corners in YAML, and there certainly can
|
||||||
|
appear cases which ryml fails to parse. Your `bug reports or pull
|
||||||
|
requests <https://github.com/biojppm/rapidyaml/issues>`__ are very
|
||||||
|
welcome.
|
||||||
|
|
||||||
|
See also `the roadmap <https://github.com/biojppm/rapidyaml/tree/master/ROADMAP.md>`__ for a list of future work.
|
||||||
|
|
||||||
|
Known limitations
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
ryml deliberately makes no effort to follow the standard in the
|
||||||
|
following situations:
|
||||||
|
|
||||||
|
- Containers are not accepted as mapping keys: keys must be scalars.
|
||||||
|
- Tab characters after ``:`` and ``-`` are not accepted tokens, unless
|
||||||
|
ryml is compiled with the macro ``RYML_WITH_TAB_TOKENS``. This
|
||||||
|
requirement exists because checking for tabs introduces branching
|
||||||
|
into the parser’s hot code and in some cases costs as much as 10% in
|
||||||
|
parsing time.
|
||||||
|
- Anchor names must not end with a terminating colon: eg
|
||||||
|
``&anchor: key: val``.
|
||||||
|
- Non-unique map keys are allowed. Enforcing key uniqueness in the
|
||||||
|
parser or in the tree would cause log-linear parsing complexity (for
|
||||||
|
root children on a mostly flat tree), and would increase code size
|
||||||
|
through added structural, logical and cyclomatic complexity. So
|
||||||
|
enforcing uniqueness in the parser would hurt users who may not care
|
||||||
|
about it (they may not care either because non-uniqueness is OK for
|
||||||
|
their use case, or because it is impossible to occur). On the other
|
||||||
|
hand, any user who requires uniqueness can easily enforce it by doing
|
||||||
|
a post-parse walk through the tree. So choosing to not enforce key
|
||||||
|
uniqueness adheres to the spirit of “don’t pay for what you don’t
|
||||||
|
use”.
|
||||||
|
- ``%YAML`` directives have no effect and are ignored.
|
||||||
|
- ``%TAG`` directives are limited to a default maximum of 4 instances
|
||||||
|
per ``Tree``. To increase this maximum, define the preprocessor
|
||||||
|
symbol ``RYML_MAX_TAG_DIRECTIVES`` to a suitable value. This
|
||||||
|
arbitrary limit reflects the usual practice of having at most 1 or 2
|
||||||
|
tag directives; also, be aware that this feature is under
|
||||||
|
consideration for removal in YAML 1.3.
|
||||||
|
|
||||||
|
Also, ryml tends to be on the permissive side where the YAML standard
|
||||||
|
dictates there should be an error; in many of these cases, ryml will
|
||||||
|
tolerate the input. This may be good or bad, but in any case is being
|
||||||
|
improved on (meaning ryml will grow progressively less tolerant of YAML
|
||||||
|
errors in the coming releases). So we strongly suggest to stay away from
|
||||||
|
those dark corners of YAML which are generally a source of problems,
|
||||||
|
which is a good practice anyway.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If you do run into trouble and would like to investigate
|
||||||
|
conformance of your YAML code, **do not use existing online YAML
|
||||||
|
linters**, many of which are not fully conformant; instead, try
|
||||||
|
using `https://play.yaml.io
|
||||||
|
<https://play.yaml.io/main/parser?input=IyBFZGl0IE1lIQoKJVlBTUwgMS4yCi0tLQpmb286IEhlbGxvLCBZQU1MIQpiYXI6IFsxMjMsIHRydWVdCmJhejoKLSBvbmUKLSB0d28KLSBudWxsCg==>`__,
|
||||||
|
an amazing tool which lets you dynamically input your YAML and
|
||||||
|
continuously see the results from all the existing parsers (kudos
|
||||||
|
to @ingydotnet and the people from the YAML test suite). And of
|
||||||
|
course, if you detect anything wrong with ryml, please `open an
|
||||||
|
issue <https://github.com/biojppm/rapidyaml/issues>`__ so that we
|
||||||
|
can improve.
|
||||||
|
|
||||||
|
|
||||||
|
YAML test suite
|
||||||
|
===============
|
||||||
|
|
||||||
|
As part of its CI testing, ryml uses the `YAML test
|
||||||
|
suite <https://github.com/yaml/yaml-test-suite>`__. This is an extensive
|
||||||
|
set of reference cases covering the full YAML spec. Each of these cases
|
||||||
|
have several subparts:
|
||||||
|
|
||||||
|
- ``in-yaml``: mildly, plainly or extremely difficult-to-parse YAML
|
||||||
|
- ``in-json``: equivalent JSON (where possible/meaningful)
|
||||||
|
- ``out-yaml``: equivalent standard YAML
|
||||||
|
- ``emit-yaml``: equivalent standard YAML
|
||||||
|
- ``events``: reference results (ie, expected tree)
|
||||||
|
|
||||||
|
When testing, ryml parses each of the 4 yaml/json parts, then emits the
|
||||||
|
parsed tree, then parses the emitted result and verifies that emission
|
||||||
|
is idempotent, ie that the emitted result is semantically the same as
|
||||||
|
its input without any loss of information. To ensure consistency, this
|
||||||
|
happens over four levels of parse/emission pairs. And to ensure
|
||||||
|
correctness, each of the stages is compared against the ``events`` spec
|
||||||
|
from the test, which constitutes the reference. The tests also check for
|
||||||
|
equality between the reference events in the test case and the events
|
||||||
|
emitted by ryml from the data tree parsed from the test case input. All
|
||||||
|
of this is then carried out combining several variations: both unix
|
||||||
|
``\n`` vs windows ``\r\n`` line endings, emitting to string, file or
|
||||||
|
streams, which results in ~250 tests per case part. With multiple parts
|
||||||
|
per case and ~400 reference cases in the test suite, this makes over
|
||||||
|
several hundred thousand individual tests to which ryml is subjected,
|
||||||
|
which are added to the unit tests in ryml, which also employ the same
|
||||||
|
extensive combinatorial approach.
|
||||||
|
|
||||||
|
Also, note that in `their own words <http://matrix.yaml.io/>`__, the
|
||||||
|
tests from the YAML test suite *contain a lot of edge cases that don’t
|
||||||
|
play such an important role in real world examples*. And yet, despite
|
||||||
|
the extreme focus of the test suite, currently ryml only fails a minor
|
||||||
|
fraction of the test cases, mostly related with the deliberate
|
||||||
|
limitations noted above. Other than those limitations, by far the main
|
||||||
|
issue with ryml is that several standard-mandated parse errors fail to
|
||||||
|
materialize. For the up-to-date list of ryml failures in the test-suite,
|
||||||
|
refer to the `list of known
|
||||||
|
exceptions <https://github.com/biojppm/rapidyaml/blob/v0.5.0/test/test_suite/test_suite_parts.cpp>`__ from ryml’s test
|
||||||
|
suite runner, which is used as part of ryml’s CI process.
|
||||||
Submodule ext/c4core updated: f120195cbf...98f783e462
16
tbump.toml
16
tbump.toml
@@ -34,6 +34,22 @@ search = "c4_project\\(VERSION {current_version}"
|
|||||||
[[file]]
|
[[file]]
|
||||||
src = "test/test_singleheader/CMakeLists.txt"
|
src = "test/test_singleheader/CMakeLists.txt"
|
||||||
search = "c4_project\\(VERSION {current_version}"
|
search = "c4_project\\(VERSION {current_version}"
|
||||||
|
[[file]]
|
||||||
|
src = "doc/conf.py"
|
||||||
|
search = "release\\s*=\\s*['\"]{current_version}['\"]"
|
||||||
|
[[file]]
|
||||||
|
src = "doc/Doxyfile"
|
||||||
|
search = "PROJECT_NUMBER\\s*=\\s*{current_version}"
|
||||||
|
[[file]]
|
||||||
|
src = "samples/quickstart.cpp"
|
||||||
|
search = "GIT_TAG \\s*v{current_version}"
|
||||||
|
[[file]]
|
||||||
|
src = "doc/doxy_main.md"
|
||||||
|
search = ".*{current_version}.*"
|
||||||
|
[[file]]
|
||||||
|
src = "doc/sphinx_*.rst"
|
||||||
|
search = ".*{current_version}.*"
|
||||||
|
|
||||||
|
|
||||||
# You can specify a list of commands to
|
# You can specify a list of commands to
|
||||||
# run after the files have been patched
|
# run after the files have been patched
|
||||||
|
|||||||
Reference in New Issue
Block a user