rapidyaml  0.16.0
parseandemitYAML,anddoitfast
quickstart.cpp
Go to the documentation of this file.
1 // ryml: quickstart
2 
3 /** @addtogroup doc_quickstart
4  *
5  * Best seen online at https://rapidyaml.readthedocs.io/v0.16.0/doxygen/
6  *
7  * This file does a quick tour of ryml. It has multiple self-contained
8  * and well-commented samples that illustrate how to use ryml, and how
9  * it works.
10  *
11  * Although this is not a unit test, the samples are written as a
12  * sequence of actions and predicate checks to better convey what is
13  * the expected result at any stage. And to ensure the code here is
14  * correct and up to date, it's also run as part of the CI tests.
15  *
16  * If something is unclear, please open an issue or send a pull
17  * request at https://github.com/biojppm/rapidyaml . If you have an
18  * issue while using ryml, it is also encouraged to try to reproduce
19  * the issue here, or look first through the relevant section.
20  *
21  * Happy ryml'ing!
22  *
23  *
24  * ### Note on use of raw strings
25  *
26  * This sample uses multiline C strings instead of C++11 R"(raw
27  * strings)" because the doxygen parser is badly broken when handling
28  * raw strings.
29  *
30  *
31  * ### Some guidance on building
32  *
33  * The directories that exist side-by-side with this file contain
34  * several examples on how to build this with cmake, such that you can
35  * hit the ground running. See [the relevant section of the main
36  * README](https://github.com/biojppm/rapidyaml/tree/v0.16.0?tab=readme-ov-file#quickstart-samples)
37  * for an overview of the different choices. I suggest starting first
38  * with the `add_subdirectory` example, treating it just like any
39  * other self-contained cmake project.
40  *
41  * Or very quickly, to build and run this sample on your PC, start by
42  * creating this `CMakeLists.txt`:
43  *
44  * @code{cmake}
45  * cmake_minimum_required(VERSION 3.13)
46  * project(ryml-quickstart LANGUAGES CXX)
47  * include(FetchContent)
48  * FetchContent_Declare(ryml
49  * GIT_REPOSITORY https://github.com/biojppm/rapidyaml.git
50  * GIT_TAG v0.16.0
51  * )
52  * FetchContent_MakeAvailable(ryml)
53  * add_executable(ryml-quickstart ${ryml_SOURCE_DIR}/samples/quickstart.cpp)
54  * target_link_libraries(ryml-quickstart ryml::ryml)
55  * add_custom_target(run ryml-quickstart
56  * COMMAND $<TARGET_FILE:ryml-quickstart>
57  * DEPENDS ryml-quickstart
58  * COMMENT "running: $<TARGET_FILE:ryml-quickstart>")
59  * @endcode
60  *
61  * Now run the following commands in the same folder:
62  *
63  * @code{bash}
64  * # configure the project
65  * cmake -B build
66  * # build and run
67  * cmake --build build --target run -j
68  * # optionally, open in your IDE
69  * cmake --open build
70  * @endcode
71  *
72  */
73 
74 //-----------------------------------------------------------------------------
75 
76 // CONTENTS:
77 //
78 // (Each function addresses a topic and is fully self-contained. Jump
79 // to the function to find out about its topic.)
80 /** @defgroup doc_quickstart_overview Overview
81  * @ingroup doc_quickstart
82  * @{ */
83 void sample_lightning_overview(); ///< lightning overview of most common features
84 void sample_quick_overview(); ///< quick overview of most common features
85 /** @} */
86 /** @defgroup doc_quickstart_substr Using csubstr
87  * @ingroup doc_quickstart
88  * @{ */
89 void sample_substr(); ///< about ryml's string views (from c4core)
90 /** @} */
91 /** @defgroup doc_quickstart_parse Parsing
92  * @ingroup doc_quickstart
93  * @{ */
94 void sample_parse_file(); ///< ready-to-go example of parsing a file from disk
95 void sample_parse_in_place(); ///< parse a mutable YAML source buffer
96 void sample_parse_in_arena(); ///< parse a read-only YAML source buffer
97 void sample_parse_reuse_tree(); ///< parse into an existing tree, maybe into a node
98 void sample_parse_reuse_parser(); ///< reuse an existing parser
99 void sample_parse_reuse_tree_and_parser(); ///< how to reuse existing trees and parsers
100 void sample_parse_style(); ///< shows how rapidyaml retains the style of parsed YAML
101 /** @} */
102 /** @defgroup doc_quickstart_tree Tree
103  * @ingroup doc_quickstart
104  * @{ */
105 void sample_iterate_tree(); ///< visit individual nodes and iterate through trees
106 void sample_location_tracking(); ///< track node YAML source locations in the parsed tree
107 void sample_create_tree(); ///< programatically create trees
108 void sample_create_tree_style(); ///< set node styles while creating trees
109 void sample_tree_arena(); ///< interact with the tree's serialization arena
110 /** @} */
111 /** @defgroup doc_quickstart_serialization Serialization
112  * @ingroup doc_quickstart
113  * @{ */
114 void sample_fundamental_types(); ///< serialize/deserialize fundamental types
115 void sample_empty_null_values(); ///< serialize/deserialize/query empty or null values
116 void sample_formatting(); ///< control formatting when serializing/deserializing
117 void sample_base64(); ///< encode/decode base64
118 void sample_serialize_basic(); ///< serialize/deserialize fundamental types
119 void sample_user_scalar_types(); ///< serialize/deserialize scalar (leaf/scalar) types
120 void sample_user_container_types_brief(); ///< serialize/deserialize container (map or seq) types: brief version
121 void sample_user_container_types(); ///< serialize/deserialize container (map or seq) types
122 void sample_std_types(); ///< serialize/deserialize STL containers
123 void sample_deserialize_error(); ///< shows error on deserializing nested nodes
124 void sample_float_precision(); ///< control precision of serialized floats
125 /** @} */
126 /** @defgroup doc_quickstart_emit Emitting
127  * @ingroup doc_quickstart
128  * @{ */
129 void sample_emit_to_container(); ///< emit to memory, eg a string or vector-like container
130 void sample_emit_to_stream(); ///< emit to a stream, eg std::ostream
131 void sample_emit_to_file(); ///< emit to a FILE*
132 void sample_emit_nested_node(); ///< pick a nested node as the root when emitting
133 /** @} */
134 /** @defgroup doc_quickstart_json JSON
135  * @ingroup doc_quickstart
136  * @{ */
137 void sample_json(); ///< JSON parsing and emitting
138 /** @} */
139 /** @defgroup doc_quickstart_style YAML styles
140  * @ingroup doc_quickstart
141  * @{ */
142 void sample_style(); ///< query/set node styles
143 void sample_style_flow_formatting();///< control formatting of flow containers
144 void sample_style_flow_ml_indent(); ///< control indentation of FLOW_ML1 and FLOW_MLN containers
145 /** @} */
146 /** @defgroup doc_quickstart_anchors YAML anchors
147  * @ingroup doc_quickstart
148  * @{ */
149 void sample_anchors_and_aliases(); ///< deal with YAML anchors and aliases
150 void sample_anchors_and_aliases_create(); ///< how to create YAML anchors and aliases
151 /** @} */
152 /** @defgroup doc_quickstart_tags YAML tags
153  * @ingroup doc_quickstart
154  * @{ */
155 void sample_tags(); ///< deal with YAML type tags
156 void sample_tag_directives(); ///< deal with YAML tag namespace directives
157 /** @} */
158 /** @defgroup doc_quickstart_docs YAML documents
159  * @ingroup doc_quickstart
160  * @{ */
161 void sample_docs(); ///< deal with YAML docs
162 /** @} */
163 /** @defgroup doc_quickstart_error Errors and error handlers
164  * @ingroup doc_quickstart
165  * @{ */
166 void sample_error_handler(); ///< set custom error handlers
167 void sample_error_basic(); ///< handler for basic errors, and obtain a full error message with basic context
168 void sample_error_parse(); ///< handler for parse errors, and obtain a full error message with parse context
169 void sample_error_visit(); ///< handler for visit errors, and obtain a full error message with visit context
170 void sample_error_visit_location(); ///< obtaining the YAML location from a visit error
171 /** @} */
172 /** @defgroup doc_quickstart_allocators Allocators
173  * @ingroup doc_quickstart
174  * @{ */
175 void sample_global_allocator(); ///< set a global allocator for ryml
176 void sample_per_tree_allocator(); ///< set per-tree allocators
177 /** @} */
178 /** @defgroup doc_quickstart_static_trees Static trees
179  * @ingroup doc_quickstart
180  * @{ */
181 void sample_static_trees(); ///< how to use static trees in ryml
182 /** @} */
183 
184 
185 
186 //-----------------------------------------------------------------------------
187 
188 /** @defgroup doc_quickstart_helpers Sample helpers
189  *
190  * Helper utilities used in the quickstart.
191  * @ingroup doc_quickstart
192  */
193 
194 /** @cond dev */
195 void handle_args(int, const char*[]);
196 int report_checks();
197 void ensure_callbacks();
198 /** @endcond */
199 
200 // ryml can be used as a single header, or as a simple library:
201 #if defined(RYML_SINGLE_HEADER) // using the single header directly in the executable
202  #define RYML_SINGLE_HDR_DEFINE_NOW
203  #include <ryml_all.hpp>
204 #elif defined(RYML_SINGLE_HEADER_LIB) // using the single header from a library
205  #include <ryml_all.hpp>
206 #else
207  // umbrella header bringing all of ryml. for compilation speed,
208  // prefer using individual includes.
209  #include <ryml.hpp>
210  // ryml itself does not use any STL container, so <ryml_std.hpp>
211  // is needed if interop with std containers is desired.
212  // This is an umbrella include bringing all the interop
213  // headers implemented in ryml; again, for compilation speed,
214  // prefer using individual includes.
215  #include <ryml_std.hpp> // optional header, provided for std:: interop
216  #include <c4/format.hpp> // needed for the examples below
217  #include <c4/format_base64.hpp> // needed for the examples below
218  // optional header to save/load files:
219  #include <c4/yml/file.hpp>
220  // optional header, definitions for error utilities to implement
221  // user-defined error callbacks:
222  #include <c4/yml/error.def.hpp>
223 #endif
224 
225 
226 // these are needed for the examples below
227 #include <iostream>
228 #include <sstream>
229 #include <vector>
230 #include <map>
231 #ifdef C4_EXCEPTIONS
232 #include <stdexcept>
233 #else
234 #include <csetjmp>
235 #endif
236 
237 
238 //-----------------------------------------------------------------------------
239 
240 int main(int argc, const char* argv[])
241 {
242  handle_args(argc, argv);
246  sample_substr();
262  sample_base64();
274  sample_style();
277  sample_json();
280  sample_tags();
282  sample_docs();
291  return report_checks();
292 }
293 
294 
295 //-----------------------------------------------------------------------------
296 //-----------------------------------------------------------------------------
297 //-----------------------------------------------------------------------------
298 // first, some helpers used in this quickstart
299 
300 /** @addtogroup doc_quickstart_helpers
301  *
302  * @{ */
303 
304 /** used by @ref CHECK() */
305 bool report_check(int line, const char *predicate, bool result);
306 
307 // define the CHECK macro: a glorified assert()
308 #ifdef __DOXYGEN__
309 /// a testing assertion, used only in this quickstart
310 # define CHECK(predicate) assert(predicate) // enable doxygen to link to the functions called inside CHECK()
311 #else
312 // GCC 4.8 has some problems, notably with raw string arguments
313 # if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC_MINOR__ > 8))))
314 # define CHECK(predicate) report_check(__LINE__, #predicate, (predicate))
315 # else
316 # define CHECK CheckPredicate{__LINE__}
317  namespace {
318  struct CheckPredicate
319  {
320  int line;
321  void operator() (bool result) const { report_check(line, nullptr, result); }
322  };
323  } // namespace
324 # endif // GCC 4.8
325 #endif // doxygen
326 
327 
328 
329 /** an error handler used by some of the quickstart examples.
330  * @ingroup doc_quickstart_helpers */
332 {
334  ryml::Callbacks original_callbacks; // saves the original callbacks
335 public:
336  // utilities used below
337  template<class Fn> bool check_error_occurs(Fn &&fn);
338  template<class Fn> bool check_assertion_occurs(Fn &&fn);
339  void check_enabled() const;
340  void check_disabled() const;
342 public:
343  // these are the functions that we want to execute on error
344  [[noreturn]] void on_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const& errdata);
345  [[noreturn]] void on_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const& errdata);
346  [[noreturn]] void on_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const& errdata);
347 public:
348  // these are the trampoline functions that we set ryml to call
349  [[noreturn]] static void s_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const& errdata, void *this_);
350  [[noreturn]] static void s_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const& errdata, void *this_);
351  [[noreturn]] static void s_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const& errdata, void *this_);
352 public:
353  // the handlers save these fields to be able to check on them
354  // after the error is triggered. They are only valid after an
355  // error, and before the next error.
356  std::string saved_msg_short;
357  std::string saved_msg_full;
363 };
364 
365 
366 /** Shows how to create a scoped error handler.
367  * @ingroup doc_quickstart_helpers */
369 {
371  {
373  check_enabled();
374  }
376  {
378  check_disabled();
379  }
380 };
381 
382 // needed to setup the callbacks when ryml does not provide them
383 void ensure_callbacks();
385 
386 
387 C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
388 C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
389 C4_SUPPRESS_WARNING_GCC_CLANG("-Wcast-qual")
390 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
391 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
392 #if defined(__GNUC__) && (__GNUC__ >= 6)
393 C4_SUPPRESS_WARNING_GCC("-Wnull-dereference") // false positives
394 #endif
395 #if defined(__clang__) && (__clang_major__ >= 13)
396 C4_SUPPRESS_WARNING_CLANG("-Wreserved-identifier")
397 #endif
398 
399 
400 /** @} */ // doc_quickstart_helpers
401 
402 
403 //-----------------------------------------------------------------------------
404 //-----------------------------------------------------------------------------
405 //-----------------------------------------------------------------------------
406 
407 /** a lightning tour over most features
408  * see @ref sample_quick_overview */
410 {
411  // Parse YAML code in place, potentially mutating the buffer:
412  char yml_buf[] = "{foo: 1, bar: [2, 3], john: doe}";
413  ryml::Tree tree = ryml::parse_in_place(yml_buf);
414 
415  // read from the tree:
416  ryml::NodeRef bar = tree["bar"];
417  CHECK(bar[0].val() == "2");
418  CHECK(bar[1].val() == "3");
419  CHECK(bar[0].val().str == yml_buf + 15); // points at the source buffer
420  CHECK(bar[1].val().str == yml_buf + 18);
421 
422  // deserializing:
423  int bar0 = 0, bar1 = 0;
424  bar[0].load(&bar0); // also checks the node is readable, and conversion succeeded
425  bar[1].load(&bar1); // see also .deserialize()
426  CHECK(bar0 == 2);
427  CHECK(bar1 == 3);
428 
429  // serializing:
430  bar[0].save(10); // creates a string in the tree's arena
431  bar[1].save(11); // see also .set_serialized()
432  CHECK(bar[0].val() == "10");
433  CHECK(bar[1].val() == "11");
434 
435  // add nodes
436  tree["new"].set_val("node");
437  bar.append_child().save(12);
438  CHECK(bar[2].val() == "12");
439 
440  // emit tree
441  std::string expected = "{foo: 1,bar: [10,11,12],john: doe,new: node}";
442  // emit tree to std::string
443  CHECK(ryml::emitrs_yaml<std::string>(tree) == expected);
444  // emit tree to FILE*
445  ryml::emit_yaml(tree, stdout); printf("\n");
446  // emit tree to ostream
447  std::cout << tree << "\n";
448 
449  // emit node
450  ryml::ConstNodeRef foo = tree["foo"];
451  expected = "foo: 1\n";
452  // emit node to std::string
453  CHECK(ryml::emitrs_yaml<std::string>(foo) == expected);
454  // emit node to FILE*
455  ryml::emit_yaml(foo, stdout);
456  // emit node to ostream
457  std::cout << foo;
458 }
459 
460 
461 //-----------------------------------------------------------------------------
462 
463 /** a brief tour over most features */
465 {
466  // Parse YAML code in place, potentially mutating the buffer:
467  char yml_buf[] = ""
468  "foo: 1" "\n"
469  "bar: [2, 3]" "\n"
470  "john: doe" "\n"
471  "";
472  ryml::Tree tree = ryml::parse_in_place(yml_buf);
473 
474  // The resulting tree contains only views to the parsed string. If
475  // the string was parsed in place, then the string must outlive
476  // the tree! This works here because both `yml_buf` and `tree`
477  // live on the same scope, so have the same lifetime.
478 
479  // It is also possible to:
480  //
481  // - parse a read-only buffer using parse_in_arena(). This
482  // copies the YAML buffer to the tree's arena, and spares the
483  // headache of the string's lifetime.
484  //
485  // - reuse an existing tree (advised)
486  //
487  // - reuse an existing parser (advised)
488  //
489  // - parse into an existing node deep in a tree
490  //
491  // Note: it will always be significantly faster to parse in place
492  // and reuse tree+parser.
493  //
494  // Below you will find samples that show how to achieve reuse; but
495  // please note that for brevity and clarity, many of the examples
496  // here are parsing in the arena, and not reusing tree or parser.
497 
498 
499  //------------------------------------------------------------------
500  // API overview
501 
502  // The ryml tree has a two-level API:
503  //
504  // The lower level index API is based on the indices of nodes,
505  // where the node's id is the node's position in the tree's data
506  // array. This API is very efficient, but somewhat difficult to use:
507  ryml::id_type root_id = tree.root_id();
508  ryml::id_type bar_id = tree.find_child(root_id, "bar"); // need to get the index right
509  CHECK(tree.is_map(root_id)); // all of the index methods are in the tree
510  CHECK(tree.is_seq(bar_id)); // ... and receive the subject index
511 
512  // The node API is a lightweight abstraction sitting on top of the
513  // index API, but offering a much more convenient interaction:
514  ryml::ConstNodeRef root = tree.rootref(); // a const node reference
515  ryml::ConstNodeRef bar = tree["bar"];
516  CHECK(root.is_map());
517  CHECK(bar.is_seq());
518 
519  // A node ref is a lightweight handle to the tree and associated id:
520  CHECK(root.tree() == &tree); // a node ref points at its tree, WITHOUT refcount
521  CHECK(root.id() == root_id); // a node ref's id is the index of the node
522  CHECK(bar.id() == bar_id); // a node ref's id is the index of the node
523 
524  // The node API translates very cleanly to the index API, so most
525  // of the code examples below are using the node API.
526 
527  // WARNING. A node ref holds a raw pointer to the tree. Care must
528  // be taken to ensure the lifetimes match, so that a node will
529  // never access the tree after the tree goes out of scope.
530 
531 
532  //------------------------------------------------------------------
533  // To read the parsed tree
534 
535  // ConstNodeRef::operator[] does a lookup, is O(num_children[node]).
536  CHECK(tree["foo"].is_keyval());
537  CHECK(tree["foo"].val() == "1"); // get the val of a node (must be leaf node, otherwise it is a container and has no val)
538  CHECK(tree["foo"].key() == "foo"); // get the key of a node (must be child of a map, otherwise it has no key)
539  CHECK(tree["bar"].is_seq());
540  CHECK(tree["bar"].has_key());
541  CHECK(tree["bar"].key() == "bar");
542  // maps use string keys, seqs use index keys:
543  CHECK(tree["bar"][0].val() == "2");
544  CHECK(tree["bar"][1].val() == "3");
545  CHECK(tree["john"].val() == "doe");
546  // An index key is the position of the child within its parent,
547  // so even maps can also use int keys, if the key position is
548  // known.
549  CHECK(tree[0].id() == tree["foo"].id());
550  CHECK(tree[1].id() == tree["bar"].id());
551  CHECK(tree[2].id() == tree["john"].id());
552  // Tree::operator[](int) searches a ***root*** child by its position.
553  CHECK(tree[0].id() == tree["foo"].id()); // 0: first child of root
554  CHECK(tree[1].id() == tree["bar"].id()); // 1: second child of root
555  CHECK(tree[2].id() == tree["john"].id()); // 2: third child of root
556  // NodeRef::operator[](int) searches a ***node*** child by its position:
557  CHECK(bar[0].val() == "2"); // 0 means first child of bar
558  CHECK(bar[1].val() == "3"); // 1 means second child of bar
559  // NodeRef::operator[](string):
560  // A string key is the key of the node: lookup is by name. So it
561  // is only available for maps, and it is NOT available for seqs,
562  // since seq members do not have keys.
563  CHECK(tree["foo"].key() == "foo");
564  CHECK(tree["bar"].key() == "bar");
565  CHECK(tree["john"].key() == "john");
566  CHECK(bar.is_seq());
567  // CHECK(bar["BOOM!"].is_seed()); // error, seqs do not have key lookup
568 
569  // Note that maps can also use index keys as well as string keys:
570  CHECK(root["foo"].id() == root[0].id());
571  CHECK(root["bar"].id() == root[1].id());
572  CHECK(root["john"].id() == root[2].id());
573 
574  // IMPORTANT. The ryml tree uses an index-based linked list for
575  // storing children, so the complexity of
576  // `Tree::operator[csubstr]` and `Tree::operator[id_type]` is O(n),
577  // linear on the number of root children. If you use
578  // `Tree::operator[]` with a large tree where the root has many
579  // children, you will see a performance hit.
580  //
581  // To avoid this hit, you can create your own accelerator
582  // structure. For example, before doing a lookup, do a single
583  // traverse at the root level to fill an `map<csubstr,id_type>`
584  // mapping key names to node indices; with a node index, a lookup
585  // (via `Tree::get()`) is O(1), so this way you can get O(log n)
586  // lookup from a key. (But please do not use `std::map` if you
587  // care about performance; use something else like a flat map or
588  // sorted vector).
589  //
590  // As for node refs, the difference from `NodeRef::operator[]` and
591  // `ConstNodeRef::operator[]` to `Tree::operator[]` is that the
592  // latter refers to the root node, whereas the former are invoked
593  // on their target node. But the lookup process works the same for
594  // both and their algorithmic complexity is the same: they are
595  // both linear in the number of direct children. But of course,
596  // depending on the data, that number may be very different from
597  // one to another.
598 
599 
600  //------------------------------------------------------------------
601  // Hierarchy:
602 
603  {
604  ryml::ConstNodeRef foo = root.first_child();
605  ryml::ConstNodeRef john = root.last_child();
606  CHECK(tree.size() == 6); // O(1) number of nodes in the tree
607  CHECK(root.num_children() == 3); // O(num_children[root])
608  CHECK(foo.num_siblings() == 3); // O(num_children[parent(foo)])
609  CHECK(foo.parent().id() == root.id()); // parent() is O(1)
610  CHECK(root.first_child().id() == root["foo"].id()); // first_child() is O(1)
611  CHECK(root.last_child().id() == root["john"].id()); // last_child() is O(1)
612  CHECK(john.first_sibling().id() == foo.id());
613  CHECK(foo.last_sibling().id() == john.id());
614  // prev_sibling(), next_sibling(): (both are O(1))
615  CHECK(foo.num_siblings() == root.num_children());
616  CHECK(foo.prev_sibling().id() == ryml::NONE); // foo is the first_child()
617  CHECK(foo.next_sibling().key() == "bar");
618  CHECK(foo.next_sibling().next_sibling().key() == "john");
619  CHECK(foo.next_sibling().next_sibling().next_sibling().id() == ryml::NONE); // john is the last_child()
620  }
621 
622 
623  //------------------------------------------------------------------
624  // Iterating:
625  {
626  ryml::csubstr expected_keys[] = {"foo", "bar", "john"};
627  // iterate children using the high-level node API:
628  {
629  ryml::id_type count = 0;
630  for(ryml::ConstNodeRef const& child : root.children())
631  CHECK(child.key() == expected_keys[count++]);
632  }
633  // iterate siblings using the high-level node API:
634  {
635  ryml::id_type count = 0;
636  for(ryml::ConstNodeRef const& child : root["foo"].siblings())
637  CHECK(child.key() == expected_keys[count++]);
638  }
639  // iterate children using the lower-level tree index API:
640  {
641  ryml::id_type count = 0;
642  for(ryml::id_type child_id = tree.first_child(root_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
643  CHECK(tree.key(child_id) == expected_keys[count++]);
644  }
645  // iterate siblings using the lower-level tree index API:
646  // (notice the only difference from above is in the loop
647  // preamble, which calls tree.first_sibling(bar_id) instead of
648  // tree.first_child(root_id))
649  {
650  ryml::id_type count = 0;
651  for(ryml::id_type child_id = tree.first_sibling(bar_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
652  CHECK(tree.key(child_id) == expected_keys[count++]);
653  }
654  }
655 
656 
657  //------------------------------------------------------------------
658  // Gotchas:
659 
660  // ryml uses assertions to prevent you from trying to obtain
661  // things that do not exist. For example:
662 
663  {
664  ryml::ConstNodeRef seq_node = tree["bar"];
665  ryml::ConstNodeRef val_node = seq_node[0];
666 
667  CHECK(seq_node.is_seq()); // seq is a container
668  CHECK(!seq_node.has_val()); // ... so it has no val
669  //CHECK(seq_node.val() == BOOM!); // ... so attempting to get a val is undefined behavior
670 
671  CHECK(val_node.parent() == seq_node); // belongs to a seq
672  CHECK(!val_node.has_key()); // ... so it has no key
673  //CHECK(val_node.key() == BOOM!); // ... so attempting to get a key is undefined behavior
674 
675  CHECK(val_node.is_val()); // this node is a val
676  //CHECK(val_node.first_child() == BOOM!); // ... so attempting to get a child is undefined behavior
677 
678  // assertions are also present in methods that /may/ read the val:
679  CHECK(seq_node.is_seq()); // seq is a container
680  //CHECK(seq_node.val_is_null() BOOM!); // so cannot get the val to check
681  }
682 
683  // By default, assertions are enabled unless the NDEBUG macro is
684  // defined (which happens in release builds).
685  //
686  // This adheres to the pay-only-for-what-you-use philosophy: if
687  // you are sure that your intent is correct, why would you need to
688  // pay the runtime cost for the assertions?
689  //
690  // The downside, of course, is that you may be unsure, or your
691  // code may be wrong; and then release builds may end up doing
692  // something wrong.
693  //
694  // So in that case, you can use the appropriate ryml predicates to
695  // check your intent (as in the examples above), or you can
696  // explicitly enable/disable assertions, by defining the macro
697  // RYML_USE_ASSERT to a proper value (see c4/yml/common.hpp). This
698  // will make problematic code trigger a call to the appropriate
699  // error callback.
700  //
701  // Also, to be clear, this does not apply to parse errors
702  // occurring when the YAML is parsed. Parse errors are always
703  // detected by the parser: this is not done through assertions,
704  // but through the appropriate error checking mechanism. So
705  // checking for these errors is always enabled and cannot be
706  // switched off. As for deserialization errors, see below.
707 
708 
709  //------------------------------------------------------------------
710  // Deserializing:
711  //
712  // - use .load(T*)/.load_key(T*) for lazy code with checked reads
713  // - ryml checks preconditions (node readability, etc), or triggers error
714  // - ryml checks deserialization, or triggers error
715  // - more expensive because of all the checks and error marshalling
716  // - optionally, can disable precondition checks with bool parameter
717  //
718  // - use .deserialize()/.deserialize_key() for sure reads
719  // - ryml asserts preconditions (readability, etc) (err on debug builds, UB otherwise)
720  // - ryml returns bool for deserialization status, [[nodiscard]]
721  // - cheaper but potentially dangerous
722  //
723  {
724  // fundamental types: ryml provides deserialization facilities
725  // .load() checks for readability + conversion errors
726  {
727  int foo = 0, bar0 = 0, bar1 = 0;
728  root["foo"].load(&foo); // calls err_visit() on failure
729  root["bar"][0].load(&bar0);
730  root["bar"][1].load(&bar1);
731  CHECK(foo == 1);
732  CHECK(bar0 == 2);
733  CHECK(bar1 == 3);
734  // .load() can also be called from the tree
735  }
736  // .deserialize() asserts readability, user checks conversion errors
737  {
738  int foo = 0, bar0 = 0, bar1 = 0;
739  CHECK(root["foo"].deserialize(&foo)); // user must check return value
740  CHECK(root["bar"][0].deserialize(&bar0));
741  CHECK(root["bar"][1].deserialize(&bar1));
742  CHECK(foo == 1);
743  CHECK(bar0 == 2);
744  CHECK(bar1 == 3);
745  // .deserialize() can also be called from the tree
746  }
747  // std containers: ryml optionally provides deserialization
748  // facilities. see serialization samples below.
749  {
750  std::string john_str, bar_str;
751  root["john"].load(&john_str);
752  root["bar"].load_key(&bar_str);
753  CHECK(john_str == "doe");
754  CHECK(bar_str == "bar");
755  std::vector<int> bar_actual, bar_expected{{2, 3}};
756  root["bar"].load(&bar_actual);
757  CHECK(bar_actual == bar_expected);
758  }
759  }
760 
761 
762  //------------------------------------------------------------------
763  // Modifying existing nodes: assigning vs serializing
764 
765  // As implied by its name, ConstNodeRef is a reference to a const
766  // node. It can be used to read from the node, but not write to it
767  // or modify the hierarchy of the node. If any modification is
768  // desired then a NodeRef must be used instead:
769  ryml::NodeRef wroot = tree.rootref(); // writeable root
770 
771  // .set_val() assigns an existing string to the receiving node.
772  // The contents are NOT copied, and the string pointer will be in
773  // effect until the tree goes out of scope! So BEWARE to only
774  // assign from strings outliving the tree.
775  wroot["foo"].set_val("says you");
776  wroot["bar"][0].set_val("-2");
777  wroot["bar"][1].set_val("-3");
778  wroot["john"].set_val("ron");
779  // Now the tree is _pointing_ at the memory of the strings above.
780  // In this case it is OK because those are static strings, located
781  // in the executable's static section, and will outlive the tree.
782  CHECK(root["foo"].val() == "says you");
783  CHECK(root["bar"][0].val() == "-2");
784  CHECK(root["bar"][1].val() == "-3");
785  CHECK(root["john"].val() == "ron");
786  // But WATCHOUT: do not assign from temporary objects:
787  // {
788  // std::string bad("will dangle");
789  // root["john"] = bad;
790  // }
791  // CHECK(root["john"] == "dangling"); // CRASH! the string was deallocated
792 
793  // For non-string types, or for strings whose lifetime is
794  // problematic WRT the tree, you can create and save a string in
795  // the tree using .set_serialized() (or its equivalent .save() if
796  // you want to have ryml do the error checking). It first
797  // serializes values to a string arena owned by the tree, then
798  // assigns the serialized string to the receiving node. This
799  // avoids constraints with the lifetime, since the arena lives
800  // with the tree.
801  CHECK(tree.arena().empty());
802  wroot["foo"].set_serialized("says who"); // requires to_chars(). see serialization samples below.
803  wroot["bar"][0].set_serialized(20);
804  wroot["bar"][1].set_serialized(30);
805  wroot["john"].set_serialized("deere");
806  CHECK(root["foo"].val() == "says who");
807  CHECK(root["bar"][0].val() == "20");
808  CHECK(root["bar"][1].val() == "30");
809  CHECK(root["john"].val() == "deere");
810  CHECK(tree.arena() == "says who2030deere"); // the result of serializations to the tree arena
811  // with .set_serialize()/.save(), the crash above is avoided:
812  {
813  std::string ok("in_scope");
814  // root["john"].set_val(ok); // don't, will dangle
815  wroot["john"].set_serialized(ok); // OK, copy to the tree's arena
816  }
817  CHECK(root["john"].val() == "in_scope"); // OK! val is now in the tree's arena
818  // serializing floating points:
819  wroot["float"].set_serialized(2.4f);
820  // to force a particular precision or float format:
821  // (see sample_float_precision() and sample_formatting())
822  wroot["digits"].set_serialized(ryml::fmt::real(2.4, /*num_digits*/6, ryml::FTOA_FLOAT));
823  CHECK(tree.arena() == "says who2030deerein_scope2.42.400000"); // the result of serializations to the tree arena
824 
825 
826  //------------------------------------------------------------------
827  // Adding new nodes:
828 
829  // adding a keyval node to a map:
830  CHECK(root.num_children() == 5);
831  wroot["newkeyval"].set_val("shiny and new"); // using these strings
832  c4::yml::NodeRef chsrl = wroot.append_child();
833  chsrl.set_key_serialized("newkeyval (serialized)"); // serializes and assigns the serialization
834  chsrl.set_serialized("shiny and new (serialized)"); // serializes and assigns the serialization
835  CHECK(root.num_children() == 7);
836  CHECK(root["newkeyval"].key() == "newkeyval");
837  CHECK(root["newkeyval"].val() == "shiny and new");
838  CHECK(root["newkeyval (serialized)"].key() == "newkeyval (serialized)");
839  CHECK(root["newkeyval (serialized)"].val() == "shiny and new (serialized)");
840  CHECK( ! tree.in_arena(root["newkeyval"].key())); // it's using directly the static string above
841  CHECK( ! tree.in_arena(root["newkeyval"].val())); // it's using directly the static string above
842  CHECK( tree.in_arena(root["newkeyval (serialized)"].key())); // it's using a serialization of the string above
843  CHECK( tree.in_arena(root["newkeyval (serialized)"].val())); // it's using a serialization of the string above
844  // adding a val node to a seq:
845  CHECK(root["bar"].num_children() == 2);
846  wroot["bar"][2].set_val("oh so nice");
847  wroot["bar"][3].set_serialized("oh so nice (serialized)");
848  CHECK(root["bar"].num_children() == 4);
849  CHECK(root["bar"][2].val() == "oh so nice");
850  CHECK(root["bar"][3].val() == "oh so nice (serialized)");
851  // adding a seq node:
852  CHECK(root.num_children() == 7);
853  wroot["newseq"].set_seq();
854  chsrl = wroot.append_child();
855  chsrl.set_key_serialized("newseq (serialized)");
856  chsrl.set_seq();
857  CHECK(root.num_children() == 9);
858  CHECK(root["newseq"].num_children() == 0);
859  CHECK(root["newseq"].is_seq());
860  CHECK(root["newseq (serialized)"].num_children() == 0);
861  CHECK(root["newseq (serialized)"].is_seq());
862  // adding a map node:
863  CHECK(root.num_children() == 9);
864  wroot["newmap"].set_map();
865  chsrl = wroot.append_child();
866  chsrl.set_key_serialized("newmap (serialized)");
867  chsrl.set_map();
868  CHECK(root.num_children() == 11);
869  CHECK(root["newmap"].num_children() == 0);
870  CHECK(root["newmap"].is_map());
871  CHECK(root["newmap (serialized)"].num_children() == 0);
872  CHECK(root["newmap (serialized)"].is_map());
873  //
874  // When the tree is const, operator[] searches the tree for a
875  // matching key/position, and returns it, or raises an error if
876  // none is found.
877  //
878  // When the tree is mutable, operator[] will not mutate the tree
879  // until the returned node is written to. operator[] first
880  // searches the tree for a node matching the key/position and
881  // returns it if it exists; otherwise, the result is a NodeRef
882  // object which keeps in itself the required information to write
883  // to the proper place in the tree. This is called being in a
884  // "seed" state.
885  //
886  // This means that passing a key/position which does not exist
887  // will not mutate the tree, but will instead store (in the
888  // returned NodeRef) the proper place of the tree to be able to do
889  // so, if and when it is required. This is why the node is said to
890  // be in "seed" state - it allows creating the entry in the tree
891  // in the future.
892  //
893  // This is a significant difference from eg, the behavior of
894  // std::map, which mutates the map immediately within the call to
895  // operator[].
896  //
897  // All of the points above apply only if the tree is mutable. If
898  // the tree is const, then a NodeRef cannot be obtained from it;
899  // only a ConstNodeRef, which can never be used to mutate the
900  // tree.
901  //
902  CHECK(!root.has_child("I am not nothing"));
903  ryml::NodeRef nothing;
904  CHECK(nothing.invalid()); // invalid because it points at nothing
905  nothing = wroot["I am nothing"];
906  CHECK(!nothing.invalid()); // points at the tree, and a specific place in the tree
907  CHECK(nothing.is_seed()); // ... but nothing is there yet.
908  CHECK(!root.has_child("I am nothing")); // same as above
909  CHECK(!nothing.readable()); // ... and this node cannot be used to
910  // read anything from the tree
911  ryml::NodeRef something = wroot["I am something"];
912  ryml::ConstNodeRef constsomething = wroot["I am something"];
913  CHECK(!root.has_child("I am something")); // same as above
914  CHECK(!something.invalid());
915  CHECK(something.is_seed()); // same as above
916  CHECK(!something.readable()); // same as above
917  CHECK(constsomething.invalid()); // NOTE: because a ConstNodeRef cannot be
918  // used to mutate a tree, it is only valid()
919  // if it is pointing at an existing node.
920  something.set_val("indeed"); // this will commit the seed to the tree, mutating at the proper place
921  CHECK(root.has_child("I am something"));
922  CHECK(root["I am something"].val() == "indeed");
923  CHECK(!something.invalid()); // it was already valid
924  CHECK(!something.is_seed()); // now the tree has this node, so the
925  // ref is no longer a seed
926  CHECK(something.readable()); // and it is now readable
927  //
928  // now the constref is also valid (but it needs to be reassigned):
929  ryml::ConstNodeRef constsomethingnew = wroot["I am something"];
930  CHECK(!constsomethingnew.invalid());
931  CHECK(constsomethingnew.readable());
932  // note that the old constref is now stale, because it only keeps
933  // the state at creation:
934  CHECK(constsomething.invalid());
935  CHECK(!constsomething.readable());
936  //
937  // -----------------------------------------------------------
938  // Remember: a seed node cannot be used to read from the tree!
939  // -----------------------------------------------------------
940  //
941  // The seed node needs to be created and become readable first.
942  //
943  // Trying to invoke any tree-reading method on a node that is not
944  // readable will cause an assertion (see RYML_USE_ASSERT).
945  //
946  // It is your responsibility to verify that the preconditions are
947  // met. If you are not sure about the structure of your data,
948  // write your code defensively to signify your full intent:
949  //
950  ryml::NodeRef wbar = wroot["bar"];
951  if(wbar.readable() && wbar.is_seq()) // .is_seq() requires .readable()
952  {
953  CHECK(wbar[0].readable() && wbar[0].val() == "20");
954  CHECK( ! wbar[100].readable());
955  CHECK( ! wbar[100].readable() || wbar[100].val() == "100"); // <- no crash because it is not .readable(), so never tries to call .val()
956  // this would work as well:
957  CHECK( ! wbar[0].is_seed() && wbar[0].val() == "20");
958  CHECK(wbar[100].is_seed() || wbar[100].val() == "100");
959  }
960 
961 
962  //------------------------------------------------------------------
963  // .operator[]() vs .at()
964 
965  // (Const)NodeRef::operator[] is an analogue to std::vector::operator[].
966  // (Const)NodeRef::at() is an analogue to std::vector::at()
967  //
968  // at() will always check the subject node is .readable().
969  //
970  // [] is meant for the happy path: unverified in Release builds,
971  // and asserts .readable() in Debug builds (more specifically when
972  // RYML_USE_ASSERT is defined and NDEBUG is not defined).
973  {
974  // in this example we will be checking errors, so set up a
975  // temporary error handler to catch them:
976  ScopedErrorHandlerExample errh; // calls ryml::set_callbacks()
977  // instantiate the tree after errh
978  ryml::Tree err_tree = ryml::parse_in_arena("{foo: bar}");
979  // ... so that the tree uses the current callbacks:
980  CHECK(err_tree.callbacks() == errh.callbacks());
981  // node does not exist, only a seed node
982  ryml::NodeRef seed_node = err_tree["this"];
983  // ... therefore not .readable()
984  CHECK(!seed_node.readable());
985  // using .at() reliably produces an error:
986  CHECK(errh.check_error_occurs([&]{
987  return seed_node.at("is").at("an").at("invalid").at("operation");
988  // ^
989  // error occurs here because it is unreadable
990  }));
991  // ... but using [] fails only when RYML_USE_ASSERT is
992  // defined. otherwise, it's the dreaded Undefined Behavior:
993  CHECK(errh.check_assertion_occurs([&]{
994  return seed_node["is"]["an"]["invalid"]["operation"];
995  // ^
996  // assertion occurs here because it is unreadable
997  }));
998  }
999 
1000 
1001  //------------------------------------------------------------------
1002  // Emitting:
1003 
1004  ryml::csubstr expected_result =
1005  "foo: says who" "\n"
1006  "bar: [20,30,oh so nice,oh so nice (serialized)]" "\n"
1007  "john: in_scope" "\n"
1008  "float: 2.4" "\n"
1009  "digits: 2.400000" "\n"
1010  "newkeyval: shiny and new" "\n"
1011  "newkeyval (serialized): shiny and new (serialized)" "\n"
1012  "newseq: []" "\n"
1013  "newseq (serialized): []" "\n"
1014  "newmap: {}" "\n"
1015  "newmap (serialized): {}" "\n"
1016  "I am something: indeed" "\n"
1017  "";
1018 
1019  // emit to a FILE*
1020  ryml::emit_yaml(tree, stdout);
1021  // emit to a stream
1022  // (this is templated on the stream, so it works both with
1023  // standard streams like std::ostream/std::stringstream, or with
1024  // user-defined streams not inheriting from ostream)
1025  std::stringstream ss;
1026  ss << tree;
1027  std::string stream_result = ss.str();
1028  // emit to a buffer:
1029  std::string str_result = ryml::emitrs_yaml<std::string>(tree);
1030  // can emit to any given buffer:
1031  char buf[1024];
1032  ryml::csubstr buf_result = ryml::emit_yaml(tree, buf);
1033  // now check
1034  CHECK(buf_result == expected_result);
1035  CHECK(str_result == expected_result);
1036  CHECK(stream_result == expected_result);
1037  // There are many possibilities to emit to buffer;
1038  // please look at the emit sample functions below.
1039 
1040 
1041  //------------------------------------------------------------------
1042  // ConstNodeRef vs NodeRef
1043 
1044  ryml::NodeRef noderef = tree["bar"][0];
1045  ryml::ConstNodeRef constnoderef = tree["bar"][0];
1046 
1047  // ConstNodeRef cannot be used to mutate the tree:
1048  //constnoderef.set_val("21"); // compile error: method does not exist
1049  //constnoderef.set_serialized("22"); // compile error
1050  // ... but a NodeRef can:
1051  noderef.set_val("21"); // ok, can assign because it's not const
1052  CHECK(tree["bar"][0].val() == "21");
1053  noderef.set_serialized("22"); // ok, can serialize and assign because it's not const
1054  CHECK(tree["bar"][0].val() == "22");
1055 
1056  // it is not possible to obtain a NodeRef from a ConstNodeRef:
1057  // noderef = constnoderef; // compile error
1058 
1059  // it is always possible to obtain a ConstNodeRef from a NodeRef:
1060  constnoderef = noderef; // ok can assign const <- nonconst
1061 
1062  // If a tree is const, then only ConstNodeRef's can be
1063  // obtained from that tree:
1064  ryml::Tree const& consttree = tree;
1065  //noderef = consttree["bar"][0]; // compile error
1066  noderef = tree["bar"][0]; // ok
1067  constnoderef = consttree["bar"][0]; // ok
1068 
1069  // ConstNodeRef and NodeRef can be compared for equality.
1070  // Equality means they point at the same node.
1071  CHECK(constnoderef == noderef);
1072  CHECK(!(constnoderef != noderef));
1073 
1074 
1075  //------------------------------------------------------------------
1076  // Getting the location of nodes in the source:
1077  //
1078  // Location tracking is opt-in:
1079  ryml::EventHandlerTree evt_handler = {};
1080  ryml::Parser parser(&evt_handler, ryml::ParserOptions().locations(true));
1081  // Now the parser will start by building the accelerator structure:
1082  ryml::Tree tree2 = parse_in_arena(&parser, "expected.yml", expected_result);
1083  // ... and use it when querying
1084  ryml::ConstNodeRef subject_node = tree2["bar"][1];
1085  CHECK(subject_node.val() == "30");
1086  ryml::Location loc = subject_node.location(parser);
1087  CHECK(parser.location_contents(loc).begins_with("30"));
1088  CHECK(loc.line == 1u);
1089  CHECK(loc.col == 9u);
1090  // For further details in location tracking,
1091  // refer to the sample function below.
1092 
1093 
1094  //------------------------------------------------------------------
1095  // Dealing with UTF8
1096  ryml::Tree langs = ryml::parse_in_arena(""
1097  "#" "\n"
1098  "# UTF8/16/32 can be placed directly in any scalar:" "\n"
1099  "#" "\n"
1100  "en: Planet (Gas)" "\n"
1101  "fr: Planète (Gazeuse)" "\n"
1102  "ru: Планета (Газ)" "\n"
1103  "ja: 惑星(ガス)" "\n"
1104  "zh: 行星(气体)" "\n"
1105  "#" "\n"
1106  "# UTF8 decoding only happens in double-quoted strings," "\n"
1107  "# as per the YAML standard" "\n"
1108  "#" "\n"
1109  "decode this: \"\\u263A c\\x61f\\xE9\"" "\n"
1110  "and this as well: \"\\u2705 \\U0001D11E\"" "\n"
1111  "not decoded: '\\u263A \\xE2\\x98\\xBA'" "\n"
1112  "neither this: '\\u2705 \\U0001D11E'" "\n");
1113  // in-place UTF8 just works:
1114  CHECK(langs["en"].val() == "Planet (Gas)");
1115  CHECK(langs["fr"].val() == "Planète (Gazeuse)");
1116  CHECK(langs["ru"].val() == "Планета (Газ)");
1117  CHECK(langs["ja"].val() == "惑星(ガス)");
1118  CHECK(langs["zh"].val() == "行星(气体)");
1119  // and \x \u \U codepoints are decoded, but only when they appear
1120  // inside double-quoted strings, as dictated by the YAML
1121  // standard:
1122  CHECK(langs["decode this"].val() == "☺ café");
1123  CHECK(langs["and this as well"].val() == "✅ 𝄞");
1124  CHECK(langs["not decoded"].val() == "\\u263A \\xE2\\x98\\xBA");
1125  CHECK(langs["neither this"].val() == "\\u2705 \\U0001D11E");
1126 }
1127 
1128 
1129 //-----------------------------------------------------------------------------
1130 
1131 /** demonstrate usage of ryml::substr and ryml::csubstr
1132  *
1133  * These types are imported from the c4core library into the ryml
1134  * namespace You may have noticed above the use of a `csubstr`
1135  * class. This class is defined in another library,
1136  * [c4core](https://github.com/biojppm/c4core), which is imported by
1137  * ryml. This is a library I use with my projects consisting of
1138  * multiplatform low-level utilities. One of these is `c4::csubstr`
1139  * (the name comes from "constant substring") which is a non-owning
1140  * read-only string view, with many methods that make it practical to
1141  * use (I would certainly argue more practical than `std::string`). In
1142  * fact, `c4::csubstr` and its writeable counterpart `c4::substr` are
1143  * the workhorses of the ryml parsing and serialization code.
1144  *
1145  * @see doc_substr */
1147 {
1148  // substr is a mutable view: pointer and length to a string in memory.
1149  // csubstr is a const-substr (immutable).
1150 
1151  // construct from explicit args
1152  {
1153  const char foobar_str[] = "foobar";
1154  auto s = ryml::csubstr(foobar_str, strlen(foobar_str));
1155  CHECK(s == "foobar");
1156  CHECK(s.size() == 6);
1157  CHECK(s.data() == foobar_str);
1158  CHECK(s.size() == s.len);
1159  CHECK(s.data() == s.str);
1160  }
1161 
1162  // construct from a string array
1163  {
1164  const char foobar_str[] = "foobar";
1165  ryml::csubstr s = foobar_str;
1166  CHECK(s == "foobar");
1167  CHECK(s != "foobar0");
1168  CHECK(s.size() == 6); // does not include the terminating \0
1169  CHECK(s.data() == foobar_str);
1170  CHECK(s.size() == s.len);
1171  CHECK(s.data() == s.str);
1172  }
1173  // you can also declare directly in-place from an array:
1174  {
1175  ryml::csubstr s = "foobar";
1176  CHECK(s == "foobar");
1177  CHECK(s != "foobar0");
1178  CHECK(s.size() == 6);
1179  CHECK(s.size() == s.len);
1180  CHECK(s.data() == s.str);
1181  }
1182 
1183  // construct from a C-string:
1184  //
1185  // Since the input is only a pointer, the string length can only
1186  // be found with a call to strlen(). To make this cost evident, we
1187  // require calling to_csubstr():
1188  {
1189  const char *foobar_str = "foobar";
1190  ryml::csubstr s = ryml::to_csubstr(foobar_str);
1191  CHECK(s == "foobar");
1192  CHECK(s != "foobar0");
1193  CHECK(s.size() == 6);
1194  CHECK(s.size() == s.len);
1195  CHECK(s.data() == s.str);
1196  }
1197 
1198  // construct from a std::string: same approach as above.
1199  // requires inclusion of the <ryml/std/string.hpp> header
1200  // or of the umbrella header <ryml_std.hpp>.
1201  //
1202  // not including <string> in the default header is a deliberate
1203  // design choice to avoid including the heavy std:: allocation
1204  // machinery
1205  {
1206  std::string foobar_str = "foobar";
1207  ryml::csubstr s = ryml::to_csubstr(foobar_str); // defined in <ryml/std/string.hpp>
1208  CHECK(s == "foobar");
1209  CHECK(s != "foobar0");
1210  CHECK(s.size() == 6);
1211  CHECK(s.size() == s.len);
1212  CHECK(s.data() == s.str);
1213  }
1214 
1215  // convert substr -> csubstr
1216  {
1217  char buf[] = "foo";
1218  ryml::substr foo = buf;
1219  CHECK(foo.len == 3);
1220  CHECK(foo.data() == buf);
1221  ryml::csubstr cfoo = foo;
1222  CHECK(cfoo.data() == buf);
1223  }
1224  // cannot convert csubstr -> substr:
1225  {
1226  // ryml::substr foo2 = cfoo; // compile error: cannot write to csubstr
1227  }
1228 
1229  // construct from char[]/const char[]: mutable vs immutable memory
1230  {
1231  char const foobar_str_ro[] = "foobar"; // ro := read-only
1232  char foobar_str_rw[] = "foobar"; // rw := read-write
1233  static_assert(std::is_array<decltype(foobar_str_ro)>::value, "this is an array");
1234  static_assert(std::is_array<decltype(foobar_str_rw)>::value, "this is an array");
1235  // csubstr <- read-only memory
1236  {
1237  ryml::csubstr foobar = foobar_str_ro;
1238  CHECK(foobar.data() == foobar_str_ro);
1239  CHECK(foobar.size() == strlen(foobar_str_ro));
1240  CHECK(foobar == "foobar"); // AKA strcmp
1241  }
1242  // csubstr <- read-write memory: you can create an immutable csubstr from mutable memory
1243  {
1244  ryml::csubstr foobar = foobar_str_rw;
1245  CHECK(foobar.data() == foobar_str_rw);
1246  CHECK(foobar.size() == strlen(foobar_str_rw));
1247  CHECK(foobar == "foobar"); // AKA strcmp
1248  }
1249  // substr <- read-write memory.
1250  {
1251  ryml::substr foobar = foobar_str_rw;
1252  CHECK(foobar.data() == foobar_str_rw);
1253  CHECK(foobar.size() == strlen(foobar_str_rw));
1254  CHECK(foobar == "foobar"); // AKA strcmp
1255  }
1256  // substr <- ro is impossible.
1257  {
1258  //ryml::substr foobar = foobar_str_ro; // compile error!
1259  }
1260  }
1261 
1262  // construct from char*/const char*: mutable vs immutable memory.
1263  // use to_substr()/to_csubstr()
1264  {
1265  char const* foobar_str_ro = "foobar"; // ro := read-only
1266  char foobar_str_rw_[] = "foobar"; // rw := read-write
1267  char * foobar_str_rw = foobar_str_rw_; // rw := read-write
1268  static_assert(!std::is_array<decltype(foobar_str_ro)>::value, "this is a decayed pointer");
1269  static_assert(!std::is_array<decltype(foobar_str_rw)>::value, "this is a decayed pointer");
1270  // csubstr <- read-only memory
1271  {
1272  //ryml::csubstr foobar = foobar_str_ro; // compile error: length is not known
1273  ryml::csubstr foobar = ryml::to_csubstr(foobar_str_ro);
1274  CHECK(foobar.data() == foobar_str_ro);
1275  CHECK(foobar.size() == strlen(foobar_str_ro));
1276  CHECK(foobar == "foobar"); // AKA strcmp
1277  }
1278  // csubstr <- read-write memory: you can create an immutable csubstr from mutable memory
1279  {
1280  ryml::csubstr foobar = ryml::to_csubstr(foobar_str_rw);
1281  CHECK(foobar.data() == foobar_str_rw);
1282  CHECK(foobar.size() == strlen(foobar_str_rw));
1283  CHECK(foobar == "foobar"); // AKA strcmp
1284  }
1285  // substr <- read-write memory.
1286  {
1287  ryml::substr foobar = ryml::to_substr(foobar_str_rw);
1288  CHECK(foobar.data() == foobar_str_rw);
1289  CHECK(foobar.size() == strlen(foobar_str_rw));
1290  CHECK(foobar == "foobar"); // AKA strcmp
1291  }
1292  // substr <- read-only is impossible.
1293  {
1294  //ryml::substr foobar = ryml::to_substr(foobar_str_ro); // compile error!
1295  }
1296  }
1297 
1298  // substr is mutable, without changing the size:
1299  {
1300  char buf[] = "foobar";
1301  ryml::substr foobar = buf;
1302  CHECK(foobar == "foobar");
1303  foobar[0] = 'F'; CHECK(foobar == "Foobar");
1304  foobar.back() = 'R'; CHECK(foobar == "FoobaR");
1305  foobar.reverse(); CHECK(foobar == "RabooF");
1306  foobar.reverse(); CHECK(foobar == "FoobaR");
1307  foobar.reverse_sub(1, 4); CHECK(foobar == "FabooR");
1308  foobar.reverse_sub(1, 4); CHECK(foobar == "FoobaR");
1309  foobar.reverse_range(2, 5); CHECK(foobar == "FoaboR");
1310  foobar.reverse_range(2, 5); CHECK(foobar == "FoobaR");
1311  foobar.replace('o', '0'); CHECK(foobar == "F00baR");
1312  foobar.replace('a', '_'); CHECK(foobar == "F00b_R");
1313  foobar.replace("_0b", 'a'); CHECK(foobar == "FaaaaR");
1314  foobar.toupper(); CHECK(foobar == "FAAAAR");
1315  foobar.tolower(); CHECK(foobar == "faaaar");
1316  foobar.fill('.'); CHECK(foobar == "......");
1317  // see also:
1318  // - .erase()
1319  // - .replace_all()
1320  }
1321 
1322  // sub-views
1323  {
1324  ryml::csubstr s = "fooFOObarBAR";
1325  CHECK(s.len == 12u);
1326  // sub(): <- first,[num]
1327  CHECK(s.sub(0) == "fooFOObarBAR");
1328  CHECK(s.sub(0, 12) == "fooFOObarBAR");
1329  CHECK(s.sub(0, 3) == "foo" );
1330  CHECK(s.sub(3) == "FOObarBAR");
1331  CHECK(s.sub(3, 3) == "FOO" );
1332  CHECK(s.sub(6) == "barBAR");
1333  CHECK(s.sub(6, 3) == "bar" );
1334  CHECK(s.sub(9) == "BAR");
1335  CHECK(s.sub(9, 3) == "BAR");
1336  // first(): <- length
1337  CHECK(s.first(0) == "" );
1338  CHECK(s.first(1) == "f" );
1339  CHECK(s.first(2) != "f" );
1340  CHECK(s.first(2) == "fo" );
1341  CHECK(s.first(3) == "foo");
1342  // last(): <- length
1343  CHECK(s.last(0) == "");
1344  CHECK(s.last(1) == "R");
1345  CHECK(s.last(2) == "AR");
1346  CHECK(s.last(3) == "BAR");
1347  // range(): <- first, last
1348  CHECK(s.range(0, 12) == "fooFOObarBAR");
1349  CHECK(s.range(1, 12) == "ooFOObarBAR");
1350  CHECK(s.range(1, 11) == "ooFOObarBA" );
1351  CHECK(s.range(2, 10) == "oFOObarB" );
1352  CHECK(s.range(3, 9) == "FOObar" );
1353  // offs(): offset from beginning, end
1354  CHECK(s.offs(0, 0) == "fooFOObarBAR");
1355  CHECK(s.offs(1, 0) == "ooFOObarBAR");
1356  CHECK(s.offs(1, 1) == "ooFOObarBA" );
1357  CHECK(s.offs(2, 1) == "oFOObarBA" );
1358  CHECK(s.offs(2, 2) == "oFOObarB" );
1359  CHECK(s.offs(3, 3) == "FOObar" );
1360  // right_of(): <- pos, include_pos
1361  CHECK(s.right_of(0, true) == "fooFOObarBAR");
1362  CHECK(s.right_of(0, false) == "ooFOObarBAR");
1363  CHECK(s.right_of(1, true) == "ooFOObarBAR");
1364  CHECK(s.right_of(1, false) == "oFOObarBAR");
1365  CHECK(s.right_of(2, true) == "oFOObarBAR");
1366  CHECK(s.right_of(2, false) == "FOObarBAR");
1367  CHECK(s.right_of(3, true) == "FOObarBAR");
1368  CHECK(s.right_of(3, false) == "OObarBAR");
1369  // left_of() <- pos, include_pos
1370  CHECK(s.left_of(12, false) == "fooFOObarBAR");
1371  CHECK(s.left_of(11, true) == "fooFOObarBAR");
1372  CHECK(s.left_of(11, false) == "fooFOObarBA" );
1373  CHECK(s.left_of(10, true) == "fooFOObarBA" );
1374  CHECK(s.left_of(10, false) == "fooFOObarB" );
1375  CHECK(s.left_of( 9, true) == "fooFOObarB" );
1376  CHECK(s.left_of( 9, false) == "fooFOObar" );
1377  // left_of(),right_of() <- substr
1378  ryml::csubstr FOO = s.sub(3, 3);
1379  CHECK(s.is_super(FOO)); // required for the following
1380  CHECK(s.left_of(FOO) == "foo");
1381  CHECK(s.right_of(FOO) == "barBAR");
1382  }
1383 
1384  // printing a substr/csubstr using printf-like
1385  {
1386  ryml::csubstr s = "some substring";
1387  ryml::csubstr some = s.first(4);
1388  CHECK(some == "some");
1389  CHECK(s == "some substring");
1390  // To print a csubstr using printf(), use the %.*s format specifier:
1391  {
1392  char result[32] = {0};
1393  std::snprintf(result, sizeof(result), "%.*s", (int)some.len, some.str);
1394  printf("~~~%s~~~\n", result);
1395  CHECK(ryml::to_csubstr((const char*)result) == "some");
1396  CHECK(ryml::to_csubstr((const char*)result) == some);
1397  }
1398  // But NOTE: because this is a string view type, in general
1399  // the C-string is NOT zero terminated. So NEVER printf it
1400  // directly, or it will overflow past the end of the given
1401  // substr, with a potential unbounded access. For example,
1402  // this is really bad:
1403  {
1404  char result[32] = {0};
1405  std::snprintf(result, sizeof(result), "%s", some.str); // ERROR! do not print the c-string directly
1406  CHECK(ryml::to_csubstr((const char*)result) == "some substring");
1407  CHECK(ryml::to_csubstr((const char*)result) == s);
1408  }
1409  }
1410 
1411  // printing a substr/csubstr using ostreams
1412  {
1413  ryml::csubstr s = "some substring";
1414  ryml::csubstr some = s.first(4);
1415  CHECK(some == "some");
1416  CHECK(s == "some substring");
1417  // simple! just use plain operator<<
1418  {
1419  std::stringstream ss;
1420  ss << s;
1421  CHECK(ss.str() == "some substring"); // as expected
1422  CHECK(ss.str() == s); // as expected
1423  }
1424  // But NOTE: because this is a string view type, in general
1425  // the C-string is NOT zero terminated. So NEVER print it
1426  // directly, or it will overflow past the end of the given
1427  // substr, with a potential unbounded access. For example,
1428  // this is bad:
1429  {
1430  std::stringstream ss;
1431  ss << some.str; // ERROR! do not print the c-string directly
1432  CHECK(ss.str() == "some substring"); // NOT "some"
1433  CHECK(ss.str() == s); // NOT some
1434  }
1435  // this is also bad (the same)
1436  {
1437  std::stringstream ss;
1438  ss << some.data(); // ERROR! do not print the c-string directly
1439  CHECK(ss.str() == "some substring"); // NOT "some"
1440  CHECK(ss.str() == s); // NOT some
1441  }
1442  // this is ok:
1443  {
1444  std::stringstream ss;
1445  ss << some;
1446  CHECK(ss.str() == "some"); // ok
1447  CHECK(ss.str() == some); // ok
1448  }
1449  }
1450 
1451  // is_sub(),is_super()
1452  {
1453  ryml::csubstr foobar = "foobar";
1454  ryml::csubstr foo = foobar.first(3);
1455  CHECK(foo.is_sub(foobar));
1456  CHECK(foo.is_sub(foo));
1457  CHECK(!foo.is_super(foobar));
1458  CHECK(!foobar.is_sub(foo));
1459  // identity comparison is true:
1460  CHECK(foo.is_super(foo));
1461  CHECK(foo.is_sub(foo));
1462  CHECK(foobar.is_sub(foobar));
1463  CHECK(foobar.is_super(foobar));
1464  }
1465 
1466  // overlaps()
1467  {
1468  ryml::csubstr foobar = "foobar";
1469  ryml::csubstr foo = foobar.first(3);
1470  ryml::csubstr oba = foobar.offs(2, 1);
1471  ryml::csubstr abc = "abc";
1472  CHECK(foobar.overlaps(foo));
1473  CHECK(foobar.overlaps(oba));
1474  CHECK(foo.overlaps(foobar));
1475  CHECK(foo.overlaps(oba));
1476  CHECK(!foo.overlaps(abc));
1477  CHECK(!abc.overlaps(foo));
1478  }
1479 
1480  // triml(): trim characters from the left
1481  // trimr(): trim characters from the right
1482  // trim(): trim characters from left AND right
1483  {
1484  CHECK(ryml::csubstr(" \t\n\rcontents without whitespace\t \n\r").trim("\t \n\r") == "contents without whitespace");
1485  ryml::csubstr aaabbb = "aaabbb";
1486  ryml::csubstr aaa___bbb = "aaa___bbb";
1487  // trim a character:
1488  CHECK(aaabbb.triml('a') == aaabbb.last(3)); // bbb
1489  CHECK(aaabbb.trimr('a') == aaabbb);
1490  CHECK(aaabbb.trim ('a') == aaabbb.last(3)); // bbb
1491  CHECK(aaabbb.triml('b') == aaabbb);
1492  CHECK(aaabbb.trimr('b') == aaabbb.first(3)); // aaa
1493  CHECK(aaabbb.trim ('b') == aaabbb.first(3)); // aaa
1494  CHECK(aaabbb.triml('c') == aaabbb);
1495  CHECK(aaabbb.trimr('c') == aaabbb);
1496  CHECK(aaabbb.trim ('c') == aaabbb);
1497  CHECK(aaa___bbb.triml('a') == aaa___bbb.last(6)); // ___bbb
1498  CHECK(aaa___bbb.trimr('a') == aaa___bbb);
1499  CHECK(aaa___bbb.trim ('a') == aaa___bbb.last(6)); // ___bbb
1500  CHECK(aaa___bbb.triml('b') == aaa___bbb);
1501  CHECK(aaa___bbb.trimr('b') == aaa___bbb.first(6)); // aaa___
1502  CHECK(aaa___bbb.trim ('b') == aaa___bbb.first(6)); // aaa___
1503  CHECK(aaa___bbb.triml('c') == aaa___bbb);
1504  CHECK(aaa___bbb.trimr('c') == aaa___bbb);
1505  CHECK(aaa___bbb.trim ('c') == aaa___bbb);
1506  // trim ANY of the characters:
1507  CHECK(aaabbb.triml("ab") == "");
1508  CHECK(aaabbb.trimr("ab") == "");
1509  CHECK(aaabbb.trim ("ab") == "");
1510  CHECK(aaabbb.triml("ba") == "");
1511  CHECK(aaabbb.trimr("ba") == "");
1512  CHECK(aaabbb.trim ("ba") == "");
1513  CHECK(aaabbb.triml("cd") == aaabbb);
1514  CHECK(aaabbb.trimr("cd") == aaabbb);
1515  CHECK(aaabbb.trim ("cd") == aaabbb);
1516  CHECK(aaa___bbb.triml("ab") == aaa___bbb.last(6)); // ___bbb
1517  CHECK(aaa___bbb.triml("ba") == aaa___bbb.last(6)); // ___bbb
1518  CHECK(aaa___bbb.triml("cd") == aaa___bbb);
1519  CHECK(aaa___bbb.trimr("ab") == aaa___bbb.first(6)); // aaa___
1520  CHECK(aaa___bbb.trimr("ba") == aaa___bbb.first(6)); // aaa___
1521  CHECK(aaa___bbb.trimr("cd") == aaa___bbb);
1522  CHECK(aaa___bbb.trim ("ab") == aaa___bbb.range(3, 6)); // ___
1523  CHECK(aaa___bbb.trim ("ba") == aaa___bbb.range(3, 6)); // ___
1524  CHECK(aaa___bbb.trim ("cd") == aaa___bbb);
1525  }
1526 
1527  // unquoted():
1528  {
1529  CHECK(ryml::csubstr( "'this is is single quoted'" ).unquoted() == "this is is single quoted");
1530  CHECK(ryml::csubstr("\"this is is double quoted\"").unquoted() == "this is is double quoted");
1531  }
1532 
1533  // stripl(): remove pattern from the left
1534  // stripr(): remove pattern from the right
1535  {
1536  ryml::csubstr abc___cba = "abc___cba";
1537  ryml::csubstr abc___abc = "abc___abc";
1538  CHECK(abc___cba.stripl("abc") == abc___cba.last(6)); // ___cba
1539  CHECK(abc___cba.stripr("abc") == abc___cba);
1540  CHECK(abc___cba.stripl("ab") == abc___cba.last(7)); // c___cba
1541  CHECK(abc___cba.stripr("ab") == abc___cba);
1542  CHECK(abc___cba.stripl("a") == abc___cba.last(8)); // bc___cba, same as triml('a')
1543  CHECK(abc___cba.stripr("a") == abc___cba.first(8));
1544  CHECK(abc___abc.stripl("abc") == abc___abc.last(6)); // ___abc
1545  CHECK(abc___abc.stripr("abc") == abc___abc.first(6)); // abc___
1546  CHECK(abc___abc.stripl("ab") == abc___abc.last(7)); // c___cba
1547  CHECK(abc___abc.stripr("ab") == abc___abc);
1548  CHECK(abc___abc.stripl("a") == abc___abc.last(8)); // bc___cba, same as triml('a')
1549  CHECK(abc___abc.stripr("a") == abc___abc);
1550  }
1551 
1552  // begins_with()/ends_with()
1553  // begins_with_any()/ends_with_any()
1554  {
1555  ryml::csubstr s = "foobar123";
1556  // char overloads
1557  CHECK(s.begins_with('f'));
1558  CHECK(s.ends_with('3'));
1559  CHECK(!s.ends_with('2'));
1560  CHECK(!s.ends_with('o'));
1561  // char[] overloads
1562  CHECK(s.begins_with("foobar"));
1563  CHECK(s.begins_with("foo"));
1564  CHECK(s.begins_with_any("foo"));
1565  CHECK(!s.begins_with("oof"));
1566  CHECK(s.begins_with_any("oof"));
1567  CHECK(s.ends_with("23"));
1568  CHECK(s.ends_with("123"));
1569  CHECK(s.ends_with_any("123"));
1570  CHECK(!s.ends_with("321"));
1571  CHECK(s.ends_with_any("231"));
1572  }
1573 
1574  // select()
1575  {
1576  ryml::csubstr s = "0123456789";
1577  CHECK(s.select('0') == s.sub(0, 1));
1578  CHECK(s.select('1') == s.sub(1, 1));
1579  CHECK(s.select('2') == s.sub(2, 1));
1580  CHECK(s.select('8') == s.sub(8, 1));
1581  CHECK(s.select('9') == s.sub(9, 1));
1582  CHECK(s.select("0123") == s.range(0, 4));
1583  CHECK(s.select("012" ) == s.range(0, 3));
1584  CHECK(s.select("01" ) == s.range(0, 2));
1585  CHECK(s.select("0" ) == s.range(0, 1));
1586  CHECK(s.select( "123") == s.range(1, 4));
1587  CHECK(s.select( "23") == s.range(2, 4));
1588  CHECK(s.select( "3") == s.range(3, 4));
1589  }
1590 
1591  // find()
1592  {
1593  ryml::csubstr s012345 = "012345";
1594  // find single characters:
1595  CHECK(s012345.find('a') == ryml::npos);
1596  CHECK(s012345.find('0' ) == 0u);
1597  CHECK(s012345.find('0', 1u) == ryml::npos);
1598  CHECK(s012345.find('1' ) == 1u);
1599  CHECK(s012345.find('1', 2u) == ryml::npos);
1600  CHECK(s012345.find('2' ) == 2u);
1601  CHECK(s012345.find('2', 3u) == ryml::npos);
1602  CHECK(s012345.find('3' ) == 3u);
1603  CHECK(s012345.find('3', 4u) == ryml::npos);
1604  // find patterns
1605  CHECK(s012345.find("ab" ) == ryml::npos);
1606  CHECK(s012345.find("01" ) == 0u);
1607  CHECK(s012345.find("01", 1u) == ryml::npos);
1608  CHECK(s012345.find("12" ) == 1u);
1609  CHECK(s012345.find("12", 2u) == ryml::npos);
1610  CHECK(s012345.find("23" ) == 2u);
1611  CHECK(s012345.find("23", 3u) == ryml::npos);
1612  }
1613 
1614  // count(): count the number of occurrences of a character
1615  {
1616  ryml::csubstr buf = "00110022003300440055";
1617  CHECK(buf.count('1' ) == 2u);
1618  CHECK(buf.count('1', 0u) == 2u);
1619  CHECK(buf.count('1', 1u) == 2u);
1620  CHECK(buf.count('1', 2u) == 2u);
1621  CHECK(buf.count('1', 3u) == 1u);
1622  CHECK(buf.count('1', 4u) == 0u);
1623  CHECK(buf.count('1', 5u) == 0u);
1624  CHECK(buf.count('0' ) == 10u);
1625  CHECK(buf.count('0', 0u) == 10u);
1626  CHECK(buf.count('0', 1u) == 9u);
1627  CHECK(buf.count('0', 2u) == 8u);
1628  CHECK(buf.count('0', 3u) == 8u);
1629  CHECK(buf.count('0', 4u) == 8u);
1630  CHECK(buf.count('0', 5u) == 7u);
1631  CHECK(buf.count('0', 6u) == 6u);
1632  CHECK(buf.count('0', 7u) == 6u);
1633  CHECK(buf.count('0', 8u) == 6u);
1634  CHECK(buf.count('0', 9u) == 5u);
1635  CHECK(buf.count('0', 10u) == 4u);
1636  CHECK(buf.count('0', 11u) == 4u);
1637  CHECK(buf.count('0', 12u) == 4u);
1638  CHECK(buf.count('0', 13u) == 3u);
1639  CHECK(buf.count('0', 14u) == 2u);
1640  CHECK(buf.count('0', 15u) == 2u);
1641  CHECK(buf.count('0', 16u) == 2u);
1642  CHECK(buf.count('0', 17u) == 1u);
1643  CHECK(buf.count('0', 18u) == 0u);
1644  CHECK(buf.count('0', 19u) == 0u);
1645  CHECK(buf.count('0', 20u) == 0u);
1646  }
1647 
1648  // first_of(),last_of()
1649  {
1650  ryml::csubstr s012345 = "012345";
1651  CHECK(s012345.first_of('a') == ryml::npos);
1652  CHECK(s012345.first_of("ab") == ryml::npos);
1653  CHECK(s012345.first_of('0') == 0u);
1654  CHECK(s012345.first_of("0") == 0u);
1655  CHECK(s012345.first_of("01") == 0u);
1656  CHECK(s012345.first_of("10") == 0u);
1657  CHECK(s012345.first_of("012") == 0u);
1658  CHECK(s012345.first_of("210") == 0u);
1659  CHECK(s012345.first_of("0123") == 0u);
1660  CHECK(s012345.first_of("3210") == 0u);
1661  CHECK(s012345.first_of("01234") == 0u);
1662  CHECK(s012345.first_of("43210") == 0u);
1663  CHECK(s012345.first_of("012345") == 0u);
1664  CHECK(s012345.first_of("543210") == 0u);
1665  CHECK(s012345.first_of('5') == 5u);
1666  CHECK(s012345.first_of("5") == 5u);
1667  CHECK(s012345.first_of("45") == 4u);
1668  CHECK(s012345.first_of("54") == 4u);
1669  CHECK(s012345.first_of("345") == 3u);
1670  CHECK(s012345.first_of("543") == 3u);
1671  CHECK(s012345.first_of("2345") == 2u);
1672  CHECK(s012345.first_of("5432") == 2u);
1673  CHECK(s012345.first_of("12345") == 1u);
1674  CHECK(s012345.first_of("54321") == 1u);
1675  CHECK(s012345.first_of("012345") == 0u);
1676  CHECK(s012345.first_of("543210") == 0u);
1677  CHECK(s012345.first_of('0', 6u) == ryml::npos);
1678  CHECK(s012345.first_of('5', 6u) == ryml::npos);
1679  CHECK(s012345.first_of("012345", 6u) == ryml::npos);
1680  //
1681  CHECK(s012345.last_of('a') == ryml::npos);
1682  CHECK(s012345.last_of("ab") == ryml::npos);
1683  CHECK(s012345.last_of('0') == 0u);
1684  CHECK(s012345.last_of("0") == 0u);
1685  CHECK(s012345.last_of("01") == 1u);
1686  CHECK(s012345.last_of("10") == 1u);
1687  CHECK(s012345.last_of("012") == 2u);
1688  CHECK(s012345.last_of("210") == 2u);
1689  CHECK(s012345.last_of("0123") == 3u);
1690  CHECK(s012345.last_of("3210") == 3u);
1691  CHECK(s012345.last_of("01234") == 4u);
1692  CHECK(s012345.last_of("43210") == 4u);
1693  CHECK(s012345.last_of("012345") == 5u);
1694  CHECK(s012345.last_of("543210") == 5u);
1695  CHECK(s012345.last_of('5') == 5u);
1696  CHECK(s012345.last_of("5") == 5u);
1697  CHECK(s012345.last_of("45") == 5u);
1698  CHECK(s012345.last_of("54") == 5u);
1699  CHECK(s012345.last_of("345") == 5u);
1700  CHECK(s012345.last_of("543") == 5u);
1701  CHECK(s012345.last_of("2345") == 5u);
1702  CHECK(s012345.last_of("5432") == 5u);
1703  CHECK(s012345.last_of("12345") == 5u);
1704  CHECK(s012345.last_of("54321") == 5u);
1705  CHECK(s012345.last_of("012345") == 5u);
1706  CHECK(s012345.last_of("543210") == 5u);
1707  CHECK(s012345.last_of('0', 6u) == 0u);
1708  CHECK(s012345.last_of('5', 6u) == 5u);
1709  CHECK(s012345.last_of("012345", 6u) == 5u);
1710  }
1711 
1712  // first_not_of(), last_not_of()
1713  {
1714  ryml::csubstr s012345 = "012345";
1715  CHECK(s012345.first_not_of('a') == 0u);
1716  CHECK(s012345.first_not_of("ab") == 0u);
1717  CHECK(s012345.first_not_of('0') == 1u);
1718  CHECK(s012345.first_not_of("0") == 1u);
1719  CHECK(s012345.first_not_of("01") == 2u);
1720  CHECK(s012345.first_not_of("10") == 2u);
1721  CHECK(s012345.first_not_of("012") == 3u);
1722  CHECK(s012345.first_not_of("210") == 3u);
1723  CHECK(s012345.first_not_of("0123") == 4u);
1724  CHECK(s012345.first_not_of("3210") == 4u);
1725  CHECK(s012345.first_not_of("01234") == 5u);
1726  CHECK(s012345.first_not_of("43210") == 5u);
1727  CHECK(s012345.first_not_of("012345") == ryml::npos);
1728  CHECK(s012345.first_not_of("543210") == ryml::npos);
1729  CHECK(s012345.first_not_of('5') == 0u);
1730  CHECK(s012345.first_not_of("5") == 0u);
1731  CHECK(s012345.first_not_of("45") == 0u);
1732  CHECK(s012345.first_not_of("54") == 0u);
1733  CHECK(s012345.first_not_of("345") == 0u);
1734  CHECK(s012345.first_not_of("543") == 0u);
1735  CHECK(s012345.first_not_of("2345") == 0u);
1736  CHECK(s012345.first_not_of("5432") == 0u);
1737  CHECK(s012345.first_not_of("12345") == 0u);
1738  CHECK(s012345.first_not_of("54321") == 0u);
1739  CHECK(s012345.first_not_of("012345") == ryml::npos);
1740  CHECK(s012345.first_not_of("543210") == ryml::npos);
1741  CHECK(s012345.last_not_of('a') == 5u);
1742  CHECK(s012345.last_not_of("ab") == 5u);
1743  CHECK(s012345.last_not_of('5') == 4u);
1744  CHECK(s012345.last_not_of("5") == 4u);
1745  CHECK(s012345.last_not_of("45") == 3u);
1746  CHECK(s012345.last_not_of("54") == 3u);
1747  CHECK(s012345.last_not_of("345") == 2u);
1748  CHECK(s012345.last_not_of("543") == 2u);
1749  CHECK(s012345.last_not_of("2345") == 1u);
1750  CHECK(s012345.last_not_of("5432") == 1u);
1751  CHECK(s012345.last_not_of("12345") == 0u);
1752  CHECK(s012345.last_not_of("54321") == 0u);
1753  CHECK(s012345.last_not_of("012345") == ryml::npos);
1754  CHECK(s012345.last_not_of("543210") == ryml::npos);
1755  CHECK(s012345.last_not_of('0') == 5u);
1756  CHECK(s012345.last_not_of("0") == 5u);
1757  CHECK(s012345.last_not_of("01") == 5u);
1758  CHECK(s012345.last_not_of("10") == 5u);
1759  CHECK(s012345.last_not_of("012") == 5u);
1760  CHECK(s012345.last_not_of("210") == 5u);
1761  CHECK(s012345.last_not_of("0123") == 5u);
1762  CHECK(s012345.last_not_of("3210") == 5u);
1763  CHECK(s012345.last_not_of("01234") == 5u);
1764  CHECK(s012345.last_not_of("43210") == 5u);
1765  CHECK(s012345.last_not_of("012345") == ryml::npos);
1766  CHECK(s012345.last_not_of("543210") == ryml::npos);
1767  }
1768 
1769  // first_non_empty_span()
1770  {
1771  CHECK(ryml::csubstr("foo bar").first_non_empty_span() == "foo");
1772  CHECK(ryml::csubstr(" foo bar").first_non_empty_span() == "foo");
1773  CHECK(ryml::csubstr("\n \r \t foo bar").first_non_empty_span() == "foo");
1774  CHECK(ryml::csubstr("\n \r \t foo\n\r\t bar").first_non_empty_span() == "foo");
1775  CHECK(ryml::csubstr("\n \r \t foo\n\r\t bar").first_non_empty_span() == "foo");
1776  CHECK(ryml::csubstr(",\n \r \t foo\n\r\t bar").first_non_empty_span() == ",");
1777  }
1778  // first_uint_span()
1779  {
1780  CHECK(ryml::csubstr("1234 asdkjh").first_uint_span() == "1234");
1781  CHECK(ryml::csubstr("1234\rasdkjh").first_uint_span() == "1234");
1782  CHECK(ryml::csubstr("1234\tasdkjh").first_uint_span() == "1234");
1783  CHECK(ryml::csubstr("1234\nasdkjh").first_uint_span() == "1234");
1784  CHECK(ryml::csubstr("1234]asdkjh").first_uint_span() == "1234");
1785  CHECK(ryml::csubstr("1234)asdkjh").first_uint_span() == "1234");
1786  CHECK(ryml::csubstr("1234gasdkjh").first_uint_span() == "");
1787  }
1788  // first_int_span()
1789  {
1790  CHECK(ryml::csubstr("-1234 asdkjh").first_int_span() == "-1234");
1791  CHECK(ryml::csubstr("-1234\rasdkjh").first_int_span() == "-1234");
1792  CHECK(ryml::csubstr("-1234\tasdkjh").first_int_span() == "-1234");
1793  CHECK(ryml::csubstr("-1234\nasdkjh").first_int_span() == "-1234");
1794  CHECK(ryml::csubstr("-1234]asdkjh").first_int_span() == "-1234");
1795  CHECK(ryml::csubstr("-1234)asdkjh").first_int_span() == "-1234");
1796  CHECK(ryml::csubstr("-1234gasdkjh").first_int_span() == "");
1797  }
1798  // first_real_span()
1799  {
1800  CHECK(ryml::csubstr("-1234 asdkjh").first_real_span() == "-1234");
1801  CHECK(ryml::csubstr("-1234\rasdkjh").first_real_span() == "-1234");
1802  CHECK(ryml::csubstr("-1234\tasdkjh").first_real_span() == "-1234");
1803  CHECK(ryml::csubstr("-1234\nasdkjh").first_real_span() == "-1234");
1804  CHECK(ryml::csubstr("-1234]asdkjh").first_real_span() == "-1234");
1805  CHECK(ryml::csubstr("-1234)asdkjh").first_real_span() == "-1234");
1806  CHECK(ryml::csubstr("-1234gasdkjh").first_real_span() == "");
1807  CHECK(ryml::csubstr("1.234 asdkjh").first_real_span() == "1.234");
1808  CHECK(ryml::csubstr("1.234e+5 asdkjh").first_real_span() == "1.234e+5");
1809  CHECK(ryml::csubstr("1.234e-5 asdkjh").first_real_span() == "1.234e-5");
1810  CHECK(ryml::csubstr("1.234 asdkjh").first_real_span() == "1.234");
1811  CHECK(ryml::csubstr("1.234e+5 asdkjh").first_real_span() == "1.234e+5");
1812  CHECK(ryml::csubstr("1.234e-5 asdkjh").first_real_span() == "1.234e-5");
1813  CHECK(ryml::csubstr("-1.234 asdkjh").first_real_span() == "-1.234");
1814  CHECK(ryml::csubstr("-1.234e+5 asdkjh").first_real_span() == "-1.234e+5");
1815  CHECK(ryml::csubstr("-1.234e-5 asdkjh").first_real_span() == "-1.234e-5");
1816  // hexadecimal real numbers
1817  CHECK(ryml::csubstr("0x1.e8480p+19 asdkjh").first_real_span() == "0x1.e8480p+19");
1818  CHECK(ryml::csubstr("0x1.e8480p-19 asdkjh").first_real_span() == "0x1.e8480p-19");
1819  CHECK(ryml::csubstr("-0x1.e8480p+19 asdkjh").first_real_span() == "-0x1.e8480p+19");
1820  CHECK(ryml::csubstr("-0x1.e8480p-19 asdkjh").first_real_span() == "-0x1.e8480p-19");
1821  CHECK(ryml::csubstr("+0x1.e8480p+19 asdkjh").first_real_span() == "+0x1.e8480p+19");
1822  CHECK(ryml::csubstr("+0x1.e8480p-19 asdkjh").first_real_span() == "+0x1.e8480p-19");
1823  // binary real numbers
1824  CHECK(ryml::csubstr("0b101.011p+19 asdkjh").first_real_span() == "0b101.011p+19");
1825  CHECK(ryml::csubstr("0b101.011p-19 asdkjh").first_real_span() == "0b101.011p-19");
1826  CHECK(ryml::csubstr("-0b101.011p+19 asdkjh").first_real_span() == "-0b101.011p+19");
1827  CHECK(ryml::csubstr("-0b101.011p-19 asdkjh").first_real_span() == "-0b101.011p-19");
1828  CHECK(ryml::csubstr("+0b101.011p+19 asdkjh").first_real_span() == "+0b101.011p+19");
1829  CHECK(ryml::csubstr("+0b101.011p-19 asdkjh").first_real_span() == "+0b101.011p-19");
1830  // octal real numbers
1831  CHECK(ryml::csubstr("0o173.045p+19 asdkjh").first_real_span() == "0o173.045p+19");
1832  CHECK(ryml::csubstr("0o173.045p-19 asdkjh").first_real_span() == "0o173.045p-19");
1833  CHECK(ryml::csubstr("-0o173.045p+19 asdkjh").first_real_span() == "-0o173.045p+19");
1834  CHECK(ryml::csubstr("-0o173.045p-19 asdkjh").first_real_span() == "-0o173.045p-19");
1835  CHECK(ryml::csubstr("+0o173.045p+19 asdkjh").first_real_span() == "+0o173.045p+19");
1836  CHECK(ryml::csubstr("+0o173.045p-19 asdkjh").first_real_span() == "+0o173.045p-19");
1837  }
1838  // see also is_number()
1839 
1840  // basename(), dirname(), extshort(), extlong()
1841  {
1842  CHECK(ryml::csubstr("/path/to/file.tar.gz").basename() == "file.tar.gz");
1843  CHECK(ryml::csubstr("/path/to/file.tar.gz").dirname() == "/path/to/");
1844  CHECK(ryml::csubstr("C:\\path\\to\\file.tar.gz").basename('\\') == "file.tar.gz");
1845  CHECK(ryml::csubstr("C:\\path\\to\\file.tar.gz").dirname('\\') == "C:\\path\\to\\");
1846  CHECK(ryml::csubstr("/path/to/file.tar.gz").extshort() == "gz");
1847  CHECK(ryml::csubstr("/path/to/file.tar.gz").extlong() == "tar.gz");
1848  CHECK(ryml::csubstr("/path/to/file.tar.gz").name_wo_extshort() == "/path/to/file.tar");
1849  CHECK(ryml::csubstr("/path/to/file.tar.gz").name_wo_extlong() == "/path/to/file");
1850  }
1851 
1852  // split()
1853  {
1854  using namespace ryml;
1855  csubstr parts[] = {"aa", "bb", "cc", "dd", "ee", "ff"};
1856  {
1857  size_t count = 0;
1858  for(csubstr part : csubstr("aa/bb/cc/dd/ee/ff").split('/'))
1859  CHECK(part == parts[count++]);
1860  CHECK(count == 6u);
1861  }
1862  {
1863  size_t count = 0;
1864  for(csubstr part : csubstr("aa.bb.cc.dd.ee.ff").split('.'))
1865  CHECK(part == parts[count++]);
1866  CHECK(count == 6u);
1867  }
1868  {
1869  size_t count = 0;
1870  for(csubstr part : csubstr("aa-bb-cc-dd-ee-ff").split('-'))
1871  CHECK(part == parts[count++]);
1872  CHECK(count == 6u);
1873  }
1874  // see also next_split()
1875  }
1876 
1877  // pop_left(), pop_right() --- non-greedy version
1878  // gpop_left(), gpop_right() --- greedy version
1879  {
1880  const bool skip_empty = true;
1881  // pop_left(): pop the last element from the left
1882  CHECK(ryml::csubstr( "0/1/2" ). pop_left('/' ) == "0" );
1883  CHECK(ryml::csubstr( "/0/1/2" ). pop_left('/' ) == "" );
1884  CHECK(ryml::csubstr("//0/1/2" ). pop_left('/' ) == "" );
1885  CHECK(ryml::csubstr( "0/1/2" ). pop_left('/', skip_empty) == "0" );
1886  CHECK(ryml::csubstr( "/0/1/2" ). pop_left('/', skip_empty) == "/0" );
1887  CHECK(ryml::csubstr("//0/1/2" ). pop_left('/', skip_empty) == "//0" );
1888  // gpop_left(): pop all but the first element (greedy pop)
1889  CHECK(ryml::csubstr( "0/1/2" ).gpop_left('/' ) == "0/1" );
1890  CHECK(ryml::csubstr( "/0/1/2" ).gpop_left('/' ) == "/0/1" );
1891  CHECK(ryml::csubstr("//0/1/2" ).gpop_left('/' ) == "//0/1" );
1892  CHECK(ryml::csubstr( "0/1/2/" ).gpop_left('/' ) == "0/1/2");
1893  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_left('/' ) == "/0/1/2");
1894  CHECK(ryml::csubstr("//0/1/2/" ).gpop_left('/' ) == "//0/1/2");
1895  CHECK(ryml::csubstr( "0/1/2//" ).gpop_left('/' ) == "0/1/2/");
1896  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_left('/' ) == "/0/1/2/");
1897  CHECK(ryml::csubstr("//0/1/2//" ).gpop_left('/' ) == "//0/1/2/");
1898  CHECK(ryml::csubstr( "0/1/2" ).gpop_left('/', skip_empty) == "0/1" );
1899  CHECK(ryml::csubstr( "/0/1/2" ).gpop_left('/', skip_empty) == "/0/1" );
1900  CHECK(ryml::csubstr("//0/1/2" ).gpop_left('/', skip_empty) == "//0/1" );
1901  CHECK(ryml::csubstr( "0/1/2/" ).gpop_left('/', skip_empty) == "0/1" );
1902  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_left('/', skip_empty) == "/0/1" );
1903  CHECK(ryml::csubstr("//0/1/2/" ).gpop_left('/', skip_empty) == "//0/1" );
1904  CHECK(ryml::csubstr( "0/1/2//" ).gpop_left('/', skip_empty) == "0/1" );
1905  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_left('/', skip_empty) == "/0/1" );
1906  CHECK(ryml::csubstr("//0/1/2//" ).gpop_left('/', skip_empty) == "//0/1" );
1907  // pop_right(): pop the last element from the right
1908  CHECK(ryml::csubstr( "0/1/2" ). pop_right('/' ) == "2" );
1909  CHECK(ryml::csubstr( "0/1/2/" ). pop_right('/' ) == "" );
1910  CHECK(ryml::csubstr( "0/1/2//" ). pop_right('/' ) == "" );
1911  CHECK(ryml::csubstr( "0/1/2" ). pop_right('/', skip_empty) == "2" );
1912  CHECK(ryml::csubstr( "0/1/2/" ). pop_right('/', skip_empty) == "2/" );
1913  CHECK(ryml::csubstr( "0/1/2//" ). pop_right('/', skip_empty) == "2//" );
1914  // gpop_right(): pop all but the first element (greedy pop)
1915  CHECK(ryml::csubstr( "0/1/2" ).gpop_right('/' ) == "1/2" );
1916  CHECK(ryml::csubstr( "0/1/2/" ).gpop_right('/' ) == "1/2/" );
1917  CHECK(ryml::csubstr( "0/1/2//" ).gpop_right('/' ) == "1/2//");
1918  CHECK(ryml::csubstr( "/0/1/2" ).gpop_right('/' ) == "0/1/2" );
1919  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_right('/' ) == "0/1/2/" );
1920  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_right('/' ) == "0/1/2//");
1921  CHECK(ryml::csubstr("//0/1/2" ).gpop_right('/' ) == "/0/1/2" );
1922  CHECK(ryml::csubstr("//0/1/2/" ).gpop_right('/' ) == "/0/1/2/" );
1923  CHECK(ryml::csubstr("//0/1/2//" ).gpop_right('/' ) == "/0/1/2//");
1924  CHECK(ryml::csubstr( "0/1/2" ).gpop_right('/', skip_empty) == "1/2" );
1925  CHECK(ryml::csubstr( "0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1926  CHECK(ryml::csubstr( "0/1/2//" ).gpop_right('/', skip_empty) == "1/2//");
1927  CHECK(ryml::csubstr( "/0/1/2" ).gpop_right('/', skip_empty) == "1/2" );
1928  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1929  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_right('/', skip_empty) == "1/2//");
1930  CHECK(ryml::csubstr("//0/1/2" ).gpop_right('/', skip_empty) == "1/2" );
1931  CHECK(ryml::csubstr("//0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1932  CHECK(ryml::csubstr("//0/1/2//" ).gpop_right('/', skip_empty) == "1/2//");
1933  }
1934 }
1935 
1936 
1937 //-----------------------------------------------------------------------------
1938 
1939 
1940 /** demonstrate how to load a YAML file from disk to parse with ryml.
1941  *
1942  * ryml offers no overload to directly parse files from disk; it only
1943  * parses source buffers (which may be mutable or immutable). It is
1944  * up to the caller to first load the file contents into a buffer
1945  * before parsing with ryml. To help with this you can use the
1946  * (efficient) helper [file_get_contents](@ref
1947  * c4::yml::file_get_contents()). See also the analogous
1948  * [file_put_contents](@ref c4::yml::file_put_contents())
1949  *
1950  * @see doc_parse */
1952 {
1953  const char filename[] = "ryml_example.yml";
1954  std::string yaml = ""
1955  "foo: 1" "\n"
1956  "bar:" "\n"
1957  "- 2" "\n"
1958  "- 3" "\n";
1959  // because this is a minimal sample, it assumes nothing on the
1960  // environment/OS (other than that it can read/write files). So we
1961  // create the file on the fly:
1962  ryml::file_put_contents(yaml, filename);
1963 
1964  // now we can load it into a std::string (for example):
1965  {
1966  std::string contents = ryml::file_get_contents<std::string>(filename);
1967  ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(contents)); // immutable (csubstr) overload
1968  CHECK(tree["foo"].val() == "1");
1969  CHECK(tree["bar"][0].val() == "2");
1970  CHECK(tree["bar"][1].val() == "3");
1971  }
1972 
1973  // or we can use a vector<char> instead:
1974  {
1975  std::vector<char> contents = ryml::file_get_contents<std::vector<char>>(filename);
1976  ryml::Tree tree = ryml::parse_in_place(ryml::to_substr(contents)); // mutable (substr) overload
1977  CHECK(tree["foo"].val() == "1");
1978  CHECK(tree["bar"][0].val() == "2");
1979  CHECK(tree["bar"][1].val() == "3");
1980  }
1981 
1982  // generally, any contiguous char container can be used with ryml,
1983  // provided that the ryml::substr/ryml::csubstr view can be
1984  // created out of it.
1985  //
1986  // ryml provides the overloads above for these two containers, but
1987  // if you are using another container it should be very easy (only
1988  // requires pointer and length).
1989 }
1990 
1991 
1992 //-----------------------------------------------------------------------------
1993 
1994 /** demonstrate in-place parsing of a mutable YAML source buffer.
1995  * @see doc_parse */
1997 {
1998  // Like the name suggests, parse_in_place() directly mutates the
1999  // source buffer in place
2000  char src[] = "{foo: 1, bar: [2, 3]}"; // ryml can parse in situ
2001  ryml::substr srcview = src; // a mutable view to the source buffer
2002  ryml::Tree tree = ryml::parse_in_place(srcview); // you can also reuse the tree and/or parser
2003  ryml::ConstNodeRef root = tree.crootref(); // get a constant reference to the root
2004 
2005  CHECK(root.is_map());
2006  CHECK(root["foo"].is_keyval());
2007  CHECK(root["foo"].key() == "foo");
2008  CHECK(root["foo"].val() == "1");
2009  CHECK(root["bar"].is_seq());
2010  CHECK(root["bar"].has_key());
2011  CHECK(root["bar"].key() == "bar");
2012  CHECK(root["bar"][0].val() == "2");
2013  CHECK(root["bar"][1].val() == "3");
2014 
2015  // deserializing:
2016  int foo = 0, bar0 = 0, bar1 = 0;
2017  root["foo"].load(&foo);
2018  root["bar"][0].load(&bar0);
2019  root["bar"][1].load(&bar1);
2020  CHECK(foo == 1);
2021  CHECK(bar0 == 2);
2022  CHECK(bar1 == 3);
2023 
2024  // after parsing, the tree holds views to the source buffer:
2025  CHECK(root["foo"].val().data() == src + strlen("{foo: "));
2026  CHECK(root["foo"].val().begin() == src + strlen("{foo: "));
2027  CHECK(root["foo"].val().end() == src + strlen("{foo: 1"));
2028  CHECK(root["foo"].val().is_sub(srcview)); // equivalent to the previous three assertions
2029  CHECK(root["bar"][0].val().data() == src + strlen("{foo: 1, bar: ["));
2030  CHECK(root["bar"][0].val().begin() == src + strlen("{foo: 1, bar: ["));
2031  CHECK(root["bar"][0].val().end() == src + strlen("{foo: 1, bar: [2"));
2032  CHECK(root["bar"][0].val().is_sub(srcview)); // equivalent to the previous three assertions
2033  CHECK(root["bar"][1].val().data() == src + strlen("{foo: 1, bar: [2, "));
2034  CHECK(root["bar"][1].val().begin() == src + strlen("{foo: 1, bar: [2, "));
2035  CHECK(root["bar"][1].val().end() == src + strlen("{foo: 1, bar: [2, 3"));
2036  CHECK(root["bar"][1].val().is_sub(srcview)); // equivalent to the previous three assertions
2037 
2038  // NOTE. parse_in_place() cannot accept ryml::csubstr
2039  // so this will cause a /compile/ error:
2040  ryml::csubstr csrcview = srcview; // ok, can assign from mutable to immutable
2041  //tree = ryml::parse_in_place(csrcview); // compile error, cannot mutate an immutable view
2042  (void)csrcview;
2043 }
2044 
2045 
2046 //-----------------------------------------------------------------------------
2047 
2048 /** demonstrate parsing of a read-only YAML source buffer
2049  * @see doc_parse */
2051 {
2052  // to parse read-only memory, ryml will copy first to the tree's
2053  // arena, and then parse the copied buffer:
2054  ryml::Tree tree = ryml::parse_in_arena("{foo: 1, bar: [2, 3]}");
2055  ryml::ConstNodeRef root = tree.crootref(); // get a const reference to the root
2056 
2057  CHECK(root.is_map());
2058  CHECK(root["foo"].is_keyval());
2059  CHECK(root["foo"].key() == "foo");
2060  CHECK(root["foo"].val() == "1");
2061  CHECK(root["bar"].is_seq());
2062  CHECK(root["bar"].has_key());
2063  CHECK(root["bar"].key() == "bar");
2064  CHECK(root["bar"][0].val() == "2");
2065  CHECK(root["bar"][1].val() == "3");
2066 
2067  // deserializing:
2068  int foo = 0, bar0 = 0, bar1 = 0;
2069  root["foo"].load(&foo);
2070  root["bar"][0].load(&bar0);
2071  root["bar"][1].load(&bar1);
2072  CHECK(foo == 1);
2073  CHECK(bar0 == 2);
2074  CHECK(bar1 == 3);
2075 
2076  // NOTE. parse_in_arena() cannot accept ryml::substr. Overloads
2077  // receiving substr buffers are declared, but intentionally left
2078  // undefined, so this will cause a /linker/ error
2079  char src[] = "{foo: is it really true}";
2080  ryml::substr srcview = src;
2081  //tree = ryml::parse_in_place(srcview); // linker error, overload intentionally undefined
2082 
2083  // If you really intend to parse a mutable buffer in the arena,
2084  // then simply convert it to immutable prior to calling
2085  // parse_in_arena():
2086  ryml::csubstr csrcview = srcview; // assigning from src also works
2087  tree = ryml::parse_in_arena(csrcview); // OK! csrcview is immutable
2088  CHECK(tree["foo"].val() == "is it really true");
2089 }
2090 
2091 
2092 //-----------------------------------------------------------------------------
2093 
2094 /** demonstrate reuse/modification of tree when parsing
2095  * @see doc_parse */
2097 {
2098  ryml::Tree tree;
2099 
2100  // it will always be faster if the tree's size is conveniently reserved:
2101  tree.reserve(30); // reserve 30 nodes (good enough for this sample)
2102  // if you are using the tree's arena to serialize data,
2103  // then reserve also the arena's size:
2104  tree.reserve_arena(256); // reserve 256 characters (good enough for this sample)
2105 
2106  // now parse into the tree:
2107  ryml::csubstr yaml = ""
2108  "foo: 1" "\n"
2109  "bar: [2,3]" "\n"
2110  "";
2111  ryml::parse_in_arena(yaml, &tree);
2112 
2113  ryml::ConstNodeRef root = tree.crootref();
2114  CHECK(root.num_children() == 2);
2115  CHECK(root.is_map());
2116  CHECK(root["foo"].is_keyval());
2117  CHECK(root["foo"].key() == "foo");
2118  CHECK(root["foo"].val() == "1");
2119  CHECK(root["bar"].is_seq());
2120  CHECK(root["bar"].has_key());
2121  CHECK(root["bar"].key() == "bar");
2122  CHECK(root["bar"][0].val() == "2");
2123  CHECK(root["bar"][1].val() == "3");
2124  CHECK(ryml::emitrs_yaml<std::string>(tree) == yaml);
2125 
2126  // WATCHOUT: parsing into an existing tree will APPEND to it:
2127  ryml::parse_in_arena("{foo2: 12, bar2: [22, 32]}", &tree);
2128  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2129  "foo: 1" "\n"
2130  "bar: [2,3]" "\n"
2131  "foo2: 12" "\n"
2132  "bar2: [22,32]" "\n"
2133  "");
2134  CHECK(root.num_children() == 4);
2135  CHECK(root["foo2"].is_keyval());
2136  CHECK(root["foo2"].key() == "foo2");
2137  CHECK(root["foo2"].val() == "12");
2138  CHECK(root["bar2"].is_seq());
2139  CHECK(root["bar2"].has_key());
2140  CHECK(root["bar2"].key() == "bar2");
2141  CHECK(root["bar2"][0].val() == "22");
2142  CHECK(root["bar2"][1].val() == "32");
2143 
2144  // if you want to fully replace the tree, you need to clear first
2145  // before parsing into an existing tree:
2146  tree.clear();
2147  tree.clear_arena(); // you may or may not want to clear the arena
2148  ryml::parse_in_arena("- a\n- b\n- {x0: 1, x1: 2}", &tree);
2149  CHECK(ryml::emitrs_yaml<std::string>(tree) == "- a\n- b\n- {x0: 1,x1: 2}\n");
2150  CHECK(root.is_seq());
2151  CHECK(root[0].val() == "a");
2152  CHECK(root[1].val() == "b");
2153  CHECK(root[2].is_map());
2154  CHECK(root[2]["x0"].val() == "1");
2155  CHECK(root[2]["x1"].val() == "2");
2156 
2157  // we can parse directly into a node nested deep in an existing tree:
2158  ryml::NodeRef mroot = tree.rootref(); // modifiable root
2159  ryml::parse_in_arena("champagne: Dom Perignon\ncoffee: Arabica", mroot.append_child());
2160  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2161  "- a" "\n"
2162  "- b" "\n"
2163  "- {x0: 1,x1: 2}" "\n"
2164  "- champagne: Dom Perignon" "\n"
2165  " coffee: Arabica" "\n"
2166  "");
2167  CHECK(root.is_seq());
2168  CHECK(root[0].val() == "a");
2169  CHECK(root[1].val() == "b");
2170  CHECK(root[2].is_map());
2171  CHECK(root[2]["x0"].val() == "1");
2172  CHECK(root[2]["x1"].val() == "2");
2173  CHECK(root[3].is_map());
2174  CHECK(root[3]["champagne"].val() == "Dom Perignon");
2175  CHECK(root[3]["coffee"].val() == "Arabica");
2176 
2177  mroot[3]["more"].set_map();
2178  mroot[3]["beer"].set_seq();
2179  CHECK(mroot[3]["more"].readable());
2180  CHECK(mroot[3]["more"].key() == "more");
2181  CHECK(mroot[3]["more"].is_map());
2182  CHECK(!mroot[3]["more"].is_val());
2183  ryml::parse_in_arena("{vinho verde: Soalheiro, vinho tinto: Redoma 2017}", mroot[3]["more"]);
2184  ryml::parse_in_arena("- Rochefort 10\n- Busch\n- Leffe Rituel", mroot[3]["beer"]);
2185  ryml::parse_in_arena("lots\nof\nwater", mroot[3]["always"]);
2186  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2187  "- a" "\n"
2188  "- b" "\n"
2189  "- {x0: 1,x1: 2}" "\n"
2190  "- champagne: Dom Perignon" "\n"
2191  " coffee: Arabica" "\n"
2192  // note these were added:
2193  " more:" "\n"
2194  " vinho verde: Soalheiro" "\n"
2195  " vinho tinto: Redoma 2017" "\n"
2196  " beer:" "\n"
2197  " - Rochefort 10" "\n"
2198  " - Busch" "\n"
2199  " - Leffe Rituel" "\n"
2200  " always: lots of water" "\n"
2201  "");
2202 
2203  // can append at the top:
2204  ryml::parse_in_arena("- foo\n- bar\n- baz\n- bat", mroot);
2205  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2206  "- a" "\n"
2207  "- b" "\n"
2208  "- {x0: 1,x1: 2}" "\n"
2209  "- champagne: Dom Perignon" "\n"
2210  " coffee: Arabica" "\n"
2211  " more:" "\n"
2212  " vinho verde: Soalheiro" "\n"
2213  " vinho tinto: Redoma 2017" "\n"
2214  " beer:" "\n"
2215  " - Rochefort 10" "\n"
2216  " - Busch" "\n"
2217  " - Leffe Rituel" "\n"
2218  " always: lots of water" "\n"
2219  // note these were added
2220  "- foo" "\n"
2221  "- bar" "\n"
2222  "- baz" "\n"
2223  "- bat" "\n"
2224  "");
2225 
2226  // or nested:
2227  ryml::parse_in_arena("[Kasteel Donker]", mroot[3]["beer"]);
2228  CHECK(ryml::emitrs_yaml<std::string>(tree) ==
2229  "- a" "\n"
2230  "- b" "\n"
2231  "- {x0: 1,x1: 2}" "\n"
2232  "- champagne: Dom Perignon" "\n"
2233  " coffee: Arabica" "\n"
2234  " more:" "\n"
2235  " vinho verde: Soalheiro" "\n"
2236  " vinho tinto: Redoma 2017" "\n"
2237  " beer:" "\n"
2238  " - Rochefort 10" "\n"
2239  " - Busch" "\n"
2240  " - Leffe Rituel" "\n"
2241  " - Kasteel Donker" "\n" // added this
2242  " always: lots of water" "\n"
2243  "- foo" "\n"
2244  "- bar" "\n"
2245  "- baz" "\n"
2246  "- bat" "\n"
2247  "");
2248 }
2249 
2250 
2251 //-----------------------------------------------------------------------------
2252 
2253 /** Demonstrates reuse of an existing parser. Doing this is
2254  * recommended when multiple files are parsed.
2255  * @see doc_parse */
2257 {
2258  ryml::EventHandlerTree evt_handler = {};
2259  ryml::Parser parser(&evt_handler);
2260 
2261  // it is also advised to reserve the parser depth
2262  // to the expected depth of the data tree:
2263  parser.reserve_stack(10); // uses small storage optimization
2264  // defaulting to 16 depth, so this
2265  // instruction is a no-op, and the stack
2266  // will located in the parser object.
2267  parser.reserve_stack(20); // But this will cause an allocation
2268  // because it is above 16.
2269 
2270  ryml::csubstr yaml = "[Dom Perignon,Gosset Grande Reserve,Jacquesson 742]";
2271  ryml::Tree champagnes = parse_in_arena(&parser, "champagnes.yml", yaml);
2272  CHECK(ryml::emitrs_yaml<std::string>(champagnes) == yaml);
2273 
2274  yaml = "[Rochefort 10,Busch,Leffe Rituel,Kasteel Donker]";
2275  ryml::Tree beers = parse_in_arena(&parser, "beers.yml", yaml);
2276  CHECK(ryml::emitrs_yaml<std::string>(beers) == yaml);
2277 }
2278 
2279 
2280 //-----------------------------------------------------------------------------
2281 
2282 /** for ultimate speed when parsing multiple times, reuse both the
2283  * tree and parser
2284  * @see doc_parse */
2286 {
2287  ryml::Tree tree;
2288  // it will always be faster if the tree's size is conveniently reserved:
2289  tree.reserve(30); // reserve 30 nodes (good enough for this sample)
2290  // if you are using the tree's arena to serialize data,
2291  // then reserve also the arena's size:
2292  tree.reserve(256); // reserve 256 characters (good enough for this sample)
2293 
2294  ryml::EventHandlerTree evt_handler;
2295  ryml::Parser parser(&evt_handler);
2296  // it is also advised to reserve the parser depth
2297  // to the expected depth of the data tree:
2298  parser.reserve_stack(10); // the parser uses small storage
2299  // optimization defaulting to 16 depth,
2300  // so this instruction is a no-op, and
2301  // the stack will be located in the
2302  // parser object.
2303  parser.reserve_stack(20); // But this will cause an allocation
2304  // because it is above 16.
2305 
2306  ryml::csubstr champagnes = ""
2307  "- Dom Perignon\n"
2308  "- Gosset Grande Reserve\n"
2309  "- Jacquesson 742\n"
2310  "";
2311  ryml::csubstr beers = ""
2312  "- Rochefort 10\n"
2313  "- Busch\n"
2314  "- Leffe Rituel\n"
2315  "- Kasteel Donker\n"
2316  "";
2317  ryml::csubstr wines = ""
2318  "- Soalheiro\n"
2319  "- Niepoort Redoma 2017\n"
2320  "- Vina Esmeralda\n"
2321  "";
2322 
2323  parse_in_arena(&parser, "champagnes.yml", champagnes, &tree);
2324  CHECK(ryml::emitrs_yaml<std::string>(tree) == champagnes);
2325 
2326  // watchout: this will APPEND to the given tree:
2327  parse_in_arena(&parser, "beers.yml", beers, &tree);
2328  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2329  "- Dom Perignon\n"
2330  "- Gosset Grande Reserve\n"
2331  "- Jacquesson 742\n"
2332  "- Rochefort 10\n"
2333  "- Busch\n"
2334  "- Leffe Rituel\n"
2335  "- Kasteel Donker\n"
2336  "");
2337 
2338  // if you don't wish to append, clear the tree first:
2339  tree.clear();
2340  parse_in_arena(&parser, "wines.yml", wines, &tree);
2341  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2342  "- Soalheiro\n"
2343  "- Niepoort Redoma 2017\n"
2344  "- Vina Esmeralda\n"
2345  "");
2346 }
2347 
2348 
2349 //-----------------------------------------------------------------------------
2350 
2351 /** shows how rapidyaml marks the tree nodes with their original style
2352  * in the parsed YAML.
2353  *
2354  * @see doc_tree
2355  * @see doc_node_classes
2356  */
2358 {
2359  ryml::csubstr yaml = ""
2360  "doe: a deer, a female deer" "\n"
2361  "ray: 'a drop of golden sun'" "\n"
2362  "me: \"a name I call myself\"" "\n"
2363  "far: |-" "\n"
2364  " a long long way to go" "\n"
2365  "sow: >-" "\n"
2366  " a needle pulling thread" "\n"
2367  "seq: [0,1,2,3]" "\n"
2368  "map: {" "\n"
2369  " 0: 10," "\n"
2370  " 1: 11," "\n"
2371  " 2: 12," "\n"
2372  " 3: 13" "\n"
2373  " }" "\n"
2374  "";
2375  const ryml::Tree tree = ryml::parse_in_arena(yaml);
2376  // note how every node is tagged with its original style:
2377  CHECK(tree["doe"].is_val_plain());
2378  CHECK(tree["ray"].is_val_squo());
2379  CHECK(tree["me"].is_val_dquo());
2380  CHECK(tree["far"].is_val_literal());
2381  CHECK(tree["sow"].is_val_folded());
2382  CHECK(tree["seq"].is_flow());
2383  CHECK(tree["seq"].is_flow_sl());
2384  CHECK(tree["map"].is_flow());
2385  CHECK(tree["map"].is_flow_mlx());
2386  CHECK(tree.rootref().is_block());
2387  // ... which results in roundtrip-stable YAML:
2388  CHECK(ryml::emitrs_yaml<std::string>(tree) == yaml);
2389 }
2390 
2391 
2392 //-----------------------------------------------------------------------------
2393 
2394 /** shows how to programatically iterate through trees
2395  * @see doc_tree
2396  * @see doc_node_classes
2397  */
2399 {
2400  const ryml::Tree tree = ryml::parse_in_arena(
2401  "doe: a deer, a female deer" "\n"
2402  "ray: a drop of golden sun" "\n"
2403  "pi: 3.14159" "\n"
2404  "xmas: true" "\n"
2405  "french-hens: 3" "\n"
2406  "calling-birds:" "\n"
2407  " - huey" "\n"
2408  " - dewey" "\n"
2409  " - louie" "\n"
2410  " - fred" "\n"
2411  "xmas-fifth-day:" "\n"
2412  " calling-birds: four" "\n"
2413  " french-hens: 3" "\n"
2414  " golden-rings: 5" "\n"
2415  " partridges:" "\n"
2416  " count: 1" "\n"
2417  " location: a pear tree" "\n"
2418  " turtle-doves: two" "\n"
2419  "cars: GTO" "\n"
2420  "");
2421  ryml::ConstNodeRef root = tree.crootref();
2422 
2423  // iterate children
2424  {
2425  std::vector<ryml::csubstr> keys, vals; // to store all the root-level keys, vals
2426  for(ryml::ConstNodeRef n : root.children())
2427  {
2428  keys.emplace_back(n.key());
2429  vals.emplace_back(n.has_val() ? n.val() : ryml::csubstr{});
2430  }
2431  CHECK(keys.size() >= 6);
2432  CHECK(vals.size() >= 6);
2433  if(keys.size() >= 6 && vals.size() >= 6)
2434  {
2435  CHECK(keys[0] == "doe");
2436  CHECK(vals[0] == "a deer, a female deer");
2437  CHECK(keys[1] == "ray");
2438  CHECK(vals[1] == "a drop of golden sun");
2439  CHECK(keys[2] == "pi");
2440  CHECK(vals[2] == "3.14159");
2441  CHECK(keys[3] == "xmas");
2442  CHECK(vals[3] == "true");
2443  CHECK(root[5].has_key());
2444  CHECK(root[5].is_seq());
2445  CHECK(root[5].key() == "calling-birds");
2446  CHECK(!root[5].has_val()); // it is a map, so not a val
2447  //CHECK(root[5].val() == ""); // ERROR! node does not have a val.
2448  CHECK(keys[5] == "calling-birds");
2449  CHECK(vals[5] == "");
2450  }
2451  }
2452 
2453  // iterate siblings
2454  {
2455  size_t count = 0;
2456  ryml::csubstr calling_birds[] = {"huey", "dewey", "louie", "fred"};
2457  for(ryml::ConstNodeRef n : root["calling-birds"][2].siblings())
2458  CHECK(n.val() == calling_birds[count++]);
2459  CHECK(count == 4u);
2460  }
2461 }
2462 
2463 
2464 //-----------------------------------------------------------------------------
2465 
2466 /** demonstrates how to obtain the (zero-based) location of a node
2467  * from a recently parsed tree
2468  *
2469  * @see RYML_LOCATIONS_SMALL_THRESHOLD
2470  * */
2472 {
2473  // NOTE: locations are zero-based. If you intend to show the
2474  // location to a human user, you may want to pre-increment the line
2475  // and column by 1.
2476  ryml::csubstr yaml = ""
2477  "{" "\n"
2478  "aa: contents," "\n"
2479  "foo: [one, [two, three]]" "\n"
2480  "}" "\n"
2481  "";
2482  // A parser is needed to track locations, and it has to be
2483  // explicitly set to do it. Location tracking is disabled by
2484  // default.
2485  ryml::ParserOptions opts = {};
2486  opts.locations(true); // enable locations, default is false
2487  ryml::EventHandlerTree evt_handler = {};
2488  ryml::Parser parser(&evt_handler, opts);
2489  CHECK(parser.options().locations());
2490  // When locations are enabled, the first task while parsing will
2491  // consist of building and caching (in the parser) a
2492  // source-to-node lookup structure to accelerate location lookups.
2493  //
2494  // The cost of building the location accelerator is linear in the
2495  // size of the source buffer. This increased cost is the reason
2496  // for the opt-in requirement. When locations are disabled there
2497  // is no cost.
2498  //
2499  // Building the location accelerator may trigger an allocation,
2500  // but this can and should be avoided by reserving prior to
2501  // parsing:
2502  parser.reserve_locations(50u); // reserve for 50 lines of YAML code
2503  // Now the structure will be built during parsing:
2504  ryml::Tree tree = parse_in_arena(&parser, "source.yml", yaml);
2505  // After this, we are ready to query the location from the parser:
2506  ryml::Location loc = tree.rootref().location(parser);
2507  // As for the complexity of the query: for large buffers it is
2508  // O(log(numlines)). For short source buffers
2509  // (RYML_LOCATIONS_SMALL_THRESHOLD lines and less), it is
2510  // O(numlines), as a plain linear search is faster in this case.
2511  CHECK(parser.location_contents(loc).begins_with("{"));
2512  CHECK(loc.offset == 0u);
2513  CHECK(loc.line == 0u);
2514  CHECK(loc.col == 0u);
2515  // on the next call, we only pay O(log(numlines)) because the
2516  // rebuild is already available:
2517  loc = tree["aa"].location(parser);
2518  CHECK(parser.location_contents(loc).begins_with("aa"));
2519  CHECK(loc.offset == 2u);
2520  CHECK(loc.line == 1u);
2521  CHECK(loc.col == 0u);
2522  // KEYSEQ in flow style: points at the key
2523  loc = tree["foo"].location(parser);
2524  CHECK(parser.location_contents(loc).begins_with("foo"));
2525  CHECK(loc.offset == 16u);
2526  CHECK(loc.line == 2u);
2527  CHECK(loc.col == 0u);
2528  loc = tree["foo"][0].location(parser);
2529  CHECK(parser.location_contents(loc).begins_with("one"));
2530  CHECK(loc.line == 2u);
2531  CHECK(loc.col == 6u);
2532  // SEQ in flow style: location points at the opening '[' (there's no key)
2533  loc = tree["foo"][1].location(parser);
2534  CHECK(parser.location_contents(loc).begins_with("["));
2535  CHECK(loc.line == 2u);
2536  CHECK(loc.col == 11u);
2537  loc = tree["foo"][1][0].location(parser);
2538  CHECK(parser.location_contents(loc).begins_with("two"));
2539  CHECK(loc.line == 2u);
2540  CHECK(loc.col == 12u);
2541  loc = tree["foo"][1][1].location(parser);
2542  CHECK(parser.location_contents(loc).begins_with("three"));
2543  CHECK(loc.line == 2u);
2544  CHECK(loc.col == 17u);
2545  // NOTE. The parser locations always point at the latest buffer to
2546  // be parsed with the parser object, so they must be queried using
2547  // the corresponding latest tree to be parsed. This means that if
2548  // the parser is reused, earlier trees will loose the possibility
2549  // of querying for location. It is undefined behavior to query the
2550  // parser for the location of a node from an earlier tree:
2551  ryml::Tree docval = parse_in_arena(&parser, "docval.yaml", "this is a docval");
2552  // From now on, none of the locations from the previous tree can
2553  // be queried:
2554  //loc = tree.rootref().location(parser); // ERROR, undefined behavior
2555  loc = docval.rootref().location(parser); // OK. this is the latest tree from this parser
2556  CHECK(parser.location_contents(loc).begins_with("this is a docval"));
2557  CHECK(loc.line == 0u);
2558  CHECK(loc.col == 0u);
2559 
2560  // NOTES ABOUT CONTAINER LOCATIONS
2561  ryml::Tree tree2 = parse_in_arena(&parser, "containers.yaml",
2562  "" "\n"
2563  "a new: buffer" "\n"
2564  "to: be parsed" "\n"
2565  "map with key:" "\n"
2566  " first: value" "\n"
2567  " second: value" "\n"
2568  "seq with key:" "\n"
2569  " - first value" "\n"
2570  " - second value" "\n"
2571  " -" "\n"
2572  " - nested first value" "\n"
2573  " - nested second value" "\n"
2574  " -" "\n"
2575  " nested first: value" "\n"
2576  " nested second: value" "\n"
2577  "");
2578  // (Likewise, the docval tree can no longer be used to query.)
2579  //
2580  // For key-less block-style maps, the location of the container
2581  // points at the first child's key. For example, in this case
2582  // the root does not have a key, so its location is taken
2583  // to be at the first child:
2584  loc = tree2.rootref().location(parser);
2585  CHECK(parser.location_contents(loc).begins_with("a new"));
2586  CHECK(loc.offset == 1u);
2587  CHECK(loc.line == 1u);
2588  CHECK(loc.col == 0u);
2589  // note the first child points exactly at the same place:
2590  loc = tree2["a new"].location(parser);
2591  CHECK(parser.location_contents(loc).begins_with("a new"));
2592  CHECK(loc.offset == 1u);
2593  CHECK(loc.line == 1u);
2594  CHECK(loc.col == 0u);
2595  loc = tree2["to"].location(parser);
2596  CHECK(parser.location_contents(loc).begins_with("to"));
2597  CHECK(loc.line == 2u);
2598  CHECK(loc.col == 0u);
2599  // but of course, if the block-style map is a KEYMAP, then the
2600  // location is the map's key, and not the first child's key:
2601  loc = tree2["map with key"].location(parser);
2602  CHECK(parser.location_contents(loc).begins_with("map with key"));
2603  CHECK(loc.line == 3u);
2604  CHECK(loc.col == 0u);
2605  loc = tree2["map with key"]["first"].location(parser);
2606  CHECK(parser.location_contents(loc).begins_with("first"));
2607  CHECK(loc.line == 4u);
2608  CHECK(loc.col == 2u);
2609  loc = tree2["map with key"]["second"].location(parser);
2610  CHECK(parser.location_contents(loc).begins_with("second"));
2611  CHECK(loc.line == 5u);
2612  CHECK(loc.col == 2u);
2613  // same thing for KEYSEQ:
2614  loc = tree2["seq with key"].location(parser);
2615  CHECK(parser.location_contents(loc).begins_with("seq with key"));
2616  CHECK(loc.line == 6u);
2617  CHECK(loc.col == 0u);
2618  loc = tree2["seq with key"][0].location(parser);
2619  CHECK(parser.location_contents(loc).begins_with("first value"));
2620  CHECK(loc.line == 7u);
2621  CHECK(loc.col == 4u);
2622  loc = tree2["seq with key"][1].location(parser);
2623  CHECK(parser.location_contents(loc).begins_with("second value"));
2624  CHECK(loc.line == 8u);
2625  CHECK(loc.col == 4u);
2626  // SEQ nested in SEQ: container location points at the first child's "- " dash
2627  loc = tree2["seq with key"][2].location(parser);
2628  CHECK(parser.location_contents(loc).begins_with("- nested first value"));
2629  CHECK(loc.line == 10u);
2630  CHECK(loc.col == 4u);
2631  loc = tree2["seq with key"][2][0].location(parser);
2632  CHECK(parser.location_contents(loc).begins_with("nested first value"));
2633  CHECK(loc.line == 10u);
2634  CHECK(loc.col == 6u);
2635  // MAP nested in SEQ: same as above: point to key
2636  loc = tree2["seq with key"][3].location(parser);
2637  CHECK(parser.location_contents(loc).begins_with("nested first: "));
2638  CHECK(loc.line == 13u);
2639  CHECK(loc.col == 4u);
2640  loc = tree2["seq with key"][3][0].location(parser);
2641  CHECK(parser.location_contents(loc).begins_with("nested first: "));
2642  CHECK(loc.line == 13u);
2643  CHECK(loc.col == 4u);
2644 }
2645 
2646 
2647 //-----------------------------------------------------------------------------
2648 
2649 /** shows how to programatically create trees
2650  * @see doc_tree
2651  * @see doc_node_classes
2652  * @see sample_create_tree_style()
2653  * */
2655 {
2656  ryml::NodeRef doe;
2657  CHECK(doe.invalid()); // it's pointing at nowhere
2658 
2659  ryml::Tree tree;
2660  ryml::NodeRef root = tree.rootref();
2661  root.set_map(); // mark root as a map
2662  doe = root["doe"];
2663  CHECK(!doe.invalid()); // it's now pointing at the tree
2664  CHECK(doe.is_seed()); // but the tree has nothing there, so this is only a seed
2665 
2666  // set the value of the node
2667  const char a_deer[] = "a deer, a female deer";
2668  doe.set_val(a_deer);
2669  // now the node really exists in the tree, and this ref is no
2670  // longer a seed:
2671  CHECK(!doe.is_seed());
2672  // WATCHOUT for lifetimes:
2673  CHECK(doe.val().str == a_deer); // it is pointing at the initial string
2674  // If you need to avoid lifetime dependency, serialize the data:
2675  {
2676  std::string a_drop = "a drop of golden sun";
2677  // this will copy the string to the tree's arena:
2678  // (see the serialization samples below)
2679  root["ray"].set_serialized(a_drop);
2680  // and now you can modify the original string without changing
2681  // the tree:
2682  a_drop[0] = 'Z';
2683  a_drop[1] = 'Z';
2684  }
2685  CHECK(root["ray"].val() == "a drop of golden sun");
2686 
2687  // etc.
2688  root["pi"].set_serialized(ryml::fmt::real(3.141592654, 5));
2689  root["xmas"].set_serialized(ryml::fmt::boolalpha(true));
2690  root["french-hens"].set_serialized(3);
2691  ryml::NodeRef calling_birds = root["calling-birds"];
2692  calling_birds.set_seq();
2693  calling_birds.append_child().set_val("huey");
2694  calling_birds.append_child().set_val("dewey");
2695  calling_birds.append_child().set_val("louie");
2696  calling_birds.append_child().set_val("fred");
2697  ryml::NodeRef xmas5 = root["xmas-fifth-day"];
2698  xmas5.set_map();
2699  xmas5["calling-birds"].set_val("four");
2700  xmas5["french-hens"].set_serialized(3);
2701  xmas5["golden-rings"].set_serialized(5);
2702  xmas5["partridges"].set_map();
2703  xmas5["partridges"]["count"].set_serialized(1);
2704  xmas5["partridges"]["location"].set_val("a pear tree");
2705  xmas5["turtle-doves"].set_val("two");
2706  root["cars"].set_val("GTO");
2707 
2708  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2709  "doe: a deer, a female deer" "\n"
2710  "ray: a drop of golden sun" "\n"
2711  "pi: 3.14159" "\n"
2712  "xmas: true" "\n"
2713  "french-hens: 3" "\n"
2714  "calling-birds:" "\n"
2715  " - huey" "\n"
2716  " - dewey" "\n"
2717  " - louie" "\n"
2718  " - fred" "\n"
2719  "xmas-fifth-day:" "\n"
2720  " calling-birds: four" "\n"
2721  " french-hens: 3" "\n"
2722  " golden-rings: 5" "\n"
2723  " partridges:" "\n"
2724  " count: 1" "\n"
2725  " location: a pear tree" "\n"
2726  " turtle-doves: two" "\n"
2727  "cars: GTO" "\n"
2728  "");
2729 
2730  // NOTE: it is good practice to set scalar styles when building
2731  // the tree. See the sample_create_tree_style() below.
2732 }
2733 
2734 
2735 //-----------------------------------------------------------------------------
2736 
2737 /** Shows how to set styles when building a tree: this may be needed
2738  * because you want to control the emitted YAML, but for scalars there
2739  * is also a performance reason to do this.
2740  *
2741  * @note You can **and should** set the style when creating a tree for
2742  * emitting later. This is because YAML has constraints on which
2743  * styles can be used for a particular scalar (eg, a plain scalar have
2744  * leading or trailing whitespace, or if in flow mode it cannot have
2745  * comma followed by space).
2746  *
2747  * @note When the scalar is not marked with an explicit style, the
2748  * ryml emitter adheres to these constraints by scanning each scalar
2749  * to choose a style for it. On the other hand, if the scalar is
2750  * marked with an explicit style, the emitter will honor that style,
2751  * and not do the scan.
2752  *
2753  * @note So explicitly setting the style for scalars
2754  * saves the emitter from having to scan each scalar while
2755  * emitting.
2756  *
2757  * @note For containers, setting the style does not offer an emit
2758  * performance improvement like with scalars. Nevertheless, you may
2759  * still find it useful to control the emitted YAML.
2760  *
2761  * Following this recommendation, ryml also explicitly sets the styles
2762  * while parsing.
2763  */
2765 {
2766  ryml::Tree tree;
2767  ryml::NodeRef root = tree;
2768  root.set_map(ryml::BLOCK);
2769  // with .set_*() we should use explicit styles (because .set_*()
2770  // only takes csubstr arguments)
2771  root["not plain"].set_val(" with whitespace "); // no style set
2772  root["doe"].set_val("a deer, a female deer", ryml::VAL_PLAIN);
2773  root["ray"].set_val("a drop of golden sun", ryml::VAL_SQUO);
2774  root["me"].set_val("a name I call myself", ryml::VAL_DQUO);
2775  root["far"].set_val("a long long way to go", ryml::VAL_LITERAL);
2776  root["sow"].set_val("a needle pulling thread", ryml::VAL_FOLDED);
2777  root["seq"].set_seq(ryml::FLOW_SL|ryml::FLOW_SPC); // flow, single-line, with spaces after commas
2778  root["map1"].set_map(ryml::FLOW_ML1); // flow, multiline, 1 value per line
2779  root["mapn"].set_map(ryml::FLOW_MLN); // flow, multiline, N values per line, wrapped
2780  // likewise for all of set_serialized(), set_key(),
2781  // set_key_serialized(), save(), save_key(): all these accept the
2782  // style as an extra argument. But when serializing there's a
2783  // nice feature: ryml will automatically set the scalar style to
2784  // VAL_PLAIN / KEY_PLAIN when its type verifies std::is_arithmetic<T>.
2785  for(int i : {0, 1, 2, 3})
2786  {
2787  ryml::NodeRef childseq = root["seq"].append_child();
2788  ryml::NodeRef childmap1 = root["map1"].append_child();
2789  ryml::NodeRef childmapn = root["mapn"].append_child();
2790  // Note how we're NOT setting the style:
2791  childseq.set_serialized(i);
2792  childmap1.set_key_serialized(i + 1);
2793  childmapn.set_key_serialized(i + 1);
2794  childmap1.set_serialized((i + 1) * 10);
2795  childmapn.set_serialized((i + 1) * 10);
2796  // ... and yet ryml has set it to plain:
2797  CHECK(childseq.is_val_plain());
2798  CHECK(childmap1.is_key_plain());
2799  CHECK(childmapn.is_key_plain());
2800  CHECK(childmap1.is_val_plain());
2801  CHECK(childmapn.is_val_plain());
2802  }
2803  // let's see the styles now:
2804  CHECK(ryml::emitrs_yaml<std::string>(tree) ==
2805  // note how this is quoted, without having explicit style
2806  // set. That is because plain scalars cannot have
2807  // leading/trailing whitespace characters. This scalar did
2808  // not have a style set, so during emitting it was scanned
2809  // to determine its style, and so VAL_SQUO was chosen:
2810  "not plain: ' with whitespace '" "\n"
2811  // as for the rest, the style is honored, and the scalars
2812  // are not scanned by the emitter, resulting in a
2813  // performance increase:
2814  "doe: a deer, a female deer" "\n"
2815  "ray: 'a drop of golden sun'" "\n"
2816  "me: \"a name I call myself\"" "\n"
2817  "far: |-" "\n"
2818  " a long long way to go" "\n"
2819  "sow: >-" "\n"
2820  " a needle pulling thread" "\n"
2821  "seq: [0, 1, 2, 3]" "\n"
2822  "map1: {" "\n"
2823  " 1: 10," "\n"
2824  " 2: 20," "\n"
2825  " 3: 30," "\n"
2826  " 4: 40" "\n"
2827  " }" "\n"
2828  "mapn: {" "\n"
2829  " 1: 10,2: 20,3: 30,4: 40" "\n"
2830  " }" "\n"
2831  "");
2832  // Note that it would be an error to set a scalar style
2833  // incompatible with the scalar contents. ryml always honor the
2834  // node style, so it will blindly emit roundtrip-unstable YAML if
2835  // the style is incompatible.
2836  //
2837  // For example, this is incorrect because a plain scalar cannot
2838  // have leading or trailing whitespace:
2839  root["plain"].set_val(" with whitespace ", ryml::VAL_PLAIN); // incorrect.
2840  // note how the whitespace will be lost when parsed:
2841  CHECK(ryml::emitrs_yaml<std::string>(root["plain"]) ==
2842  "plain: with whitespace " "\n"
2843  "");
2844 }
2845 
2846 
2847 //-----------------------------------------------------------------------------
2848 
2849 /** demonstrates explicit and implicit interaction with the tree's
2850  * string arena. Note that ryml only holds strings in the tree's
2851  * nodes. */
2853 {
2854  // mutable buffers are parsed in place:
2855  {
2856  char buf[] = "[a, b, c, d]";
2857  ryml::substr yml = buf;
2858  ryml::Tree tree = ryml::parse_in_place(yml);
2859  // notice the arena is empty:
2860  CHECK(tree.arena().empty());
2861  // and the tree is pointing at the original buffer:
2862  ryml::NodeRef root = tree.rootref();
2863  CHECK(root[0].val().is_sub(yml));
2864  CHECK(root[1].val().is_sub(yml));
2865  CHECK(root[2].val().is_sub(yml));
2866  CHECK(root[3].val().is_sub(yml));
2867  CHECK(yml.is_super(root[0].val()));
2868  CHECK(yml.is_super(root[1].val()));
2869  CHECK(yml.is_super(root[2].val()));
2870  CHECK(yml.is_super(root[3].val()));
2871  }
2872 
2873  // when parsing immutable buffers, the buffer is first copied to the
2874  // tree's arena; the copy in the arena is then the buffer which is
2875  // actually parsed
2876  {
2877  ryml::csubstr yml = "[a, b, c, d]";
2878  ryml::Tree tree = ryml::parse_in_arena(yml);
2879  // notice the buffer was copied to the arena:
2880  CHECK(tree.arena().data() != yml.data());
2881  CHECK(tree.arena() == yml);
2882  // and the tree is pointing at the arena instead of to the
2883  // original buffer:
2884  ryml::NodeRef root = tree.rootref();
2885  ryml::csubstr arena = tree.arena();
2886  CHECK(root[0].val().is_sub(arena));
2887  CHECK(root[1].val().is_sub(arena));
2888  CHECK(root[2].val().is_sub(arena));
2889  CHECK(root[3].val().is_sub(arena));
2890  CHECK(arena.is_super(root[0].val()));
2891  CHECK(arena.is_super(root[1].val()));
2892  CHECK(arena.is_super(root[2].val()));
2893  CHECK(arena.is_super(root[3].val()));
2894  }
2895 
2896  // the arena is also used when the data is serialized to the tree.
2897  // first example: parse in place
2898  {
2899  char buf[] = "[a, b, c, d]"; // mutable
2900  ryml::substr yml = buf;
2901  ryml::Tree tree = ryml::parse_in_place(yml);
2902  // notice the arena is empty:
2903  CHECK(tree.arena().empty());
2904  ryml::NodeRef root = tree.rootref();
2905 
2906  // serialize an integer, and mutate the tree
2907  CHECK(root[2].val() == "c");
2908  CHECK(root[2].val().is_sub(yml)); // val is first pointing at the buffer
2909  root[2].set_serialized(12345);
2910  CHECK(root[2].val() == "12345");
2911  CHECK(root[2].val().is_sub(tree.arena())); // now val is pointing at the arena
2912  // notice the serialized string was appended to the tree's arena:
2913  CHECK(tree.arena() == "12345");
2914 
2915  // serialize an integer, and mutate the tree
2916  CHECK(root[3].val() == "d");
2917  CHECK(root[3].val().is_sub(yml)); // val is first pointing at the buffer
2918  root[3].set_serialized(67890);
2919  CHECK(root[3].val() == "67890");
2920  CHECK(root[3].val().is_sub(tree.arena())); // now val is pointing at the arena
2921  // notice the serialized string was appended to the tree's arena:
2922  CHECK(tree.arena() == "1234567890");
2923  }
2924  // the arena is also used when the data is serialized to string
2925  // second example: parse in arena
2926  {
2927  ryml::csubstr yml = "[a, b, c, d]"; // immutable
2928  ryml::Tree tree = ryml::parse_in_arena(yml);
2929  // notice the buffer was copied to the arena:
2930  CHECK(tree.arena().data() != yml.data());
2931  CHECK(tree.arena() == yml);
2932  ryml::NodeRef root = tree.rootref();
2933 
2934  // serialize an integer, and mutate the tree
2935  CHECK(root[2].val() == "c");
2936  root[2].set_serialized(12345); // serialize an integer
2937  CHECK(root[2].val() == "12345");
2938  // notice the serialized string was appended to the tree's arena:
2939  // notice also the previous values remain there.
2940  // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2941  CHECK(tree.arena() == "[a, b, c, d]12345");
2942  // old values: --------------^
2943 
2944  // serialize an integer, and mutate the tree
2945  root[3].set_serialized(67890);
2946  CHECK(root[3].val() == "67890");
2947  // notice the serialized string was appended to the tree's arena:
2948  // notice also the previous values remain there.
2949  // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2950  CHECK(tree.arena() == "[a, b, c, d]1234567890");
2951  // old values: --------------^ ---^^^^^
2952  }
2953 
2954  // to_arena(): directly serialize values to the arena:
2955  {
2956  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2957  ryml::csubstr c10 = tree.to_arena(10101010);
2958  CHECK(c10 == "10101010");
2959  CHECK(c10.is_sub(tree.arena()));
2960  CHECK(tree.arena() == "{a: b}10101010");
2961  CHECK(tree.key(1) == "a");
2962  CHECK(tree.val(1) == "b");
2963  tree.set_val(1, c10);
2964  CHECK(tree.val(1) == c10);
2965  // and you can also do it through a node:
2966  ryml::NodeRef root = tree.rootref();
2967  root["a"].set_serialized(2222);
2968  CHECK(root["a"].val() == "2222");
2969  CHECK(tree.arena() == "{a: b}101010102222");
2970  }
2971 
2972  // copy_to_arena(): manually copy a string to the arena:
2973  {
2974  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2975  ryml::csubstr mystr = "Gosset Grande Reserve";
2976  ryml::csubstr copied = tree.copy_to_arena(mystr);
2977  CHECK(!copied.overlaps(mystr));
2978  CHECK(copied == mystr);
2979  CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2980  }
2981 
2982  // alloc_arena(): allocate a buffer from the arena:
2983  {
2984  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2985  ryml::csubstr mystr = "Gosset Grande Reserve";
2986  ryml::substr copied = tree.alloc_arena(mystr.size());
2987  CHECK(!copied.overlaps(mystr));
2988  memcpy(copied.str, mystr.str, mystr.len);
2989  CHECK(copied == mystr);
2990  CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2991  }
2992 
2993  // reserve_arena(): ensure the arena has a certain size to avoid reallocations
2994  {
2995  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2996  CHECK(tree.arena().size() == strlen("{a: b}"));
2997  tree.reserve_arena(100);
2998  CHECK(tree.arena_capacity() >= 100);
2999  CHECK(tree.arena().size() == strlen("{a: b}"));
3000  tree.to_arena(123456);
3001  CHECK(tree.arena().first(12) == "{a: b}123456");
3002  }
3003 }
3004 
3005 
3006 //-----------------------------------------------------------------------------
3007 
3008 /** ryml provides facilities for serializing and deserializing the C++
3009  fundamental types, including boolean and null values; this is
3010  provided by the several overloads in @ref doc_to_chars and @ref
3011  doc_from_chars. To add serialization for user scalar types (ie,
3012  those types that should be serialized as strings in leaf nodes),
3013  you just need to define the appropriate overloads of to_chars and
3014  from_chars for those types; see @ref sample_user_scalar_types for
3015  an example on how to achieve this, and see @ref doc_serialization
3016  for more information on serialization. */
3018 {
3019  ryml::Tree tree;
3020  CHECK(tree.arena().empty());
3021  CHECK(tree.to_arena('a') == "a"); CHECK(tree.arena() == "a");
3022  CHECK(tree.to_arena("bcde") == "bcde"); CHECK(tree.arena() == "abcde");
3023  CHECK(tree.to_arena(unsigned(0)) == "0"); CHECK(tree.arena() == "abcde0");
3024  CHECK(tree.to_arena(int(1)) == "1"); CHECK(tree.arena() == "abcde01");
3025  CHECK(tree.to_arena(uint8_t(0)) == "0"); CHECK(tree.arena() == "abcde010");
3026  CHECK(tree.to_arena(uint16_t(1)) == "1"); CHECK(tree.arena() == "abcde0101");
3027  CHECK(tree.to_arena(uint32_t(2)) == "2"); CHECK(tree.arena() == "abcde01012");
3028  CHECK(tree.to_arena(uint64_t(3)) == "3"); CHECK(tree.arena() == "abcde010123");
3029  CHECK(tree.to_arena(int8_t( 4)) == "4"); CHECK(tree.arena() == "abcde0101234");
3030  CHECK(tree.to_arena(int8_t(-4)) == "-4"); CHECK(tree.arena() == "abcde0101234-4");
3031  CHECK(tree.to_arena(int16_t( 5)) == "5"); CHECK(tree.arena() == "abcde0101234-45");
3032  CHECK(tree.to_arena(int16_t(-5)) == "-5"); CHECK(tree.arena() == "abcde0101234-45-5");
3033  CHECK(tree.to_arena(int32_t( 6)) == "6"); CHECK(tree.arena() == "abcde0101234-45-56");
3034  CHECK(tree.to_arena(int32_t(-6)) == "-6"); CHECK(tree.arena() == "abcde0101234-45-56-6");
3035  CHECK(tree.to_arena(int64_t( 7)) == "7"); CHECK(tree.arena() == "abcde0101234-45-56-67");
3036  CHECK(tree.to_arena(int64_t(-7)) == "-7"); CHECK(tree.arena() == "abcde0101234-45-56-67-7");
3037  CHECK(tree.to_arena((void*)1) == "0x1"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x1");
3038  CHECK(tree.to_arena(float(0.124)) == "0.124"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.124");
3039  CHECK(tree.to_arena(double(0.234)) == "0.234"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.234");
3040 
3041  // write boolean values - see also sample_formatting()
3042  CHECK(tree.to_arena(bool(true)) == "1"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.2341");
3043  CHECK(tree.to_arena(bool(false)) == "0"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410");
3044  CHECK(tree.to_arena(c4::fmt::boolalpha(true)) == "true"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410true");
3045  CHECK(tree.to_arena(c4::fmt::boolalpha(false)) == "false"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse");
3046 
3047  // write special float values
3048  // see also sample_float_precision()
3049  const float fnan = std::numeric_limits<float >::quiet_NaN();
3050  const double dnan = std::numeric_limits<double>::quiet_NaN();
3051  const float finf = std::numeric_limits<float >::infinity();
3052  const double dinf = std::numeric_limits<double>::infinity();
3053  CHECK(tree.to_arena( finf) == ".inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf");
3054  CHECK(tree.to_arena( dinf) == ".inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf");
3055  CHECK(tree.to_arena(-finf) == "-.inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf");
3056  CHECK(tree.to_arena(-dinf) == "-.inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf");
3057  CHECK(tree.to_arena( fnan) == ".nan"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf.nan");
3058  CHECK(tree.to_arena( dnan) == ".nan"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf.nan.nan");
3059 
3060  // read special float values
3061  // see also sample_float_precision()
3062  C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wfloat-equal");
3063  tree = ryml::parse_in_arena(R"({ninf: -.inf, pinf: .inf, nan: .nan})");
3064  float f = 0.f;
3065  double d = 0.;
3066  CHECK(f == 0.f);
3067  CHECK(d == 0.);
3068  tree["ninf"].load(&f); CHECK(f == -finf);
3069  tree["ninf"].load(&d); CHECK(d == -dinf);
3070  tree["pinf"].load(&f); CHECK(f == finf);
3071  tree["pinf"].load(&d); CHECK(d == dinf);
3072  tree["nan" ].load(&f); CHECK(std::isnan(f));
3073  tree["nan" ].load(&d); CHECK(std::isnan(d));
3074  C4_SUPPRESS_WARNING_GCC_CLANG_POP
3075 
3076  // value overflow detection:
3077  // (for integral types only)
3078  {
3079  // we will be detecting errors below, so we use this sample helper
3080  ScopedErrorHandlerExample err = {};
3081  ryml::Tree t(err.callbacks()); // instantiate with the error-detecting callbacks
3082  // create a simple tree with an int value
3083  ryml::parse_in_arena(R"({val: 258})", &t);
3084  // by default, overflow is not detected:
3085  uint8_t valu8 = 0;
3086  int8_t vali8 = 0;
3087  ryml::ConstNodeRef n = t["val"];
3088  n.load(&valu8); CHECK(valu8 == 2); // not 257; it wrapped around
3089  n.load(&vali8); CHECK(vali8 == 2); // not 257; it wrapped around
3090  // ...but there are facilities to detect overflow
3091  CHECK(ryml::overflows<uint8_t>(n.val()));
3092  CHECK(ryml::overflows<int8_t>(n.val()));
3093  CHECK( ! ryml::overflows<int16_t>(n.val()));
3094  // and there is a format helper
3095  CHECK(err.check_error_occurs([&]{
3096  n.load(ryml::fmt::overflow_checked(valu8)); // this will cause an error
3097  }));
3098  CHECK(err.check_error_occurs([&]{
3099  n.load(ryml::fmt::overflow_checked(vali8)); // this will cause an error
3100  }));
3101  }
3102 }
3103 
3104 
3105 //-----------------------------------------------------------------------------
3106 
3107 /** Shows how to deal with empty/null values. See also @ref
3108  * c4::yml::Tree::val_is_null */
3110 {
3111  // reading empty/null values - see also sample_formatting()
3113  "plain:" "\n"
3114  "squoted: ''" "\n"
3115  "dquoted: \"\"" "\n"
3116  "literal: |" "\n"
3117  "folded: >" "\n"
3118  "all_null: [~, null, Null, NULL]" "\n"
3119  "non_null: [nULL, non_null, non null, null it is not]" "\n"
3120  "");
3121  // first, remember that .has_val() is a structural predicate
3122  // indicating the node is a leaf, and not a container.
3123  CHECK(tree["plain"].has_val()); // has a val, even if it's empty!
3124  CHECK(tree["squoted"].has_val());
3125  CHECK(tree["dquoted"].has_val());
3126  CHECK(tree["literal"].has_val());
3127  CHECK(tree["folded"].has_val());
3128  CHECK( ! tree["all_null"].has_val());
3129  CHECK( ! tree["non_null"].has_val());
3130  // In essence, has_val() is the logical opposite of is_container()
3131  CHECK( ! tree["plain"].is_container());
3132  CHECK( ! tree["squoted"].is_container());
3133  CHECK( ! tree["dquoted"].is_container());
3134  CHECK( ! tree["literal"].is_container());
3135  CHECK( ! tree["folded"].is_container());
3136  CHECK(tree["all_null"].is_container());
3137  CHECK(tree["non_null"].is_container());
3138  //
3139  // Right. How about the contents of each val?
3140  //
3141  // all of these scalars have zero-length:
3142  CHECK(tree["plain"].val().len == 0);
3143  CHECK(tree["squoted"].val().len == 0);
3144  CHECK(tree["dquoted"].val().len == 0);
3145  CHECK(tree["literal"].val().len == 0);
3146  CHECK(tree["folded"].val().len == 0);
3147  // but only the empty scalar has null string:
3148  CHECK(tree["plain"].val().str == nullptr);
3149  CHECK(tree["squoted"].val().str != nullptr);
3150  CHECK(tree["dquoted"].val().str != nullptr);
3151  CHECK(tree["literal"].val().str != nullptr);
3152  CHECK(tree["folded"].val().str != nullptr);
3153  // likewise, scalar comparison to nullptr has the same results:
3154  // (remember that .val() gives you the scalar value, node must
3155  // have a val, ie must be a leaf node, not a container)
3156  CHECK(tree["plain"].val() == nullptr);
3157  CHECK(tree["squoted"].val() != nullptr);
3158  CHECK(tree["dquoted"].val() != nullptr);
3159  CHECK(tree["literal"].val() != nullptr);
3160  CHECK(tree["folded"].val() != nullptr);
3161  // the tree and node classes provide the corresponding predicate
3162  // functions .key_is_null() and .val_is_null().
3163  // (note that these functions have the same preconditions as .val(),
3164  // because they need get the val to look into its contents)
3165  CHECK(tree["plain"].val_is_null());
3166  CHECK( ! tree["squoted"].val_is_null());
3167  CHECK( ! tree["dquoted"].val_is_null());
3168  CHECK( ! tree["literal"].val_is_null());
3169  CHECK( ! tree["folded"].val_is_null());
3170  // matching to null is case-sensitive. only the cases shown here
3171  // match to null:
3172  for(ryml::ConstNodeRef child : tree["all_null"].children())
3173  {
3174  CHECK(child.val() != nullptr); // it is pointing at a string, so it is not nullptr!
3175  CHECK(child.val_is_null());
3176  }
3177  for(ryml::ConstNodeRef child : tree["non_null"].children())
3178  {
3179  CHECK(child.val() != nullptr);
3180  CHECK( ! child.val_is_null());
3181  }
3182  //
3183  //
3184  // Because the meaning of null/~/empty will vary from application
3185  // to application, ryml makes no assumption on what should be
3186  // serialized as null. It leaves this decision to the user. But
3187  // it also provides the proper toolbox for the user to implement
3188  // its intended solution.
3189  //
3190  // writing/disambiguating null values:
3191  ryml::csubstr null = {};
3192  ryml::csubstr nonnull = "";
3193  ryml::csubstr strnull = "null";
3194  ryml::csubstr tilde = "~";
3195  CHECK(null .len == 0); CHECK(null .str == nullptr); CHECK(null == nullptr);
3196  CHECK(nonnull.len == 0); CHECK(nonnull.str != nullptr); CHECK(nonnull != nullptr);
3197  CHECK(strnull.len != 0); CHECK(strnull.str != nullptr); CHECK(strnull != nullptr);
3198  CHECK(tilde .len != 0); CHECK(tilde .str != nullptr); CHECK(tilde != nullptr);
3199  tree.clear();
3200  tree.clear_arena();
3201  tree.rootref().set_map();
3202  // serializes as an empty plain scalar:
3203  tree["empty_null"].set_serialized(null); CHECK(tree.arena() == "");
3204  // serializes as an empty quoted scalar:
3205  tree["empty_nonnull"].set_serialized(nonnull); CHECK(tree.arena() == "");
3206  // serializes as the normal 'null' string:
3207  tree["str_null"].set_serialized(strnull); CHECK(tree.arena() == "null");
3208  // serializes as the normal '~' string:
3209  tree["str_tilde"].set_serialized(tilde); CHECK(tree.arena() == "null~");
3210  // this is the resulting yaml:
3211  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
3212  "empty_null: " "\n"
3213  "empty_nonnull: ''" "\n"
3214  "str_null: null" "\n"
3215  "str_tilde: ~" "\n"
3216  "");
3217  // To enforce a particular concept of what is a null string, you
3218  // can use the appropriate condition based on pointer nullity or
3219  // other appropriate criteria.
3220  //
3221  // As an example, proper comparison to nullptr:
3222  auto null_if_nullptr = [](ryml::csubstr s) {
3223  return s.str == nullptr ? "null" : s;
3224  };
3225  tree["empty_null"].set_serialized(null_if_nullptr(null));
3226  tree["empty_nonnull"].set_serialized(null_if_nullptr(nonnull));
3227  tree["str_null"].set_serialized(null_if_nullptr(strnull));
3228  tree["str_tilde"].set_serialized(null_if_nullptr(tilde));
3229  // this is the resulting yaml:
3230  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
3231  "empty_null: null" "\n"
3232  "empty_nonnull: ''" "\n"
3233  "str_null: null" "\n"
3234  "str_tilde: ~" "\n"
3235  "");
3236  //
3237  // As another example, nullity check based on the YAML nullity
3238  // predicate:
3239  auto null_if_predicate = [](ryml::csubstr s) {
3240  return ryml::scalar_is_null(s) ? "null" : s;
3241  };
3242  tree["empty_null"].set_serialized(null_if_predicate(null));
3243  tree["empty_nonnull"].set_serialized(null_if_predicate(nonnull));
3244  tree["str_null"].set_serialized(null_if_predicate(strnull));
3245  tree["str_tilde"].set_serialized(null_if_predicate(tilde));
3246  // this is the resulting yaml:
3247  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
3248  "empty_null: null" "\n"
3249  "empty_nonnull: ''" "\n"
3250  "str_null: null" "\n"
3251  "str_tilde: null" "\n"
3252  "");
3253  //
3254  // As another example, nullity check based on the YAML nullity
3255  // predicate, but returning "~" to simbolize nullity:
3256  auto tilde_if_predicate = [](ryml::csubstr s) {
3257  return ryml::scalar_is_null(s) ? "~" : s;
3258  };
3259  tree["empty_null"].set_serialized(tilde_if_predicate(null));
3260  tree["empty_nonnull"].set_serialized(tilde_if_predicate(nonnull));
3261  tree["str_null"].set_serialized(tilde_if_predicate(strnull));
3262  tree["str_tilde"].set_serialized(tilde_if_predicate(tilde));
3263  // this is the resulting yaml:
3264  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
3265  "empty_null: ~" "\n"
3266  "empty_nonnull: ''" "\n"
3267  "str_null: ~" "\n"
3268  "str_tilde: ~" "\n"
3269  "");
3270 }
3271 
3272 
3273 //-----------------------------------------------------------------------------
3274 
3275 /** ryml provides facilities for formatting/deformatting (imported
3276  * from c4core into the ryml namespace). See @ref doc_format_utils
3277  * . These functions are very useful to serialize and deserialize
3278  * scalar types; see @ref doc_serialization .
3279  */
3281 {
3282  // format(), format_sub(), formatrs(): format arguments
3283  {
3284  char buf_[256] = {};
3285  ryml::substr buf = buf_;
3286  size_t size = ryml::format(buf, "a={} foo {} {} bar {}", 0.1, 10, 11, 12);
3287  CHECK(size == strlen("a=0.1 foo 10 11 bar 12"));
3288  CHECK(buf.first(size) == "a=0.1 foo 10 11 bar 12");
3289  // it is safe to call on an empty buffer:
3290  // returns the size needed for the result, and no overflow occurs:
3291  size = ryml::format({} , "a={} foo {} {} bar {}", "this_is_a", 10, 11, 12);
3292  CHECK(size == ryml::format(buf, "a={} foo {} {} bar {}", "this_is_a", 10, 11, 12));
3293  CHECK(size == strlen("a=this_is_a foo 10 11 bar 12"));
3294  // it is also safe to call on an insufficient buffer:
3295  char smallbuf[8] = {};
3296  size = ryml::format(smallbuf, "{} is too large {}", "this", "for the buffer");
3297  CHECK(size == strlen("this is too large for the buffer"));
3298  // ... and the result is truncated at the buffer size:
3299  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "this is\0");
3300 
3301  // format_sub() directly returns the written string:
3302  ryml::csubstr result = ryml::format_sub(buf, "b={}, damn it.", 1);
3303  CHECK(result == "b=1, damn it.");
3304  CHECK(result.is_sub(buf));
3305 
3306  // formatrs() means FORMAT & ReSize:
3307  //
3308  // Instead of a substr, it receives any owning linear char container
3309  // for which to_substr() is defined (using ADL).
3310  // <ryml_std.hpp> has to_substr() definitions for std::string and
3311  // std::vector<char>.
3312  //
3313  // formatrs() starts by calling format(), and if needed, resizes the container
3314  // and calls format() again.
3315  //
3316  // Note that unless the container is previously sized, this
3317  // may cause an allocation, which will make your code slower.
3318  // Make sure to call .reserve() on the container for real
3319  // production code.
3320  std::string sbuf;
3321  ryml::formatrs(&sbuf, "and c={} seems about right", 2);
3322  CHECK(sbuf == "and c=2 seems about right");
3323  std::vector<char> vbuf; // works with any linear char container
3324  ryml::formatrs(&vbuf, "and c={} seems about right", 2);
3325  CHECK(sbuf == "and c=2 seems about right");
3326  // with formatrs() it is also possible to append:
3327  ryml::formatrs_append(&sbuf, ", and finally d={} - done", 3);
3328  CHECK(sbuf == "and c=2 seems about right, and finally d=3 - done");
3329  }
3330 
3331  // unformat(): read arguments - opposite of format()
3332  {
3333  char buf_[256];
3334 
3335  int a = 0, b = 1, c = 2;
3336  ryml::csubstr result = ryml::format_sub(buf_, "{} and {} and {}", a, b, c);
3337  CHECK(result == "0 and 1 and 2");
3338  int aa = -1, bb = -2, cc = -3;
3339  size_t num_characters = ryml::unformat(result, "{} and {} and {}", aa, bb, cc);
3340  CHECK(num_characters != ryml::csubstr::npos); // if a conversion fails, returns ryml::csubstr::npos
3341  CHECK(num_characters == result.size());
3342  CHECK(aa == a);
3343  CHECK(bb == b);
3344  CHECK(cc == c);
3345 
3346  result = ryml::format_sub(buf_, "{} and {} and {}", 10, 20, 30);
3347  CHECK(result == "10 and 20 and 30");
3348  num_characters = ryml::unformat(result, "{} and {} and {}", aa, bb, cc);
3349  CHECK(num_characters != ryml::csubstr::npos); // if a conversion fails, returns ryml::csubstr::npos
3350  CHECK(num_characters == result.size());
3351  CHECK(aa == 10);
3352  CHECK(bb == 20);
3353  CHECK(cc == 30);
3354  }
3355 
3356  // cat(), cat_sub(), catrs(): concatenate arguments
3357  {
3358  char buf_[256] = {};
3359  ryml::substr buf = buf_;
3360  size_t size = ryml::cat(buf, "a=", 0.1, "foo", 10, 11, "bar", 12);
3361  CHECK(size == strlen("a=0.1foo1011bar12"));
3362  CHECK(buf.first(size) == "a=0.1foo1011bar12");
3363  // it is safe to call on an empty buffer:
3364  // returns the size needed for the result, and no overflow occurs:
3365  CHECK(ryml::cat({}, "a=", 0) == 3);
3366  // it is also safe to call on an insufficient buffer:
3367  char smallbuf[8] = {};
3368  size = ryml::cat(smallbuf, "this", " is too large ", "for the buffer");
3369  CHECK(size == strlen("this is too large for the buffer"));
3370  // ... and the result is truncated at the buffer size:
3371  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "this is\0");
3372 
3373  // cat_sub() directly returns the written string:
3374  ryml::csubstr result = ryml::cat_sub(buf, "b=", 1, ", damn it.");
3375  CHECK(result == "b=1, damn it.");
3376  CHECK(result.is_sub(buf));
3377 
3378  // catrs() means CAT & ReSize:
3379  //
3380  // Instead of a substr, it receives any owning linear char container
3381  // for which to_substr() is defined (using ADL).
3382  // <ryml_std.hpp> has to_substr() definitions for std::string and
3383  // std::vector<char>.
3384  //
3385  // catrs() starts by calling cat(), and if needed, resizes the container
3386  // and calls cat() again.
3387  //
3388  // Note that unless the container is previously sized, this
3389  // may cause an allocation, which will make your code slower.
3390  // Make sure to call .reserve() on the container for real
3391  // production code.
3392  std::string sbuf;
3393  ryml::catrs(&sbuf, "and c=", 2, " seems about right");
3394  CHECK(sbuf == "and c=2 seems about right");
3395  std::vector<char> vbuf; // works with any linear char container
3396  ryml::catrs(&vbuf, "and c=", 2, " seems about right");
3397  CHECK(sbuf == "and c=2 seems about right");
3398  // with catrs() it is also possible to append:
3399  ryml::catrs_append(&sbuf, ", and finally d=", 3, " - done");
3400  CHECK(sbuf == "and c=2 seems about right, and finally d=3 - done");
3401  }
3402 
3403  // uncat(): read arguments - opposite of cat()
3404  {
3405  char buf_[256];
3406 
3407  int a = 0, b = 1, c = 2;
3408  ryml::csubstr result = ryml::cat_sub(buf_, a, ' ', b, ' ', c);
3409  CHECK(result == "0 1 2");
3410  int aa = -1, bb = -2, cc = -3;
3411  char sep1 = 'a', sep2 = 'b';
3412  size_t num_characters = ryml::uncat(result, aa, sep1, bb, sep2, cc);
3413  CHECK(num_characters == result.size());
3414  CHECK(aa == a);
3415  CHECK(bb == b);
3416  CHECK(cc == c);
3417  CHECK(sep1 == ' ');
3418  CHECK(sep2 == ' ');
3419 
3420  result = ryml::cat_sub(buf_, 10, ' ', 20, ' ', 30);
3421  CHECK(result == "10 20 30");
3422  num_characters = ryml::uncat(result, aa, sep1, bb, sep2, cc);
3423  CHECK(num_characters == result.size());
3424  CHECK(aa == 10);
3425  CHECK(bb == 20);
3426  CHECK(cc == 30);
3427  CHECK(sep1 == ' ');
3428  CHECK(sep2 == ' ');
3429  }
3430 
3431  // catsep(), catsep_sub(), catseprs(): concatenate arguments, with a separator
3432  {
3433  char buf_[256] = {};
3434  ryml::substr buf = buf_;
3435  // use ' ' as a separator
3436  size_t size = ryml::catsep(buf, ' ', "a=", 0, "b=", 1, "c=", 2, 45, 67);
3437  CHECK(buf.first(size) == "a= 0 b= 1 c= 2 45 67");
3438  // any separator may be used
3439  // use " and " as a separator
3440  size = ryml::catsep(buf, " and ", "a=0", "b=1", "c=2", 45, 67);
3441  CHECK(buf.first(size) == "a=0 and b=1 and c=2 and 45 and 67");
3442  // use " ... " as a separator
3443  size = ryml::catsep(buf, " ... ", "a=0", "b=1", "c=2", 45, 67);
3444  CHECK(buf.first(size) == "a=0 ... b=1 ... c=2 ... 45 ... 67");
3445  // use '/' as a separator
3446  size = ryml::catsep(buf, '/', "a=", 0, "b=", 1, "c=", 2, 45, 67);
3447  CHECK(buf.first(size) == "a=/0/b=/1/c=/2/45/67");
3448  // use 888 as a separator
3449  size = ryml::catsep(buf, 888, "a=0", "b=1", "c=2", 45, 67);
3450  CHECK(buf.first(size) == "a=0888b=1888c=28884588867");
3451 
3452  // it is safe to call on an empty buffer:
3453  // returns the size needed for the result, and no overflow occurs:
3454  CHECK(size == ryml::catsep({}, 888, "a=0", "b=1", "c=2", 45, 67));
3455  // it is also safe to call on an insufficient buffer:
3456  char smallbuf[8] = {};
3457  CHECK(size == ryml::catsep(smallbuf, 888, "a=0", "b=1", "c=2", 45, 67));
3458  CHECK(size == strlen("a=0888b=1888c=28884588867"));
3459  // ... and the result is truncated:
3460  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "a=0888b\0");
3461 
3462  // catsep_sub() directly returns the written substr:
3463  ryml::csubstr result = ryml::catsep_sub(buf, " and ", "a=0", "b=1", "c=2", 45, 67);
3464  CHECK(result == "a=0 and b=1 and c=2 and 45 and 67");
3465  CHECK(result.is_sub(buf));
3466 
3467  // catseprs() means CATSEP & ReSize:
3468  //
3469  // Instead of a substr, it receives any owning linear char container
3470  // for which to_substr() is defined (using ADL).
3471  // <ryml_std.hpp> has to_substr() definitions for std::string and
3472  // std::vector<char>.
3473  //
3474  // catseprs() starts by calling catsep(), and if needed, resizes the container
3475  // and calls catsep() again.
3476  //
3477  // Note that unless the container is previously sized, this
3478  // may cause an allocation, which will make your code slower.
3479  // Make sure to call .reserve() on the container for real
3480  // production code.
3481  std::string sbuf;
3482  ryml::catseprs(&sbuf, " and ", "a=0", "b=1", "c=2", 45, 67);
3483  CHECK(sbuf == "a=0 and b=1 and c=2 and 45 and 67");
3484  std::vector<char> vbuf; // works with any linear char container
3485  ryml::catseprs(&vbuf, " and ", "a=0", "b=1", "c=2", 45, 67);
3486  CHECK(ryml::to_csubstr(vbuf) == "a=0 and b=1 and c=2 and 45 and 67");
3487 
3488  // with catseprs() it is also possible to append:
3489  ryml::catseprs_append(&sbuf, " well ", " --- a=0", "b=11", "c=12", 145, 167);
3490  CHECK(sbuf == "a=0 and b=1 and c=2 and 45 and 67 --- a=0 well b=11 well c=12 well 145 well 167");
3491  }
3492 
3493  // uncatsep(): read arguments with a separator - opposite of catsep()
3494  {
3495  char buf_[256] = {};
3496 
3497  int a = 0, b = 1, c = 2;
3498  ryml::csubstr result = ryml::catsep_sub(buf_, ' ', a, b, c);
3499  CHECK(result == "0 1 2");
3500  int aa = -1, bb = -2, cc = -3;
3501  size_t num_characters = ryml::uncatsep(result, " ", aa, bb, cc);
3502  CHECK(num_characters == result.size());
3503  CHECK(aa == a);
3504  CHECK(bb == b);
3505  CHECK(cc == c);
3506 
3507  result = ryml::catsep_sub(buf_, "--", 10, 20, 30);
3508  CHECK(result == "10--20--30");
3509  num_characters = ryml::uncatsep(result, "--", aa, bb, cc);
3510  CHECK(num_characters == result.size());
3511  CHECK(aa == 10);
3512  CHECK(bb == 20);
3513  CHECK(cc == 30);
3514  }
3515 
3516  // formatting individual arguments
3517  {
3518  using namespace ryml; // all the symbols below are in the ryml namespace.
3519  char buf_[256] = {}; // all the results below are written in this buffer
3520  substr buf = buf_;
3521  // --------------------------------------
3522  // fmt::boolalpha(): format as true/false
3523  // --------------------------------------
3524  // just as with std streams, printing a bool will output the integer value:
3525  CHECK("0" == cat_sub(buf, false));
3526  CHECK("1" == cat_sub(buf, true));
3527  // to force a "true"/"false", use fmt::boolalpha:
3528  CHECK("false" == cat_sub(buf, fmt::boolalpha(false)));
3529  CHECK("true" == cat_sub(buf, fmt::boolalpha(true)));
3530 
3531  // ---------------------------------
3532  // fmt::hex(): format as hexadecimal
3533  // ---------------------------------
3534  CHECK("0xff" == cat_sub(buf, fmt::hex(255)));
3535  CHECK("0x100" == cat_sub(buf, fmt::hex(256)));
3536  CHECK("-0xff" == cat_sub(buf, fmt::hex(-255)));
3537  CHECK("-0x100" == cat_sub(buf, fmt::hex(-256)));
3538  CHECK("3735928559" == cat_sub(buf, UINT32_C(0xdeadbeef)));
3539  CHECK("0xdeadbeef" == cat_sub(buf, fmt::hex(UINT32_C(0xdeadbeef))));
3540  // ----------------------------
3541  // fmt::bin(): format as binary
3542  // ----------------------------
3543  CHECK("0b1000" == cat_sub(buf, fmt::bin(8)));
3544  CHECK("0b1001" == cat_sub(buf, fmt::bin(9)));
3545  CHECK("0b10001" == cat_sub(buf, fmt::bin(17)));
3546  CHECK("0b11001" == cat_sub(buf, fmt::bin(25)));
3547  CHECK("-0b1000" == cat_sub(buf, fmt::bin(-8)));
3548  CHECK("-0b1001" == cat_sub(buf, fmt::bin(-9)));
3549  CHECK("-0b10001" == cat_sub(buf, fmt::bin(-17)));
3550  CHECK("-0b11001" == cat_sub(buf, fmt::bin(-25)));
3551  // ---------------------------
3552  // fmt::bin(): format as octal
3553  // ---------------------------
3554  CHECK("0o77" == cat_sub(buf, fmt::oct(63)));
3555  CHECK("0o100" == cat_sub(buf, fmt::oct(64)));
3556  CHECK("0o377" == cat_sub(buf, fmt::oct(255)));
3557  CHECK("0o400" == cat_sub(buf, fmt::oct(256)));
3558  CHECK("0o1000" == cat_sub(buf, fmt::oct(512)));
3559  CHECK("-0o77" == cat_sub(buf, fmt::oct(-63)));
3560  CHECK("-0o100" == cat_sub(buf, fmt::oct(-64)));
3561  CHECK("-0o377" == cat_sub(buf, fmt::oct(-255)));
3562  CHECK("-0o400" == cat_sub(buf, fmt::oct(-256)));
3563  CHECK("-0o1000" == cat_sub(buf, fmt::oct(-512)));
3564  // ---------------------------
3565  // fmt::zpad(): pad with zeros
3566  // ---------------------------
3567  CHECK("000063" == cat_sub(buf, fmt::zpad(63, 6)));
3568  CHECK( "00063" == cat_sub(buf, fmt::zpad(63, 5)));
3569  CHECK( "0063" == cat_sub(buf, fmt::zpad(63, 4)));
3570  CHECK( "063" == cat_sub(buf, fmt::zpad(63, 3)));
3571  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 2)));
3572  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 1))); // will never trim the result
3573  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 0))); // will never trim the result
3574  CHECK("0x00003f" == cat_sub(buf, fmt::zpad(fmt::hex(63), 6)));
3575  CHECK("0o000077" == cat_sub(buf, fmt::zpad(fmt::oct(63), 6)));
3576  CHECK("0b00011001" == cat_sub(buf, fmt::zpad(fmt::bin(25), 8)));
3577  // ------------------------------------------------
3578  // fmt::left(): align left with a given field width
3579  // ------------------------------------------------
3580  CHECK("63 " == cat_sub(buf, fmt::left(63, 6)));
3581  CHECK("63 " == cat_sub(buf, fmt::left(63, 5)));
3582  CHECK("63 " == cat_sub(buf, fmt::left(63, 4)));
3583  CHECK("63 " == cat_sub(buf, fmt::left(63, 3)));
3584  CHECK("63" == cat_sub(buf, fmt::left(63, 2)));
3585  CHECK("63" == cat_sub(buf, fmt::left(63, 1))); // will never trim the result
3586  CHECK("63" == cat_sub(buf, fmt::left(63, 0))); // will never trim the result
3587  // the fill character can be specified (defaults to ' '):
3588  CHECK("63----" == cat_sub(buf, fmt::left(63, 6, '-')));
3589  CHECK("63++++" == cat_sub(buf, fmt::left(63, 6, '+')));
3590  CHECK("63////" == cat_sub(buf, fmt::left(63, 6, '/')));
3591  CHECK("630000" == cat_sub(buf, fmt::left(63, 6, '0')));
3592  CHECK("63@@@@" == cat_sub(buf, fmt::left(63, 6, '@')));
3593  CHECK("0x003f " == cat_sub(buf, fmt::left(fmt::zpad(fmt::hex(63), 4), 10)));
3594  // --------------------------------------------------
3595  // fmt::right(): align right with a given field width
3596  // --------------------------------------------------
3597  CHECK(" 63" == cat_sub(buf, fmt::right(63, 6)));
3598  CHECK(" 63" == cat_sub(buf, fmt::right(63, 5)));
3599  CHECK(" 63" == cat_sub(buf, fmt::right(63, 4)));
3600  CHECK(" 63" == cat_sub(buf, fmt::right(63, 3)));
3601  CHECK("63" == cat_sub(buf, fmt::right(63, 2)));
3602  CHECK("63" == cat_sub(buf, fmt::right(63, 1))); // will never trim the result
3603  CHECK("63" == cat_sub(buf, fmt::right(63, 0))); // will never trim the result
3604  // the fill character can be specified (defaults to ' '):
3605  CHECK("----63" == cat_sub(buf, fmt::right(63, 6, '-')));
3606  CHECK("++++63" == cat_sub(buf, fmt::right(63, 6, '+')));
3607  CHECK("////63" == cat_sub(buf, fmt::right(63, 6, '/')));
3608  CHECK("000063" == cat_sub(buf, fmt::right(63, 6, '0')));
3609  CHECK("@@@@63" == cat_sub(buf, fmt::right(63, 6, '@')));
3610  CHECK(" 0x003f" == cat_sub(buf, fmt::right(fmt::zpad(fmt::hex(63), 4), 10)));
3611 
3612  // ------------------------------------------
3613  // fmt::real(): format floating point numbers
3614  // ------------------------------------------
3615  // see also sample_float_precision()
3616  CHECK("0" == cat_sub(buf, fmt::real(0.01f, 0)));
3617  CHECK("0.0" == cat_sub(buf, fmt::real(0.01f, 1)));
3618  CHECK("0.01" == cat_sub(buf, fmt::real(0.01f, 2)));
3619  CHECK("0.010" == cat_sub(buf, fmt::real(0.01f, 3)));
3620  CHECK("0.0100" == cat_sub(buf, fmt::real(0.01f, 4)));
3621  CHECK("0.01000" == cat_sub(buf, fmt::real(0.01f, 5)));
3622  CHECK("1" == cat_sub(buf, fmt::real(1.01f, 0)));
3623  CHECK("1.0" == cat_sub(buf, fmt::real(1.01f, 1)));
3624  CHECK("1.01" == cat_sub(buf, fmt::real(1.01f, 2)));
3625  CHECK("1.010" == cat_sub(buf, fmt::real(1.01f, 3)));
3626  CHECK("1.0100" == cat_sub(buf, fmt::real(1.01f, 4)));
3627  CHECK("1.01000" == cat_sub(buf, fmt::real(1.01f, 5)));
3628  CHECK("1" == cat_sub(buf, fmt::real(1.234234234, 0)));
3629  CHECK("1.2" == cat_sub(buf, fmt::real(1.234234234, 1)));
3630  CHECK("1.23" == cat_sub(buf, fmt::real(1.234234234, 2)));
3631  CHECK("1.234" == cat_sub(buf, fmt::real(1.234234234, 3)));
3632  CHECK("1.2342" == cat_sub(buf, fmt::real(1.234234234, 4)));
3633  CHECK("1.23423" == cat_sub(buf, fmt::real(1.234234234, 5)));
3634  CHECK("1000000.00000" == cat_sub(buf, fmt::real(1000000.000000000, 5)));
3635  CHECK("1234234.23423" == cat_sub(buf, fmt::real(1234234.234234234, 5)));
3636  // AKA %f
3637  CHECK("1000000.00000" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_FLOAT))); // AKA %f, same as above
3638  CHECK("1234234.23423" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_FLOAT))); // AKA %f
3639  CHECK("1234234.2342" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_FLOAT))); // AKA %f
3640  CHECK("1234234.234" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_FLOAT))); // AKA %f
3641  CHECK("1234234.23" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_FLOAT))); // AKA %f
3642  // AKA %e
3643  CHECK("1.00000e+06" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_SCIENT))); // AKA %e
3644  CHECK("1.23423e+06" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_SCIENT))); // AKA %e
3645  CHECK("1.2342e+06" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_SCIENT))); // AKA %e
3646  CHECK("1.234e+06" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_SCIENT))); // AKA %e
3647  CHECK("1.23e+06" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_SCIENT))); // AKA %e
3648  // AKA %g
3649  CHECK("1e+06" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_FLEX))); // AKA %g
3650  CHECK("1.2342e+06" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_FLEX))); // AKA %g
3651  CHECK("1.234e+06" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_FLEX))); // AKA %g
3652  CHECK("1.23e+06" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_FLEX))); // AKA %g
3653  CHECK("1.2e+06" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_FLEX))); // AKA %g
3654  // FTOA_HEXA: AKA %a (hexadecimal formatting of floats)
3655  CHECK("0x1.e8480p+19" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_HEXA))); // AKA %a
3656  CHECK("0x1.2d53ap+20" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_HEXA))); // AKA %a
3657 
3658  // --------------------------------------------------------------
3659  // fmt::raw(): dump data in raw (binary) machine format (respecting alignment)
3660  // --------------------------------------------------------------
3661  {
3662  C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wcast-align") // we're casting the values directly, so alignment is strictly respected.
3663  const uint32_t payload[] = {10, 20, 30, 40, UINT32_C(0xdeadbeef)};
3664  // (package payload as a substr, for comparison only)
3665  csubstr expected = csubstr((const char *)payload, sizeof(payload));
3666  csubstr actual = cat_sub(buf, fmt::raw(payload));
3667  CHECK(!actual.overlaps(expected));
3668  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3669  // also possible with variables:
3670  for(const uint32_t value : payload)
3671  {
3672  // (package payload as a substr, for comparison only)
3673  expected = csubstr((const char *)&value, sizeof(value));
3674  actual = cat_sub(buf, fmt::raw(value));
3675  CHECK(actual.size() == sizeof(uint32_t));
3676  CHECK(!actual.overlaps(expected));
3677  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3678  // with non-const data, fmt::craw() may be needed for disambiguation:
3679  actual = cat_sub(buf, fmt::craw(value));
3680  CHECK(actual.size() == sizeof(uint32_t));
3681  CHECK(!actual.overlaps(expected));
3682  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3683  //
3684  // read back:
3685  uint32_t result = 0;
3686  auto reader = fmt::raw(result);
3687  CHECK(uncat(actual, reader));
3688  // and compare:
3689  // (vs2017/release/32bit does not reload result from cache, so force it)
3690  CHECK(result == value); // roundtrip completed successfully
3691  }
3692  C4_SUPPRESS_WARNING_GCC_CLANG_POP
3693  }
3694 
3695  // -------------------------
3696  // fmt::base64(): see below!
3697  // -------------------------
3698  }
3699 }
3700 
3701 
3702 //-----------------------------------------------------------------------------
3703 
3704 /** demonstrates how to read and write base64-encoded blobs.
3705  * @see @ref doc_base64 */
3707 {
3708  // let's start by creating a tree with base64 vals and keys
3709  ryml::Tree tree;
3710  tree.rootref().set_map();
3711  struct text_and_base64 { ryml::csubstr text, base64; };
3712  text_and_base64 cases[] = {
3713  {{"Hello, World!"}, {"SGVsbG8sIFdvcmxkIQ=="}},
3714  {{"Brevity is the soul of wit."}, {"QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu"}},
3715  {{"All that glitters is not gold."}, {"QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu"}},
3716  };
3717  // to encode base64 and write the result to val:
3718  for(text_and_base64 c : cases)
3719  tree[c.text].set_serialized(ryml::fmt::base64(c.text));
3720  // to encode base64 and write the result to key:
3721  for(text_and_base64 c : cases)
3722  {
3723  ryml::NodeRef ch = tree.rootref().append_child();
3725  ch.set_serialized(c.text);
3726  }
3727  // check the result:
3728  for(text_and_base64 c : cases)
3729  {
3730  CHECK(tree[c.text].val() == c.base64);
3731  CHECK(tree[c.base64].val() == c.text);
3732  }
3733  // and this is how the YAML now looks:
3734  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
3735  "Hello, World!: SGVsbG8sIFdvcmxkIQ==" "\n"
3736  "Brevity is the soul of wit.: QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu" "\n"
3737  "All that glitters is not gold.: QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu" "\n"
3738  // note that the keys below are base64-encoded
3739  "SGVsbG8sIFdvcmxkIQ==: Hello, World!" "\n"
3740  "QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu: Brevity is the soul of wit." "\n"
3741  "QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu: All that glitters is not gold." "\n"
3742  "");
3743  char buf1_[128], buf2_[128];
3744  ryml::substr buf1 = buf1_; // this is where we will write the result (using load())
3745  ryml::substr buf2 = buf2_; // this is where we will write the result (using deserialize()/deserialize_key())
3746  // to decode base64 and write the result to buf:
3747  for(const text_and_base64 c : cases)
3748  {
3749  // this decodes base64 and write the decoded result into an
3750  // existing buffer (buf1):
3751  size_t len = 0; // the decoded length
3752  tree[c.text].load(ryml::fmt::base64(buf1, &len));
3753  // The base64() tag function is used to get the
3754  // deserialization using c4::decode_base64(). This will
3755  // respect the limits of the buffer, and fail with an error if
3756  // the buffer is too small (or if the base64 encoding is
3757  // wrong). The optional second parameter is set to the decoded
3758  // size, ie, the length of the decoded result, which is also
3759  // the size required for the buffer.
3760  CHECK(len <= buf1.len);
3761  CHECK(c.text.len == len);
3762  CHECK(buf1.first(len) == c.text);
3763  // likewise for keys:
3764  tree[c.base64].load_key(ryml::fmt::base64(buf2, &len));
3765  CHECK(len <= buf2.len);
3766  CHECK(buf2.first(len) == c.text);
3767  //
3768  // interop with std::string:
3769  std::string result;
3770  tree[c.text].load(ryml::fmt::base64(result));
3771  CHECK(result == c.text);
3772  // likewise for keys:
3773  tree[c.base64].load_key(ryml::fmt::base64(result));
3774  CHECK(result == c.text);
3775  //
3776  // Manual interop with std::string: using substr.
3777  // This shows how to manually resize the destination
3778  // buffer, and is similar to the implementation for containers.
3779  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3780  len = 0;
3781  // try to read into the buffer, and get back the required size
3782  // (in len)
3783  auto payload = ryml::fmt::base64(ryml::to_substr(result), &len);
3784  bool ok = tree[c.text].deserialize(payload);
3785  if(len > result.size()) // the size was not enough; resize and call again
3786  {
3787  CHECK(!ok);
3788  result.resize(len);
3789  payload = ryml::fmt::base64(ryml::to_substr(result), &len); // reassign
3790  ok = tree[c.text].deserialize(payload);
3791  }
3792  CHECK(ok);
3793  result.resize(len); // trim to the length of the decoded buffer
3794  CHECK(result == c.text);
3795  // likewise for keys
3796  }
3797  //
3798  // ryml base64() serialization uses native endianness. If you're
3799  // encoding types whose size > 1 byte, the results will vary
3800  // according to endianess. Let's use a helper here to work around
3801  // that (in practice, you should use something like htons() before
3802  // encoding):
3803  union { uint32_t u; char c[sizeof(uint32_t)]; } endianess_test = {1};
3804  const bool is_little_endian = endianess_test.c[0] == 1; // NOLINT
3805  auto endian_select = [is_little_endian](ryml::csubstr little_endian, ryml::csubstr big_endian){
3806  return is_little_endian ? little_endian : big_endian;
3807  };
3808  //
3809  // directly encode variables: integers
3810  {
3811  const uint64_t valin = UINT64_C(0xdeadbeef);
3812  ryml::NodeRef node = tree["deadbeef"];
3814  CHECK(node.val() == endian_select("776t3gAAAAA=", "AAAAAN6tvu8="));
3815  uint64_t valout = 0;
3816  size_t len = 0;
3817  node.load(ryml::fmt::base64(valout, &len));
3818  CHECK(len == sizeof(valout));
3819  CHECK(valout == UINT64_C(0xdeadbeef)); // base64 roundtrip is bit-accurate
3820  // also works without length parameter:
3821  valout = {};
3822  node.load(ryml::fmt::base64(valout));
3823  CHECK(valout == UINT64_C(0xdeadbeef)); // base64 roundtrip is bit-accurate
3824  }
3825  // directly encode variables: floating point
3826  {
3827  const double valin = 123456.7891011;
3828  ryml::NodeRef node = tree["float"];
3830  CHECK(node.val() == endian_select("nHkooAwk/kA=", "QP4kDKAoeZw="));
3831  double valout = 0;
3832  size_t len = 0;
3833  node.load(ryml::fmt::base64(valout, &len));
3834  CHECK(len == sizeof(valout));
3835  CHECK(memcmp(&valout, &valin, sizeof(valout)) == 0); // base64 roundtrip is bit-accurate // NOLINT
3836  // also works without length parameter:
3837  valout = {};
3838  node.load(ryml::fmt::base64(valout));
3839  CHECK(memcmp(&valout, &valin, sizeof(valout)) == 0); // base64 roundtrip is bit-accurate // NOLINT
3840  }
3841  // directly encode memory ranges
3842  {
3843  const uint32_t data_in[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xdeadbeef};
3844  uint32_t data_out[11] = {};
3845  ryml::NodeRef node = tree["int_data"];
3846  node.set_serialized(ryml::fmt::base64(data_in, C4_COUNTOF(data_in)), ryml::VAL_PLAIN);
3847  CHECK(node.val() ==
3848  endian_select("AAAAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAO++rd4=",
3849  "AAAAAAAAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACd6tvu8="));
3850  CHECK(memcmp(data_in, data_out, sizeof(data_in)) != 0); // before the roundtrip
3851  size_t len = 0;
3852  node.load(ryml::fmt::base64(data_out, C4_COUNTOF(data_in), &len));
3853  CHECK(len == sizeof(data_out));
3854  CHECK(memcmp(data_in, data_out, sizeof(data_in)) == 0); // after the roundtrip
3855  // also works without length parameter (because data_out is an
3856  // array and not a pointer):
3857  memset(data_out, 0, sizeof(data_out));
3858  node.load(ryml::fmt::base64(data_out));
3859  CHECK(memcmp(data_in, data_out, sizeof(data_in)) == 0); // after the roundtrip
3860  }
3861 }
3862 
3863 
3864 //-----------------------------------------------------------------------------
3865 // Serialization info
3866 
3867 /** This sample shows the main user-facing calls triggering
3868  * (de)serialization. ryml provides built-ins for all fundamental
3869  * types. For samples on how to implement other types such as STL
3870  * containers or user types, see the samples below.
3871  *
3872  * Read also the [doxygen intro to using serialization](https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__using.html)
3873  */
3875 {
3876  ryml::csubstr yaml = "{0: 0, 10: 10, foo: foo}";
3877  ryml::Tree tree = ryml::parse_in_arena(yaml);
3878  //
3879  //
3880  // Deserialization is done with .load() and .load_key()
3881  { int val = 1; tree[0].load(&val); CHECK(val == 0); }
3882  { unsigned val = 0; tree[1].load(&val); CHECK(val == 10); }
3883  { int key = 1; tree[0].load_key(&key); CHECK(key == 0); }
3884  { unsigned key = 0; tree[1].load_key(&key); CHECK(key == 10); }
3885  // also available in the tree:
3886  { int val = 1; tree.load(tree[0].id(), &val); CHECK(val == 0); }
3887  { unsigned val = 0; tree.load(tree[1].id(), &val); CHECK(val == 10); }
3888  { int key = 1; tree.load_key(tree[0].id(), &key); CHECK(key == 0); }
3889  { unsigned key = 0; tree.load_key(tree[1].id(), &key); CHECK(key == 10); }
3890  // .load() calls the (non returning) error callback when the
3891  // serialization fails. If you want to avoid the exceptional flow,
3892  // you can use .deserialize() / .deserialize_key(), and do not forget to
3893  // check its return status (marked as `[[nodiscard]]`):
3894  { int val = 1; CHECK(tree[0].deserialize(&val)); CHECK(val == 0); }
3895  { unsigned val = 0; CHECK(tree[1].deserialize(&val)); CHECK(val == 10); }
3896  { int key = 1; CHECK(tree[0].deserialize_key(&key)); CHECK(key == 0); }
3897  { unsigned key = 0; CHECK(tree[1].deserialize_key(&key)); CHECK(key == 10); }
3898  // also available in the tree:
3899  { int val = 1; CHECK(tree.deserialize(tree[0].id(), &val)); CHECK(val == 0); }
3900  { unsigned val = 0; CHECK(tree.deserialize(tree[1].id(), &val)); CHECK(val == 10); }
3901  { int key = 1; CHECK(tree.deserialize_key(tree[0].id(), &key)); CHECK(key == 0); }
3902  { unsigned key = 0; CHECK(tree.deserialize_key(tree[1].id(), &key)); CHECK(key == 10); }
3903  //
3904  //
3905  // Serialization is done with .save() and .save_key(). It is
3906  // carried out by converting the value to string in the tree's
3907  // arena (see sample_tree_arena()).
3908  { int val = 10; tree[0].save(val); CHECK(tree[0].val() == "10"); }
3909  { unsigned val = 11; tree[1].save(val); CHECK(tree[1].val() == "11"); }
3910  { int key = 12; tree[0].save_key(key); CHECK(tree[0].key() == "12"); }
3911  { unsigned key = 13; tree[1].save_key(key); CHECK(tree[1].key() == "13"); }
3912  // also available in the tree:
3913  { int val = 20; tree.save(tree[0].id(), val); CHECK(tree[0].val() == "20"); }
3914  { unsigned val = 21; tree.save(tree[1].id(), val); CHECK(tree[1].val() == "21"); }
3915  { int key = 22; tree.save_key(tree[0].id(), key); CHECK(tree[0].key() == "22"); }
3916  { unsigned key = 23; tree.save_key(tree[1].id(), key); CHECK(tree[1].key() == "23"); }
3917  //
3918  //
3919  // Like .load(), .save() checks the node for write-ability and
3920  // triggers an error if it failed. Likewise, to avoid the
3921  // exceptional path, you can use .set_serialized() and
3922  // .set_key_serialized():
3923  { int val = 14; tree[0].set_serialized(val); CHECK(tree[0].val() == "14"); }
3924  { unsigned val = 15; tree[1].set_serialized(val); CHECK(tree[1].val() == "15"); }
3925  { int key = 16; tree[0].set_key_serialized(key); CHECK(tree[0].key() == "16"); }
3926  { unsigned key = 17; tree[1].set_key_serialized(key); CHECK(tree[1].key() == "17"); }
3927  /// And likewise, you can use these from the tree as well:
3928  /// @ref c4::yml::Tree::set_serialized() / @ref c4::yml::Tree::set_key_serialized().
3929  { int val = 18; tree.set_serialized(tree[0].id(), val); CHECK(tree[0].val() == "18"); }
3930  { unsigned val = 19; tree.set_serialized(tree[1].id(), val); CHECK(tree[1].val() == "19"); }
3931  { int key = 20; tree.set_key_serialized(tree[0].id(), key); CHECK(tree[0].key() == "20"); }
3932  { unsigned key = 21; tree.set_key_serialized(tree[1].id(), key); CHECK(tree[1].key() == "21"); }
3933  //
3934  //
3935  // You can also (de)serialize tags and even anchors, if your
3936  // application requires it. For serialization the trick is to use
3937  // .to_arena():
3938  tree[0].set_val_tag(tree[0].to_arena(42)); CHECK(tree[0].val_tag() == "42");
3939  tree[1].set_key_tag(tree[1].to_arena(43)); CHECK(tree[1].key_tag() == "43");
3940  tree[0].set_val_anchor(tree[0].to_arena(44)); CHECK(tree[0].val_anchor() == "44");
3941  tree[1].set_key_anchor(tree[1].to_arena(45)); CHECK(tree[1].key_anchor() == "45");
3942  /// For deserialization, use from_chars().
3943  { int val = 0; CHECK(from_chars(tree[0].val_tag(), &val)); CHECK(val == 42); }
3944  { unsigned key = 0; CHECK(from_chars(tree[1].key_tag(), &key)); CHECK(key == 43); }
3945  { int val = 0; CHECK(from_chars(tree[0].val_anchor(), &val)); CHECK(val == 44); }
3946  { unsigned key = 0; CHECK(from_chars(tree[1].key_anchor(), &key)); CHECK(key == 45); }
3947 }
3948 
3949 
3950 //-----------------------------------------------------------------------------
3951 // ryml uses Argument Dependent Lookup to dispatch the serialization
3952 // to each type. This enables the user to implement serialization for
3953 // custom types (and also enables rapidyaml to implement serialization
3954 // of fundamental types).
3955 //
3956 // user scalar types: where nothing in the tree needs to be set.
3957 // implemented in ryml through to_chars() + from_chars()
3958 
3959 /** @addtogroup doc_quickstart_helpers
3960  * @{ */
3961 
3962 /** @defgroup doc_sample_scalar_types Serialize/deserialize scalar types
3963  * @{ */
3964 
3965 // IMPORTANT: read the doxygen documentation for scalar serialization at:
3966 // https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__user__types.html
3967 
3968 template<class T> struct vec2 { T x, y; }; ///< example scalar type, serialized and deserialized
3969 template<class T> struct vec3 { T x, y, z; }; ///< example scalar type, serialized and deserialized
3970 template<class T> struct vec4 { T x, y, z, w; }; ///< example scalar type, serialized and deserialized
3971 
3972 template<class T> struct parse_only_vec2 { T x, y; }; ///< example scalar type, deserialized only
3973 template<class T> struct parse_only_vec3 { T x, y, z; }; ///< example scalar type, deserialized only
3974 template<class T> struct parse_only_vec4 { T x, y, z, w; }; ///< example scalar type, deserialized only
3975 
3976 template<class T> struct emit_only_vec2 { T x, y; }; ///< example scalar type, serialized only
3977 template<class T> struct emit_only_vec3 { T x, y, z; }; ///< example scalar type, serialized only
3978 template<class T> struct emit_only_vec4 { T x, y, z, w; }; ///< example scalar type, serialized only
3979 
3980 
3981 // to serialize scalars, you need to define to_chars():
3982 
3983 template<class T> size_t to_chars(ryml::substr buf, vec2<T> v) { return ryml::format(buf, "({},{})", v.x, v.y); }
3984 template<class T> size_t to_chars(ryml::substr buf, vec3<T> v) { return ryml::format(buf, "({},{},{})", v.x, v.y, v.z); }
3985 template<class T> size_t to_chars(ryml::substr buf, vec4<T> v) { return ryml::format(buf, "({},{},{},{})", v.x, v.y, v.z, v.w); }
3986 
3987 template<class T> size_t to_chars(ryml::substr buf, emit_only_vec2<T> v) { return ryml::format(buf, "({},{})", v.x, v.y); }
3988 template<class T> size_t to_chars(ryml::substr buf, emit_only_vec3<T> v) { return ryml::format(buf, "({},{},{})", v.x, v.y, v.z); }
3989 template<class T> size_t to_chars(ryml::substr buf, emit_only_vec4<T> v) { return ryml::format(buf, "({},{},{},{})", v.x, v.y, v.z, v.w); }
3990 
3991 
3992 // to deserialize scalars, you need to define from_chars():
3993 
3994 template<class T> bool from_chars(ryml::csubstr buf, vec2<T> *v) { size_t ret = ryml::unformat(buf, "({},{})", v->x, v->y); return ret != ryml::yml::npos; }
3995 template<class T> bool from_chars(ryml::csubstr buf, vec3<T> *v) { size_t ret = ryml::unformat(buf, "({},{},{})", v->x, v->y, v->z); return ret != ryml::yml::npos; }
3996 template<class T> bool from_chars(ryml::csubstr buf, vec4<T> *v) { size_t ret = ryml::unformat(buf, "({},{},{},{})", v->x, v->y, v->z, v->w); return ret != ryml::yml::npos; }
3997 
3998 template<class T> bool from_chars(ryml::csubstr buf, parse_only_vec2<T> *v) { size_t ret = ryml::unformat(buf, "({},{})", v->x, v->y); return ret != ryml::yml::npos; }
3999 template<class T> bool from_chars(ryml::csubstr buf, parse_only_vec3<T> *v) { size_t ret = ryml::unformat(buf, "({},{},{})", v->x, v->y, v->z); return ret != ryml::yml::npos; }
4000 template<class T> bool from_chars(ryml::csubstr buf, parse_only_vec4<T> *v) { size_t ret = ryml::unformat(buf, "({},{},{},{})", v->x, v->y, v->z, v->w); return ret != ryml::yml::npos; }
4001 
4002 
4003 // IMPORTANT: read the doxygen documentation for scalar serialization at:
4004 // https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__user__types.html
4005 
4006 /** @} */ // doc_sample_scalar_types
4007 /** @} */ // doc_quickstart_helpers
4008 
4009 
4010 /** to add scalar types (ie leaf types converting to/from string),
4011  * define the functions above for those types.
4012  *
4013  * @note read the doxygen documentation for scalar types at:
4014  * https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__user__types.html
4015  *
4016  * See @ref
4017  * doc_sample_scalar_types. */
4019 {
4020  ryml::Tree t;
4021  ryml::NodeRef root = t;
4022  root.set_map();
4023 
4024  vec2<int> v2in{10, 11};
4025  vec2<int> v2out{1, 2};
4026  root["v2"].save(v2in); // serializes to the tree's arena, and then sets the val
4027  root["v2"].load(&v2out);
4028  CHECK(v2in.x == v2out.x);
4029  CHECK(v2in.y == v2out.y);
4030  vec3<int> v3in{100, 101, 102};
4031  vec3<int> v3out{1, 2, 3};
4032  root["v3"].save(v3in); // serializes to the tree's arena, and then sets the val
4033  root["v3"].load(&v3out);
4034  CHECK(v3in.x == v3out.x);
4035  CHECK(v3in.y == v3out.y);
4036  CHECK(v3in.z == v3out.z);
4037  vec4<int> v4in{1000, 1001, 1002, 1003};
4038  vec4<int> v4out{1, 2, 3, 4};
4039  root["v4"].save(v4in); // serializes to the tree's arena, and then sets the val
4040  root["v4"].load(&v4out);
4041  CHECK(v4in.x == v4out.x);
4042  CHECK(v4in.y == v4out.y);
4043  CHECK(v4in.z == v4out.z);
4044  CHECK(v4in.w == v4out.w);
4045  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
4046  "v2: (10,11)" "\n"
4047  "v3: (100,101,102)" "\n"
4048  "v4: (1000,1001,1002,1003)" "\n"
4049  "");
4050 
4051  // note that only the used functions are needed:
4052  // - if a type is only parsed, then only from_chars() is needed
4053  // - if a type is only emitted, then only to_chars() is needed
4054  emit_only_vec2<int> eov2in{20, 21}; // only has to_chars()
4055  parse_only_vec2<int> pov2out{1, 2}; // only has from_chars()
4056  root["v2"].save(eov2in); // serializes to the tree's arena, and then sets the keyval
4057  root["v2"].load(&pov2out);
4058  CHECK(eov2in.x == pov2out.x);
4059  CHECK(eov2in.y == pov2out.y);
4060  emit_only_vec3<int> eov3in{30, 31, 32}; // only has to_chars()
4061  parse_only_vec3<int> pov3out{1, 2, 3}; // only has from_chars()
4062  root["v3"].save(eov3in); // serializes to the tree's arena, and then sets the keyval
4063  root["v3"].load(&pov3out);
4064  CHECK(eov3in.x == pov3out.x);
4065  CHECK(eov3in.y == pov3out.y);
4066  CHECK(eov3in.z == pov3out.z);
4067  emit_only_vec4<int> eov4in{40, 41, 42, 43}; // only has to_chars()
4068  parse_only_vec4<int> pov4out{1, 2, 3, 4}; // only has from_chars()
4069  root["v4"].save(eov4in); // serializes to the tree's arena, and then sets the keyval
4070  root["v4"].load(&pov4out);
4071  CHECK(eov4in.x == pov4out.x);
4072  CHECK(eov4in.y == pov4out.y);
4073  CHECK(eov4in.z == pov4out.z);
4074  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
4075  "v2: (20,21)" "\n"
4076  "v3: (30,31,32)" "\n"
4077  "v4: (40,41,42,43)" "\n"
4078  "");
4079 }
4080 
4081 
4082 //-----------------------------------------------------------------------------
4083 // ryml uses C++'s Argument Dependent Lookup to dispatch the serialization
4084 // to each type. This enables the user to implement serialization for
4085 // custom types (and also enables rapidyaml to implement serialization
4086 // of fundamental types).
4087 
4088 /** @addtogroup doc_quickstart_helpers
4089  * @{ */
4090 
4091 /** @defgroup doc_sample_container_types_brief Serialize container types (brief)
4092  *
4093  * Serialization definitions used in @ref sample_user_container_types_brief()
4094  *
4095  * @{ */
4096 
4097 // let's start with a brief, minimal example, and expand below.
4098 
4099 // first define an example nested user type:
4100 struct Inner { int foo, bar; }; /// serializes as a map
4101 struct Outer { Inner inner[2]; }; /// serializes as a seq
4102 
4103 // now define the write functions:
4104 void write(ryml::NodeRef &n, Inner const& inner)
4105 {
4107  n["foo"].set_serialized(inner.foo);
4108  n["bar"].set_serialized(inner.bar);
4109 }
4110 void write(ryml::NodeRef &n, Outer const& outer)
4111 {
4112  n.set_seq(ryml::BLOCK);
4113  n[0].set_serialized(outer.inner[0]);
4114  n[1].set_serialized(outer.inner[1]);
4115 }
4116 
4117 // now define the read functions:
4119 {
4120  ryml::ReadResult r(n.is_map(), n.id());
4121  if(r) r = n.deserialize_child("foo", &inner->foo); // O(N)
4122  if(r) r = n.deserialize_child("bar", &inner->bar);
4123  return r;
4124 }
4126 {
4127  ryml::ReadResult r(n.is_seq(), n.id());
4128  // size is fixed, so we do this with explicit indices.
4129  if(r) r = n.deserialize_child(0, &val->inner[0]); // O(N)
4130  if(r) r = n.deserialize_child(1, &val->inner[1]);
4131  return r;
4132 }
4133 
4134 /** @} */
4135 
4136 /** @} */
4137 
4138 /** shows a minimal example of user container types, defined in @ref doc_sample_container_types_brief
4139  *
4140  * @note read the doxygen documentation for containers/general types at:
4141  * https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__user__types.html
4142  */
4144 {
4145  ryml::Tree tree;
4146  ryml::NodeRef root = tree;
4147  Outer outer{{{0,1}, {2,3}}};
4148  Outer roundtrip;
4149  // let's do a serialization roundtrip.
4150  // first serialize:
4151  root.save(outer);
4152  // see how the YAML looks:
4153  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
4154  "- {foo: 0,bar: 1}\n"
4155  "- {foo: 2,bar: 3}\n");
4156  // now complete the roundtrip by deserializing:
4157  root.load(&roundtrip);
4158  // finally, verify that the result is equal:
4159  CHECK(0 == memcmp(&outer, &roundtrip, sizeof(Outer)));
4160 }
4161 
4162 
4163 //-----------------------------------------------------------------------------
4164 // let's now elaborate further
4165 
4166 /** @addtogroup doc_quickstart_helpers
4167  * @{ */
4168 
4169 /** @defgroup doc_sample_container_types Serialize container types
4170  *
4171  * Serialization definitions used in @ref sample_user_container_types()
4172  *
4173  * @{ */
4174 
4175 /** example user container type: seq-like */
4176 template<class T>
4178 {
4179  std::vector<T> seq_member;
4180  void check_eq(my_seq_type const& that) const
4181  {
4182  CHECK(seq_member.size() == that.seq_member.size());
4183  if(seq_member.size() == that.seq_member.size())
4184  for(size_t i = 0; i < seq_member.size(); ++i)
4185  CHECK(seq_member[i] == that.seq_member[i]);
4186  }
4187 };
4188 /** example user container type: map-like */
4189 template<class K, class V>
4191 {
4192  std::map<K, V> map_member;
4193  void check_eq(my_map_type const& that) const
4194  {
4195  CHECK(map_member.size() == that.map_member.size());
4196  if(map_member.size() == that.map_member.size())
4197  {
4198  for(auto const& itthat : that.map_member)
4199  {
4200  auto it = map_member.find(itthat.first);
4201  CHECK(it != map_member.end());
4202  if(it != map_member.end())
4203  CHECK(it->second == itthat.second);
4204  }
4205  }
4206  }
4207 };
4208 /** example user container type with nested user types.
4209  * notice all the members have user-defined serialization methods. */
4210 struct my_type
4211 {
4212  // these are serialized as scalar (leaf) nodes:
4216  // these are serialized as container nodes:
4219  void check_eq(my_type const& that) const
4220  {
4221  CHECK(v2.x == that.v2.x);
4222  CHECK(v2.y == that.v2.y);
4223  CHECK(v3.x == that.v3.x);
4224  CHECK(v3.y == that.v3.y);
4225  CHECK(v3.z == that.v3.z);
4226  CHECK(v4.x == that.v4.x);
4227  CHECK(v4.y == that.v4.y);
4228  CHECK(v4.z == that.v4.z);
4229  CHECK(v4.w == that.v4.w);
4230  seq.check_eq(that.seq);
4231  map.check_eq(that.map);
4232  }
4233 };
4234 
4235 
4236 // IMPORTANT: read the doxygen documentation for deserialization of user types:
4237 // https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__user__types.html
4238 
4239 
4240 // Let's first show examples of serialization (writing) functions:
4241 // writing is easier because it does not have to check the data.
4242 
4243 // here's an example of serializing a seq type
4244 template<class T>
4245 void write(ryml::Tree *tree, ryml::id_type id, my_seq_type<T> const& seq)
4246 {
4247  tree->set_seq(id);
4248  for(T const& v : seq.seq_member)
4249  {
4250  // inside write(), prefer using .set_serialized() instead of .save()
4251  tree->set_serialized(tree->append_child(id), v);
4252  }
4253 }
4254 // special optimization: if all you have is strings, AND you are sure
4255 // they outlive the tree, you can avoid the copy to the tree's
4256 // arena. But beware the lifetime issue!
4258 {
4259  tree->set_seq(id);
4260  for(std::string const& v : seq.seq_member)
4261  {
4262  // now the tree is pointing at seq's strings. using .set_val()
4263  // does not serialize, and this avoids the string copy to the
4264  // tree's arena
4265  tree->set_val(tree->append_child(id), ryml::to_csubstr(v));
4266  }
4267 }
4268 
4269 // example of map serialization:
4270 template<class K, class V>
4272 {
4273  tree->set_map(id);
4274  for(auto const& v : map.map_member)
4275  {
4276  // inside write(), prefer using .set_serialized() instead of .save()
4277  ryml::id_type child_id = tree->append_child(id);
4278  tree->set_key_serialized(child_id, v.first); // we're serializing the key!
4279  tree->set_serialized(child_id, v.second);
4280  }
4281 }
4282 // another example of map serialization:
4283 void write(ryml::Tree *tree, ryml::id_type id, my_type const& val)
4284 {
4285  tree->set_map(id);
4286  // inside write(), prefer using .set_serialized() instead of .save()
4287  //
4288  ryml::id_type ch;
4289  // these are leaf nodes:
4290  ch = tree->append_child(id); tree->set_key(ch, "v2"); tree->set_serialized(ch, val.v2);
4291  ch = tree->append_child(id); tree->set_key(ch, "v3"); tree->set_serialized(ch, val.v3);
4292  ch = tree->append_child(id); tree->set_key(ch, "v4"); tree->set_serialized(ch, val.v4);
4293  // these are container nodes (note how the call is equal):
4294  ch = tree->append_child(id); tree->set_key(ch, "seq"); tree->set_serialized(ch, val.seq);
4295  ch = tree->append_child(id); tree->set_key(ch, "map"); tree->set_serialized(ch, val.map);
4296  // Note above that we're NOT serializing the keys. That works and
4297  // is correct here because the keys themselves are fixed, and are
4298  // static strings located in the executable. But if the keys came
4299  // from the data, they too would have to be serialized with
4300  // .set_key_serialized().
4301 }
4302 
4303 
4304 // Now let's implement the read() functions. Here we have more work to
4305 // do because in general we need to check the sanity of the data
4306 // coming from YAML.
4307 //
4308 // IMPORTANT: read the doxygen documentation for deserialization of user types:
4309 // https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__user__types.html
4310 
4311 
4312 template<class T>
4314 {
4315  if(!tree->is_seq(id)) return ryml::ReadResult(id); // id must be a seq
4316  seq->seq_member.clear(); // we'll overwrite the vector
4317  for(ryml::id_type child = tree->first_child(id);
4318  child != ryml::NONE;
4319  child = tree->next_sibling(child))
4320  {
4321  // create a new entry
4322  seq->seq_member.emplace_back();
4323  // inside read() you SHOULD NOT use .load() because of its
4324  // exceptional flow. Instead, you should use .deserialize() to
4325  // play nice with .deserialize() callers calling this function.
4326  // Read more at the doxygen page linked above.
4327  ryml::ReadResult r = tree->deserialize(child, &seq->seq_member.back());
4328  if(!r) return r; // return the inner-most result
4329  }
4330  return ryml::ReadResult(); // all good
4331 }
4332 
4333 template<class K, class V>
4335 {
4336  if(!tree->is_map(id)) return ryml::ReadResult(id); // id must be a seq
4337  for(ryml::id_type child = tree->first_child(id);
4338  child != ryml::NONE;
4339  child = tree->next_sibling(child))
4340  {
4341  K k{};
4342  // again, we're using .deserialize() instead of .load()
4343  // because (1) we should gracefully return a ReadResult and
4344  // anyway (2) we're certain the node is readable (because
4345  // we're inside the loop).
4346  ryml::ReadResult r = tree->deserialize_key(child, &k);
4347  if(r) r = tree->deserialize(child, &map->map_member[std::move(k)]);
4348  if(!r) return r; // return the inner-most result
4349  }
4350  return ryml::ReadResult(); // all good.
4351 }
4352 
4353 
4354 //
4355 // Given that ryml provides both a tree and a node API, you can
4356 // implement a tree read() (as above), or a node read(). The latter
4357 // will only be called if you trigger deserialization from a
4358 // node. That's ok here, because that's what we're doing below.
4359 //
4360 // Regardless, it needs to gracefully handle bad YAML data, so we must
4361 // check every step. We use .deserialize_child() which will look for a
4362 // child by key, and deserialize that child, returning the deserialize
4363 // result. If no such child exists, it returns a read result reporting
4364 // the node (not the child). .deserialize_child() also works with
4365 // indices, simplifying the implementation of fixed-size seqs.
4366 //
4367 // Note also how each step is predicated on the latest result. This
4368 // will ensure that the first failing step is reported.
4370 {
4371  ryml::ReadResult r(n.is_map(), n.id()); // node must be a map
4372  if(r) r = n.deserialize_child("v2", &val->v2);
4373  if(r) r = n.deserialize_child("v3", &val->v3);
4374  if(r) r = n.deserialize_child("v4", &val->v4);
4375  if(r) r = n.deserialize_child("seq", &val->seq);
4376  if(r) r = n.deserialize_child("map", &val->map);
4377  // hint: you can also add a default argument for when no such child exists
4378  // hint: you can also use indices instead of keys
4379  return r;
4380 }
4381 // But if you're going to ever deserialize from a tree+id, then, you
4382 // must implement a tree+id read(). Also, if you only implement the
4383 // tree+id read(), it will get picked both from tree calls or from
4384 // node calls when no node read() exists. That is the reason why you
4385 // should prefer to implement the tree version. As an example, see how
4386 // similar the tree version is:
4388 {
4389  ryml::ReadResult r(tree->is_map(id), id); // node must be a map
4390  if(r) r = tree->deserialize_child(id, "v2", &val->v2);
4391  if(r) r = tree->deserialize_child(id, "v3", &val->v3);
4392  if(r) r = tree->deserialize_child(id, "v4", &val->v4);
4393  if(r) r = tree->deserialize_child(id, "seq", &val->seq);
4394  if(r) r = tree->deserialize_child(id, "map", &val->map);
4395  // hint: you can also add a default argument for when no such child exists
4396  // hint: you can also use indices instead of keys
4397  return r;
4398 }
4399 
4400 /** @} */ // doc_sample_container_types
4401 
4402 /** @} */ // quickstart_helpers
4403 
4404 
4405 /** shows how to serialize/deserialize container types (defined in @ref doc_sample_container_types).
4406  *
4407  * @note read the doxygen documentation for containers/general types at:
4408  * https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__user__types.html
4409  *
4410  * @see doc_sample_container_types
4411  * @see sample_std_types
4412  * @see sample_deserialize_error
4413  * */
4415 {
4416  // let's do a YAML roundtrip:
4417  ryml::Tree tree;
4418  ryml::NodeRef root_node = tree;
4419  ryml::id_type root_id = tree.root_id();
4420 
4421  // here we will be doing a serialization roundtrip with a
4422  // user-defined container type.
4423  //
4424  // the read() and write() functions for this type are defined
4425  // above.
4426  const my_type orig{
4427  {20, 21},
4428  {30, 31, 32},
4429  {40, 41, 42, 43},
4430  {{101, 102, 103, 104, 105, 106, 107}},
4431  {{{1001, 2001}, {1002, 2002}, {1003, 2003}}},
4432  };
4433 
4434  // serialize to the tree:
4435  root_node.save(orig);
4436  // check the YAML:
4437  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
4438  "v2: (20,21)" "\n"
4439  "v3: (30,31,32)" "\n"
4440  "v4: (40,41,42,43)" "\n"
4441  "seq:" "\n"
4442  " - 101" "\n"
4443  " - 102" "\n"
4444  " - 103" "\n"
4445  " - 104" "\n"
4446  " - 105" "\n"
4447  " - 106" "\n"
4448  " - 107" "\n"
4449  "map:" "\n"
4450  " 1001: 2001" "\n"
4451  " 1002: 2002" "\n"
4452  " 1003: 2003" "\n"
4453  "");
4454  // and now let's deserialize to this variable.
4455  // first from a node:
4456  {
4457  my_type roundtrip;
4458  root_node.load(&roundtrip); // picks the node read(), because we wrote one.
4459  // if we didn't, it would then pick the tree read()
4460  roundtrip.check_eq(orig); // finally, let's compare.
4461  }
4462  // let's also deserialize from the tree:
4463  {
4464  my_type roundtrip;
4465  tree.load(root_id, &roundtrip); // will pick the tree read()
4466  roundtrip.check_eq(orig); // finally, let's compare.
4467  }
4468 
4469  // We created above a my_seq<std::string> specialization showing
4470  // that we can use write() without serializing the data to the
4471  // tree's arena. Let's show it working here:
4472  const my_seq_type<std::string> strseq{{
4473  "doe",
4474  "a deer, a female deer",
4475  "ray",
4476  "a drop of golden sun"
4477  }};
4478  // serialize it to a nested node in the tree:
4479  tree["not in arena"].save(strseq);
4480  // check the YAML:
4481  CHECK(ryml::emitrs_yaml<std::string>(tree["not in arena"]) == ""
4482  // note the new elements:
4483  "not in arena:" "\n"
4484  " - doe" "\n"
4485  " - a deer, a female deer" "\n"
4486  " - ray" "\n"
4487  " - a drop of golden sun" "\n"
4488  "");
4489  // show how the strings are NOT in the tree's arena
4490  size_t pos = 0;
4491  CHECK(strseq.seq_member.size() == tree["not in arena"].num_children());
4492  for(ryml::ConstNodeRef child : tree["not in arena"].children())
4493  {
4494  ryml::csubstr str_orig = ryml::to_csubstr(strseq.seq_member[pos++]);
4495  CHECK(child.val() == str_orig); // same string
4496  CHECK(!child.val().is_sub(tree.arena())); // not in the tree's arena
4497  CHECK(child.val().is_sub(str_orig)); // ... but the original memory
4498  }
4499 }
4500 
4501 
4502 //-----------------------------------------------------------------------------
4503 
4504 /** shows what happens on deserialization errors
4505  *
4506  * @note read the doxygen documentation for containers/general types at:
4507  * https://rapidyaml.readthedocs.io/v0.16.0/doxygen/group__doc__serialization__user__types.html
4508  *
4509  * @see sample_user_container_types()
4510  * @see sample_error_visit_location()
4511  * @see sample_location_tracking()
4512  */
4514 {
4515  // in this example we will be checking errors, so set up a
4516  // temporary error handler to catch them:
4517  ScopedErrorHandlerExample errh; // calls ryml::set_callbacks()
4518  // let's parse this YAML containing an invalid value,
4519  // with location tracking:
4520  ryml::EventHandlerTree tree_handler;
4521  ryml::Parser parser(&tree_handler, ryml::ParserOptions{}.locations(true));
4522  const ryml::Tree tree = parse_in_arena(&parser,
4523  "v2: (20,21)" "\n"
4524  "v3: (30,31,32)" "\n"
4525  "v4: (40,41,42,43)" "\n"
4526  "seq:" "\n"
4527  " - 101" "\n"
4528  " - 102" "\n"
4529  " - 103" "\n"
4530  " - 104" "\n"
4531  " - 105" "\n"
4532  " - 106" "\n"
4533  " - 107" "\n"
4534  "map:" "\n"
4535  " 1001: 2001" "\n"
4536  " 1002: not an int" "\n" // valid YAML, will cause deserialization error
4537  " 1003: 2003" "\n"
4538  "");
4539  ryml::ConstNodeRef root = tree;
4540  // and now let's deserialize to this variable
4541  my_type var;
4542  // .deserialize() reports the error but does not trigger an error
4543  // call:
4544  ryml::ReadResult result = root.deserialize(&var);
4545  CHECK(!result);
4546  CHECK(result.node == tree["map"]["1002"].id()); // this is where the error occurred
4547  CHECK(tree.location(parser, result.node).line == 13);
4548  CHECK(tree.location(parser, result.node).col == 2); // this node starts a column 2
4549  CHECK(parser.val_location(tree.val(result.node).str).col == 8); // its value starts at column 8
4550  // and .load() will trigger a visit error, reporting the node as
4551  // well. see sample_error_visit_location() for examples on how to
4552  // track the location of a visit error
4553  errh.check_error_occurs([&]{ root.load(&var); });
4554 }
4555 
4556 
4557 //-----------------------------------------------------------------------------
4558 
4559 /** demonstrates usage with the std implementations provided by ryml
4560  in the ryml_std.hpp header
4561  @see @ref doc_sample_container_types
4562  @see also the STL section in @ref doc_serialization */
4564 {
4565  // we're using C-strings because doxygen breaks down on raw strings
4566  std::string yml_std_string = ""
4567  "- v2: (20,21)" "\n"
4568  " v3: (30,31,32)" "\n"
4569  " v4: (40,41,42,43)" "\n"
4570  " seq:" "\n"
4571  " - 101" "\n"
4572  " - 102" "\n"
4573  " - 103" "\n"
4574  " - 104" "\n"
4575  " - 105" "\n"
4576  " - 106" "\n"
4577  " - 107" "\n"
4578  " map:" "\n"
4579  " 1001: 2001" "\n"
4580  " 1002: 2002" "\n"
4581  " 1003: 2003" "\n"
4582  "- v2: (120,121)" "\n"
4583  " v3: (130,131,132)" "\n"
4584  " v4: (140,141,142,143)" "\n"
4585  " seq:" "\n"
4586  " - 1101" "\n"
4587  " - 1102" "\n"
4588  " - 1103" "\n"
4589  " - 1104" "\n"
4590  " - 1105" "\n"
4591  " - 1106" "\n"
4592  " - 1107" "\n"
4593  " map:" "\n"
4594  " 11001: 12001" "\n"
4595  " 11002: 12002" "\n"
4596  " 11003: 12003" "\n"
4597  "- v2: (220,221)" "\n"
4598  " v3: (230,231,232)" "\n"
4599  " v4: (240,241,242,243)" "\n"
4600  " seq:" "\n"
4601  " - 2101" "\n"
4602  " - 2102" "\n"
4603  " - 2103" "\n"
4604  " - 2104" "\n"
4605  " - 2105" "\n"
4606  " - 2106" "\n"
4607  " - 2107" "\n"
4608  " map:" "\n"
4609  " 21001: 22001" "\n"
4610  " 21002: 22002" "\n"
4611  " 21003: 22003" "\n"
4612  "";
4613  // parse in-place using the std::string above
4614  ryml::Tree tree = ryml::parse_in_place(ryml::to_substr(yml_std_string));
4615  // my_type is a container-of-containers type. see above its
4616  // definition implementation for ryml.
4617  std::vector<my_type> vec;
4618  tree.rootref().load(&vec);
4619  CHECK(vec.size() == 3);
4620  ryml::Tree tree_out;
4621  tree_out.rootref().save(vec);
4622  CHECK(ryml::emitrs_yaml<std::string>(tree_out) == yml_std_string);
4623 }
4624 
4625 
4626 //-----------------------------------------------------------------------------
4627 
4628 /** control precision of serialized floats */
4630 {
4631  std::vector<double> reference{1.23234412342131234, 2.12323123143434237, 3.67847983572591234};
4632  // A safe precision for comparing doubles. May vary depending on
4633  // compiler flags. Double goes to about 15 digits, so 14 should be
4634  // safe enough for this test to succeed.
4635  const double precision_safe = 1.e-14;
4636  const size_t num_digits_safe = 14;
4637  const size_t num_digits_original = 17;
4638  auto get_num_digits = [](ryml::csubstr number){ return number.len - 2u; };
4639  //
4640  // no significant precision is lost when reading
4641  // floating point numbers:
4642  {
4643  ryml::Tree tree = ryml::parse_in_arena("[1.23234412342131234, 2.12323123143434237, 3.67847983572591234]");
4644  std::vector<double> output;
4645  tree.rootref().load(&output);
4646  CHECK(output.size() == reference.size());
4647  for(size_t i = 0; i < reference.size(); ++i)
4648  {
4649  CHECK(get_num_digits(tree[(ryml::id_type)i].val()) == num_digits_original);
4650  CHECK(fabs(output[i] - reference[i]) < precision_safe);
4651  }
4652  }
4653  //
4654  // However, depending on the compilation settings, there may be a
4655  // significant precision loss when serializing with the default
4656  // approach, .save(double):
4657  {
4658  ryml::Tree serialized;
4659  serialized.rootref().save(reference);
4660  // Without std::to_chars() there is a loss of precision:
4661  #if (!C4CORE_HAVE_STD_TOCHARS) // check if std::to_chars() is available.
4662  CHECK((ryml::emitrs_yaml<std::string>(serialized) == ""
4663  "- 1.23234" "\n"
4664  "- 2.12323" "\n"
4665  "- 3.67848" "\n"
4666  "") || (bool)"this is indicative; the exact results will vary from platform to platform.");
4667  C4_UNUSED(num_digits_safe);
4668  // ... but when using C++17 and above, the results are eminently equal:
4669  #else
4670  CHECK((ryml::emitrs_yaml<std::string>(serialized) == ""
4671  "- 1.2323441234213124" "\n"
4672  "- 2.1232312314343424" "\n"
4673  "- 3.6784798357259123" "\n"
4674  "") || (bool)"this is indicative; the exact results will vary from platform to platform.");
4675  size_t pos = 0;
4676  for(ryml::ConstNodeRef child : serialized.rootref().children())
4677  {
4678  CHECK(get_num_digits(child.val()) >= num_digits_safe);
4679  double out = {};
4680  child.load(&out);
4681  CHECK(fabs(out - reference[pos++]) < precision_safe);
4682  }
4683  #endif
4684  }
4685  //
4686  // The difference is explained by the availability of
4687  // fastfloat::from_chars(), std::from_chars() and std::to_chars().
4688  //
4689  // ryml prefers the fastfloat::from_chars() version. Unfortunately
4690  // fastfloat does not have to_chars() (see
4691  // https://github.com/fastfloat/fast_float/issues/23).
4692  //
4693  // When C++17 is used, ryml uses std::to_chars(), which produces
4694  // good defaults.
4695  //
4696  // However, with earlier standards, or in some library
4697  // implementations, there's only snprintf() available. Every other
4698  // std library function will either disrespect the string limits,
4699  // or more precisely, accept no string size limits. So the
4700  // implementation of c4core (which ryml uses) falls back to
4701  // snprintf("%g"), and that picks by default a (low) number of
4702  // digits.
4703  //
4704  // But not all is lost for C++11/C++14 users!
4705  //
4706  // To force a particular precision when serializing, you can use
4707  // c4::fmt::real() (brought into the ryml:: namespace). Or you can
4708  // serialize the number yourself! The small downside is that you
4709  // have to build the container.
4710  //
4711  // First a function to check the result:
4712  auto check_precision = [&](ryml::Tree const& serialized){
4713  std::cout << serialized;
4714  // now it works!
4715  CHECK((ryml::emitrs_yaml<std::string>(serialized) == ""
4716  "- 1.23234412342131239" "\n"
4717  "- 2.12323123143434245" "\n"
4718  "- 3.67847983572591231" "\n"
4719  "") || (bool)"this is indicative; the exact results will vary from platform to platform.");
4720  size_t pos = 0;
4721  for(ryml::ConstNodeRef child : serialized.rootref().children())
4722  {
4723  CHECK(get_num_digits(child.val()) == num_digits_original);
4724  double out = {};
4725  child.load(&out);
4726  CHECK(fabs(out - reference[pos++]) < precision_safe);
4727  }
4728  };
4729  //
4730  // Serialization example using fmt::real()
4731  {
4732  ryml::Tree serialized;
4733  ryml::NodeRef root = serialized.rootref();
4734  root.set_seq();
4735  for(const double v : reference)
4736  root.append_child().save(ryml::fmt::real(v, num_digits_original, ryml::FTOA_FLOAT));
4737  check_precision(serialized); // OK - now within bounds!
4738  }
4739  //
4740  // Serialization example using snprintf
4741  {
4742  ryml::Tree serialized;
4743  ryml::NodeRef root = serialized.rootref();
4744  root.set_seq();
4745  char tmp[64];
4746  for(const double v : reference)
4747  {
4748  // reuse a buffer to serialize.
4749  // add 1 to the significant digits because the %g
4750  // specifier counts the integral digits.
4751  (void)snprintf(tmp, sizeof(tmp), "%.18g", v);
4752  // copy the serialized string to the tree (.save()
4753  // copies to the arena, .set_val() just assigns the string
4754  // pointer and would be wrong in this case):
4755  root.append_child().save(ryml::to_csubstr((const char*)tmp));
4756  }
4757  check_precision(serialized); // OK - now within bounds!
4758  }
4759 }
4760 
4761 
4762 //-----------------------------------------------------------------------------
4763 
4764 /** demonstrates how to emit to a linear container of char */
4766 {
4767  ryml::csubstr ymla =
4768  "- 1\n"
4769  "- 2\n"
4770  "";
4771  ryml::csubstr ymlb =
4772  "- a" "\n"
4773  "- b" "\n"
4774  "- x0: 1" "\n"
4775  " x1: 2" "\n"
4776  "- champagne: Dom Perignon" "\n"
4777  " coffee: Arabica" "\n"
4778  " more:" "\n"
4779  " vinho verde: Soalheiro" "\n"
4780  " vinho tinto: Redoma 2017" "\n"
4781  " beer:" "\n"
4782  " - Rochefort 10" "\n"
4783  " - Busch" "\n"
4784  " - Leffe Rituel" "\n"
4785  "- foo" "\n"
4786  "- bar" "\n"
4787  "- baz" "\n"
4788  "- bat" "\n"
4789  "";
4790  const ryml::Tree treea = ryml::parse_in_arena(ymla);
4791  const ryml::Tree treeb = ryml::parse_in_arena(ymlb);
4792 
4793  // it is possible to emit to any linear container of char.
4794  //
4795  // eg, std::vector<char>
4796  {
4797  // do a blank call on an empty buffer to find the required size.
4798  // no overflow will occur, and returns a substr with the size
4799  // required to output
4800  ryml::csubstr output = ryml::emit_yaml(treea, treea.root_id(), ryml::substr{}, /*error_on_excess*/false);
4801  CHECK(output.str == nullptr);
4802  CHECK(output.len > 0);
4803  size_t num_needed_chars = output.len;
4804  std::vector<char> buf(num_needed_chars);
4805  // now try again with the proper buffer
4806  output = ryml::emit_yaml(treea, treea.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4807  CHECK(output == ymla);
4808 
4809  // it is possible to reuse the buffer and grow it as needed.
4810  // first do a blank run to find the size:
4811  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::substr{}, /*error_on_excess*/false);
4812  CHECK(output.str == nullptr);
4813  CHECK(output.len > 0);
4814  CHECK(output.len == ymlb.len);
4815  num_needed_chars = output.len;
4816  buf.resize(num_needed_chars);
4817  // now try again with the proper buffer
4818  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4819  CHECK(output == ymlb);
4820 
4821  // there is a convenience wrapper performing the same as above:
4822  // provided to_substr() is defined for that container.
4823  output = ryml::emitrs_yaml(treeb, &buf);
4824  CHECK(output == ymlb);
4825 
4826  // or you can just output a new container:
4827  // provided to_substr() is defined for that container.
4828  std::vector<char> another = ryml::emitrs_yaml<std::vector<char>>(treeb);
4829  CHECK(ryml::to_csubstr(another) == ymlb);
4830 
4831  // you can also emit nested nodes:
4832  another = ryml::emitrs_yaml<std::vector<char>>(treeb[3][2]);
4833  CHECK(ryml::to_csubstr(another) == ""
4834  "more:" "\n"
4835  " vinho verde: Soalheiro" "\n"
4836  " vinho tinto: Redoma 2017" "\n"
4837  "");
4838  }
4839 
4840 
4841  // eg, std::string. notice this is the same code as above
4842  {
4843  // do a blank call on an empty buffer to find the required size.
4844  // no overflow will occur, and returns a substr with the size
4845  // required to output
4846  ryml::csubstr output = ryml::emit_yaml(treea, treea.root_id(), ryml::substr{}, /*error_on_excess*/false);
4847  CHECK(output.str == nullptr);
4848  CHECK(output.len > 0);
4849  size_t num_needed_chars = output.len;
4850  std::string buf;
4851  buf.resize(num_needed_chars);
4852  // now try again with the proper buffer
4853  output = ryml::emit_yaml(treea, treea.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4854  CHECK(output == ymla);
4855 
4856  // it is possible to reuse the buffer and grow it as needed.
4857  // first do a blank run to find the size:
4858  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::substr{}, /*error_on_excess*/false);
4859  CHECK(output.str == nullptr);
4860  CHECK(output.len > 0);
4861  CHECK(output.len == ymlb.len);
4862  num_needed_chars = output.len;
4863  buf.resize(num_needed_chars);
4864  // now try again with the proper buffer
4865  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4866  CHECK(output == ymlb);
4867 
4868  // there is a convenience wrapper performing the above instructions:
4869  // provided to_substr() is defined for that container
4870  output = ryml::emitrs_yaml(treeb, &buf);
4871  CHECK(output == ymlb);
4872 
4873  // or you can just output a new container:
4874  // provided to_substr() is defined for that container.
4875  std::string another = ryml::emitrs_yaml<std::string>(treeb);
4876  CHECK(ryml::to_csubstr(another) == ymlb);
4877 
4878  // you can also emit nested nodes:
4879  another = ryml::emitrs_yaml<std::string>(treeb[3][2]);
4880  CHECK(ryml::to_csubstr(another) == ""
4881  "more:" "\n"
4882  " vinho verde: Soalheiro" "\n"
4883  " vinho tinto: Redoma 2017" "\n"
4884  "");
4885  }
4886 }
4887 
4888 
4889 //-----------------------------------------------------------------------------
4890 
4891 /** demonstrates how to emit to a stream-like structure */
4893 {
4894  ryml::csubstr ymlb =
4895  "- a" "\n"
4896  "- b" "\n"
4897  "- x0: 1" "\n"
4898  " x1: 2" "\n"
4899  "- champagne: Dom Perignon" "\n"
4900  " coffee: Arabica" "\n"
4901  " more:" "\n"
4902  " vinho verde: Soalheiro" "\n"
4903  " vinho tinto: Redoma 2017" "\n"
4904  " beer:" "\n"
4905  " - Rochefort 10" "\n"
4906  " - Busch" "\n"
4907  " - Leffe Rituel" "\n"
4908  "- foo" "\n"
4909  "- bar" "\n"
4910  "- baz" "\n"
4911  "- bat" "\n"
4912  "";
4913  const ryml::Tree tree = ryml::parse_in_arena(ymlb);
4914 
4915  std::string s;
4916 
4917  // emit a full tree
4918  {
4919  std::stringstream ss;
4920  ss << tree; // works with any stream having .operator<<() and .write()
4921  s = ss.str();
4922  CHECK(ryml::to_csubstr(s) == ymlb);
4923  }
4924 
4925  // emit a full tree as json
4926  {
4927  std::stringstream ss;
4928  ss << ryml::as_json(tree); // works with any stream having .operator<<() and .write()
4929  s = ss.str();
4930  CHECK(s ==
4931  "[" "\n"
4932  " \"a\"," "\n"
4933  " \"b\"," "\n"
4934  " {" "\n"
4935  " \"x0\": 1," "\n"
4936  " \"x1\": 2" "\n"
4937  " }," "\n"
4938  " {" "\n"
4939  " \"champagne\": \"Dom Perignon\"," "\n"
4940  " \"coffee\": \"Arabica\"," "\n"
4941  " \"more\": {" "\n"
4942  " \"vinho verde\": \"Soalheiro\"," "\n"
4943  " \"vinho tinto\": \"Redoma 2017\"" "\n"
4944  " }," "\n"
4945  " \"beer\": [" "\n"
4946  " \"Rochefort 10\"," "\n"
4947  " \"Busch\"," "\n"
4948  " \"Leffe Rituel\"" "\n"
4949  " ]" "\n"
4950  " }," "\n"
4951  " \"foo\"," "\n"
4952  " \"bar\"," "\n"
4953  " \"baz\"," "\n"
4954  " \"bat\"" "\n"
4955  "]" "\n"
4956  "");
4957  }
4958 
4959  // emit a nested node
4960  {
4961  std::stringstream ss;
4962  ss << tree[3][2]; // works with any stream having .operator<<() and .write()
4963  s = ss.str();
4964  CHECK(s == ""
4965  "more:" "\n"
4966  " vinho verde: Soalheiro" "\n"
4967  " vinho tinto: Redoma 2017" "\n"
4968  "");
4969  }
4970 
4971  // emit a nested node as json
4972  {
4973  std::stringstream ss;
4974  ss << ryml::as_json(tree[3][2]); // works with any stream having .operator<<() and .write()
4975  s = ss.str();
4976  CHECK(ryml::to_csubstr(s) ==
4977  "\"more\": {" "\n"
4978  " \"vinho verde\": \"Soalheiro\"," "\n"
4979  " \"vinho tinto\": \"Redoma 2017\"" "\n"
4980  "}" "\n"
4981  "");
4982  }
4983 }
4984 
4985 
4986 //-----------------------------------------------------------------------------
4987 
4988 /** demonstrates how to emit to a FILE* */
4990 {
4991  ryml::csubstr yml = ""
4992  "- a" "\n"
4993  "- b" "\n"
4994  "- x0: 1" "\n"
4995  " x1: 2" "\n"
4996  "- champagne: Dom Perignon" "\n"
4997  " coffee: Arabica" "\n"
4998  " more:" "\n"
4999  " vinho verde: Soalheiro" "\n"
5000  " vinho tinto: Redoma 2017" "\n"
5001  " beer:" "\n"
5002  " - Rochefort 10" "\n"
5003  " - Busch" "\n"
5004  " - Leffe Rituel" "\n"
5005  "- foo" "\n"
5006  "- bar" "\n"
5007  "- baz" "\n"
5008  "- bat" "\n"
5009  "";
5010  const ryml::Tree tree = ryml::parse_in_arena(yml);
5011  // this is emitting to stdout, but of course you can pass in any
5012  // FILE* obtained from fopen()
5013  ryml::emit_yaml(tree, tree.root_id(), stdout);
5014 }
5015 
5016 
5017 //-----------------------------------------------------------------------------
5018 
5019 /** just like parsing into a nested node, you can also emit from a nested node. */
5021 {
5022  const ryml::Tree tree = ryml::parse_in_arena(""
5023  "- a" "\n"
5024  "- b" "\n"
5025  "- x0: 1" "\n"
5026  " x1: 2" "\n"
5027  "- champagne: Dom Perignon" "\n"
5028  " coffee: Arabica" "\n"
5029  " more:" "\n"
5030  " vinho verde: Soalheiro" "\n"
5031  " vinho tinto: Redoma 2017" "\n"
5032  " beer:" "\n"
5033  " - Rochefort 10" "\n"
5034  " - Busch" "\n"
5035  " - Leffe Rituel" "\n"
5036  " - - and so" "\n"
5037  " - many other" "\n"
5038  " - wonderful beers" "\n"
5039  "- more" "\n"
5040  "- seq" "\n"
5041  "- members" "\n"
5042  "- here" "\n"
5043  "");
5044  // Let's now emit the beer node. Note that its key is also
5045  // emitted, making the result a map with a single child:
5046  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"]) == ""
5047  "beer:" "\n"
5048  " - Rochefort 10" "\n"
5049  " - Busch" "\n"
5050  " - Leffe Rituel" "\n"
5051  " - - and so" "\n"
5052  " - many other" "\n"
5053  " - wonderful beers" "\n"
5054  "");
5055  // You can use EmitOptions to prevent the key from being
5056  // emitted. Note how the result is now just the contents without
5057  // the key, and therefore the root is now a seq:
5058  auto without_key = ryml::EmitOptions{}.emit_nonroot_key(false);
5059  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"], without_key) == ""
5060  "- Rochefort 10" "\n"
5061  "- Busch" "\n"
5062  "- Leffe Rituel" "\n"
5063  "- - and so" "\n"
5064  " - many other" "\n"
5065  " - wonderful beers" "\n"
5066  "");
5067  // For sequences, the behavior is opposite. Notice how the leading
5068  // dash defaults to omit:
5069  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"][3]) == ""
5070  "- and so" "\n"
5071  "- many other" "\n"
5072  "- wonderful beers" "\n"
5073  "");
5074  // And likewise, you can use EmitOptions to force the dash:
5075  auto with_dash = ryml::EmitOptions{}.emit_nonroot_dash(true);
5076  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"][3], with_dash) == ""
5077  "- - and so" "\n" // note the added dash and indentation
5078  " - many other" "\n"
5079  " - wonderful beers" "\n"
5080  "");
5081  // Example: emit a scalar node (seq member):
5082  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"][0]) == "Rochefort 10");
5083  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"][0], with_dash) == "- Rochefort 10\n");
5084  // Example: emit a scalar node (map member):
5085  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["more"][0]) == "vinho verde: Soalheiro\n");
5086  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["more"][0], without_key) == "Soalheiro");
5087 }
5088 
5089 
5090 //-----------------------------------------------------------------------------
5091 
5092 /** query/set/modify node style to control formatting of emitted YAML
5093  * code.
5094  *
5095  * See also:
5096  * - @ref sample_parse_style()
5097  * - @ref sample_create_tree_style()
5098  * - @ref sample_style_flow_formatting()
5099  * - @ref sample_style_flow_ml_indent()
5100  * */
5102 {
5103  // we will be using these helpers throughout this function
5104  auto tostr = [](ryml::ConstNodeRef n) {
5105  return ryml::emitrs_yaml<std::string>(n);
5106  };
5107  auto tostr_opts = [](ryml::ConstNodeRef n, ryml::EmitOptions opts) {
5108  return ryml::emitrs_yaml<std::string>(n, opts);
5109  };
5110  // let's parse this yaml:
5111  ryml::csubstr yaml = ""
5112  "block map:" "\n"
5113  " block key: block val" "\n"
5114  "block seq:" "\n"
5115  " - block val 1" "\n"
5116  " - block val 2" "\n"
5117  " - 'quoted'" "\n"
5118  "flow map, singleline: {flow key: flow val}" "\n"
5119  "flow seq, singleline: [flow val,flow val]" "\n"
5120  "flow map, multiline: {" "\n"
5121  " flow key: flow val" "\n"
5122  " }" "\n"
5123  "flow seq, multiline: [" "\n"
5124  " flow val," "\n"
5125  " flow val" "\n"
5126  " ]" "\n"
5127  "";
5128  ryml::Tree tree = ryml::parse_in_arena(yaml);
5129  // while parsing, ryml marks parsed nodes with their original style:
5130  CHECK(tree.rootref().is_block());
5131  CHECK(tree["block map"].is_key_plain());
5132  CHECK(tree["block seq"].is_key_plain());
5133  CHECK(tree["flow map, singleline"].is_key_plain());
5134  CHECK(tree["flow seq, singleline"].is_key_plain());
5135  CHECK(tree["flow map, multiline"].is_key_plain());
5136  CHECK(tree["flow seq, multiline"].is_key_plain());
5137  CHECK(tree["block map"].is_block());
5138  CHECK(tree["block seq"].is_block());
5139  // flow is either singleline (FLOW_SL) or multiline (FLOW_ML1)
5140  CHECK(tree["flow map, singleline"].is_flow_sl());
5141  CHECK(tree["flow seq, singleline"].is_flow_sl());
5142  CHECK(tree["flow map, multiline"].is_flow_ml1());
5143  CHECK(tree["flow seq, multiline"].is_flow_ml1());
5144  // is_flow() is equivalent to (is_flow_sl() || is_flow_ml1() || is_flow_mln())
5145  CHECK(tree["flow map, singleline"].is_flow());
5146  CHECK(tree["flow seq, singleline"].is_flow());
5147  CHECK(tree["flow map, multiline"].is_flow());
5148  CHECK(tree["flow seq, multiline"].is_flow());
5149  //
5150  // since the tree nodes are marked with their original parsed
5151  // style, emitting the parsed tree will preserve the original
5152  // style (minus whitespace):
5153  //
5154  CHECK(tostr(tree) == yaml); // same as before!
5155  //
5156  // you can set/modify the style programatically!
5157  //
5158  // here are more examples.
5159  //
5160  {
5161  ryml::NodeRef n = tree["block map"]; // Let's look at one node
5162  // It looks like this originally:
5163  CHECK(tostr(n) ==
5164  "block map:\n"
5165  " block key: block val\n"
5166  "");
5167  // let's modify its style:
5168  n.set_key_style(ryml::KEY_SQUO); // scalar style: to single-quoted scalar
5169  n.set_container_style(ryml::FLOW_SL); // container style: to flow singleline
5170  // now it looks like this:
5171  CHECK(tostr(n) ==
5172  "'block map': {block key: block val}\n"
5173  "");
5174  }
5175  // next example
5176  {
5177  ryml::NodeRef n = tree["block seq"];
5178  CHECK(tostr(n) == ""
5179  "block seq:\n"
5180  " - block val 1\n"
5181  " - block val 2\n"
5182  " - 'quoted'\n"
5183  "");
5184  n.set_key_style(ryml::KEY_DQUO); // scalar style: to double-quoted scalar
5185  n[2].set_val_style(ryml::VAL_PLAIN); // scalar style: to plain
5186  n.set_container_style(ryml::FLOW_MLN); // container style: to flow multiline, N values per line
5187  CHECK(tostr(n) == ""
5188  "\"block seq\": [\n"
5189  " block val 1,block val 2,quoted\n"
5190  " ]\n");
5191  n.set_container_style(ryml::FLOW_MLN|ryml::FLOW_SPC); // force space after comma
5192  CHECK(tostr(n) == ""
5193  "\"block seq\": [\n"
5194  " block val 1, block val 2, quoted\n"
5195  " ]\n");
5196  auto maxcols20 = ryml::EmitOptions{}.max_cols(20); // set the max number of cols for FLOW_MLN
5197  CHECK(tostr_opts(n, maxcols20) == ""
5198  "\"block seq\": [\n"
5199  " block val 1, block val 2,\n"
5200  " quoted\n"
5201  " ]\n");
5202  n.set_container_style(ryml::FLOW_MLN); // no spaces now
5203  CHECK(tostr_opts(n, maxcols20) == ""
5204  "\"block seq\": [\n"
5205  " block val 1,block val 2,\n"
5206  " quoted\n"
5207  " ]\n");
5208  n.set_container_style(ryml::FLOW_SL); // to flow singleline
5209  CHECK(tostr(n) == ""
5210  "\"block seq\": [block val 1,block val 2,quoted]\n");
5211  n.set_container_style(ryml::FLOW_SL|ryml::FLOW_SPC); // now with space after comma
5212  CHECK(tostr(n) == ""
5213  "\"block seq\": [block val 1, block val 2, quoted]\n");
5214  n.set_container_style(ryml::FLOW_ML1); // to flow multiline, 1 value per line
5215  CHECK(tostr(n) == ""
5216  "\"block seq\": [\n"
5217  " block val 1,\n"
5218  " block val 2,\n"
5219  " quoted\n"
5220  " ]\n");
5221  /// @see See more details about formatting flow containers in
5222  /// @ref sample_style_flow_formatting() (below).
5223  }
5224  // next example
5225  {
5226  ryml::NodeRef n = tree["flow map, singleline"];
5227  CHECK(tostr(n) == "flow map, singleline: {flow key: flow val}\n");
5229  n["flow key"].set_val_style(ryml::VAL_LITERAL);
5230  CHECK(tostr(n) == ""
5231  "flow map, singleline:\n"
5232  " flow key: |-\n"
5233  " flow val\n"
5234  "");
5235  }
5236  // next example
5237  {
5238  ryml::NodeRef n = tree["flow map, multiline"];
5239  CHECK(tostr(n) == ""
5240  "flow map, multiline: {\n"
5241  " flow key: flow val\n"
5242  " }\n"
5243  "");
5245  CHECK(tostr(n) == ""
5246  "flow map, multiline:\n"
5247  " flow key: flow val\n"
5248  "");
5249  }
5250  // next example
5251  {
5252  ryml::NodeRef n = tree["flow seq, singleline"];
5253  CHECK(tostr(n) == "flow seq, singleline: [flow val,flow val]\n");
5258  CHECK(tostr(n) == ""
5259  "? >-\n"
5260  " flow seq, singleline\n"
5261  ":\n"
5262  " - 'flow val'\n"
5263  " - \"flow val\"\n"
5264  "");
5265  }
5266  // next example
5267  {
5268  ryml::NodeRef n = tree["flow seq, multiline"];
5269  CHECK(tostr(n) == ""
5270  "flow seq, multiline: [\n"
5271  " flow val,\n"
5272  " flow val\n"
5273  " ]\n"
5274  "");
5276  CHECK(tostr(n) == "flow seq, multiline: [flow val,flow val]\n");
5277  }
5278  // note the full tree now:
5279  CHECK(tostr(tree) != yaml);
5280  CHECK(tostr(tree) ==
5281  "'block map': {block key: block val}" "\n"
5282  "\"block seq\": [" "\n"
5283  " block val 1," "\n"
5284  " block val 2," "\n"
5285  " quoted" "\n"
5286  " ]" "\n"
5287  "flow map, singleline:" "\n"
5288  " flow key: |-" "\n"
5289  " flow val" "\n"
5290  "? >-" "\n"
5291  " flow seq, singleline" "\n"
5292  ":" "\n"
5293  " - 'flow val'" "\n"
5294  " - \"flow val\"" "\n"
5295  "flow map, multiline:" "\n"
5296  " flow key: flow val" "\n"
5297  "flow seq, multiline: [flow val,flow val]" "\n"
5298  "");
5299  // you can clear the style of single nodes:
5300  tree["block map"].clear_style();
5301  tree["block seq"].clear_style();
5302  CHECK(tostr(tree) ==
5303  "block map:" "\n"
5304  " block key: block val" "\n"
5305  "block seq:" "\n"
5306  " - block val 1" "\n"
5307  " - block val 2" "\n"
5308  " - quoted" "\n"
5309  "flow map, singleline:" "\n"
5310  " flow key: |-" "\n"
5311  " flow val" "\n"
5312  "? >-" "\n"
5313  " flow seq, singleline" "\n"
5314  ":" "\n"
5315  " - 'flow val'" "\n"
5316  " - \"flow val\"" "\n"
5317  "flow map, multiline:" "\n"
5318  " flow key: flow val" "\n"
5319  "flow seq, multiline: [flow val,flow val]" "\n"
5320  "");
5321  // you can clear the style recursively:
5322  tree.rootref().clear_style(/*recurse*/true);
5323  // when emitting nodes which have no style set, ryml will default
5324  // to block format for containers, and call
5325  // ryml::scalar_style_choose() to pick the style for each scalar
5326  // (at the cost of a scan over each scalar). Note that ryml picks
5327  // single-quoted for scalars containing commas:
5328  CHECK(tostr(tree) ==
5329  "block map:" "\n"
5330  " block key: block val" "\n"
5331  "block seq:" "\n"
5332  " - block val 1" "\n"
5333  " - block val 2" "\n"
5334  " - quoted" "\n"
5335  "flow map, singleline:" "\n"
5336  " flow key: flow val" "\n"
5337  "flow seq, singleline:" "\n"
5338  " - flow val" "\n"
5339  " - flow val" "\n"
5340  "flow map, multiline:" "\n"
5341  " flow key: flow val" "\n"
5342  "flow seq, multiline:" "\n"
5343  " - flow val" "\n"
5344  " - flow val" "\n"
5345  "");
5346  // you can set the style based on type conditions:
5347  //
5348  // eg, set a single key to single-quoted
5349  tree["block map"].set_style_conditionally(/*type_mask*/ryml::KEY,
5350  /*remflags*/ryml::KEY_STYLE,
5351  /*addflags*/ryml::KEY_SQUO,
5352  /*recurse*/false);
5353  CHECK(tostr(tree) ==
5354  "'block map':" "\n"
5355  " block key: block val" "\n"
5356  "block seq:" "\n"
5357  " - block val 1" "\n"
5358  " - block val 2" "\n"
5359  " - quoted" "\n"
5360  "flow map, singleline:" "\n"
5361  " flow key: flow val" "\n"
5362  "flow seq, singleline:" "\n"
5363  " - flow val" "\n"
5364  " - flow val" "\n"
5365  "flow map, multiline:" "\n"
5366  " flow key: flow val" "\n"
5367  "flow seq, multiline:" "\n"
5368  " - flow val" "\n"
5369  " - flow val" "\n"
5370  "");
5371  // change all keys to single-quoted:
5372  tree.rootref().set_style_conditionally(/*type_mask*/ryml::KEY,
5373  /*remflags*/ryml::KEY_STYLE,
5374  /*addflags*/ryml::KEY_SQUO,
5375  /*recurse*/true);
5376  // change all vals to double-quoted
5377  tree.rootref().set_style_conditionally(/*type_mask*/ryml::VAL,
5378  /*remflags*/ryml::VAL_STYLE,
5379  /*addflags*/ryml::VAL_DQUO,
5380  /*recurse*/true);
5381  // change all seqs to flow
5382  tree.rootref().set_style_conditionally(/*type_mask*/ryml::SEQ,
5383  /*remflags*/ryml::CONTAINER_STYLE,
5384  /*addflags*/ryml::FLOW_SL,
5385  /*recurse*/true);
5386  // change all maps to flow
5387  tree.rootref().set_style_conditionally(/*type_mask*/ryml::MAP,
5388  /*remflags*/ryml::CONTAINER_STYLE,
5389  /*addflags*/ryml::BLOCK,
5390  /*recurse*/true);
5391  // done!
5392  CHECK(tostr(tree) == ""
5393  "'block map':" "\n"
5394  " 'block key': \"block val\"" "\n"
5395  "'block seq': [\"block val 1\",\"block val 2\",\"quoted\"]" "\n"
5396  "'flow map, singleline':" "\n"
5397  " 'flow key': \"flow val\"" "\n"
5398  "'flow seq, singleline': [\"flow val\",\"flow val\"]" "\n"
5399  "'flow map, multiline':" "\n"
5400  " 'flow key': \"flow val\"" "\n"
5401  "'flow seq, multiline': [\"flow val\",\"flow val\"]" "\n"
5402  "");
5403  // you can also set a conditional style in a single node (or its branch if recurse is true):
5404  tree["flow seq, singleline"].set_style_conditionally(/*type_mask*/ryml::SEQ,
5405  /*remflags*/ryml::CONTAINER_STYLE,
5406  /*addflags*/ryml::BLOCK,
5407  /*recurse*/false);
5408  CHECK(tostr(tree) == ""
5409  "'block map':" "\n"
5410  " 'block key': \"block val\"" "\n"
5411  "'block seq': [\"block val 1\",\"block val 2\",\"quoted\"]" "\n"
5412  "'flow map, singleline':" "\n"
5413  " 'flow key': \"flow val\"" "\n"
5414  "'flow seq, singleline':" "\n"
5415  " - \"flow val\"" "\n"
5416  " - \"flow val\"" "\n"
5417  "'flow map, multiline':" "\n"
5418  " 'flow key': \"flow val\"" "\n"
5419  "'flow seq, multiline': [\"flow val\",\"flow val\"]" "\n"
5420  "");
5421  /// see also:
5422  /// - @ref ryml::scalar_style_choose_block()
5423  /// - @ref ryml::scalar_style_choose_flow()
5424  /// - @ref ryml::scalar_style_choose_json()
5425  /// - @ref ryml::scalar_style_query_squo()
5426  /// - @ref ryml::scalar_style_query_plain_flow()
5427  /// - @ref ryml::scalar_style_query_plain_block()
5428 }
5429 
5430 
5431 //-----------------------------------------------------------------------------
5432 
5433 /** Shows how to control formatting of flow styles. */
5435 {
5436  // we will be using this helper throughout this function
5437  auto tostr = [](ryml::ConstNodeRef n, ryml::EmitOptions opts) {
5438  return ryml::emitrs_yaml<std::string>(n, opts);
5439  };
5440  auto tostr_json = [](ryml::ConstNodeRef n, ryml::EmitOptions opts) {
5441  return ryml::emitrs_json<std::string>(n, opts);
5442  };
5443  const ryml::EmitOptions emit_defaults = ryml::EmitOptions{};
5444  // let's parse this, which is in FLOW_ML1 (flow multiline, 1 value per line):
5445  ryml::csubstr yaml = ""
5446  "{" "\n"
5447  " map: {" "\n"
5448  " seq: [" "\n"
5449  " 0," "\n"
5450  " 1," "\n"
5451  " 2," "\n"
5452  " 3," "\n"
5453  " [40,41]" "\n"
5454  " ]" "\n"
5455  " }" "\n"
5456  "}" "\n"
5457  "";
5458  // note that the parser defaults to detecting multiline flow
5459  // (FLOW_ML1) containers:
5460  {
5461  const ryml::Tree tree = ryml::parse_in_arena(yaml);
5462  CHECK(tree["map"].is_flow_ml1()); // etc
5463  CHECK(tree["map"]["seq"].is_flow_ml1()); // etc
5464  CHECK(tree["map"]["seq"][4].is_flow_sl()); // etc
5465  // emitted yaml is exactly equal to parsed yaml:
5466  CHECK(tostr(tree, emit_defaults) == yaml);
5467  // json looks similar (except for the double quotes):
5468  CHECK(tostr_json(tree, emit_defaults) ==
5469  "{" "\n"
5470  " \"map\": {" "\n"
5471  " \"seq\": [" "\n"
5472  " 0," "\n"
5473  " 1," "\n"
5474  " 2," "\n"
5475  " 3," "\n"
5476  " [40,41]" "\n"
5477  " ]" "\n"
5478  " }" "\n"
5479  "}" "\n"
5480  "");
5481  }
5482  // if you prefer to shorten the emitted yaml, you can set the
5483  // parser to disable flow multiline detection. It will then pick
5484  // singleline flow (FLOW_SL) for all flow containers:
5485  {
5487  .detect_flow_ml(false);
5488  const ryml::Tree tree = ryml::parse_in_arena(yaml, opts);
5489  CHECK(tree["map"].is_flow_sl()); // etc
5490  // notice how this is smaller now:
5491  CHECK(tostr(tree, emit_defaults) ==
5492  "{map: {seq: [0,1,2,3,[40,41]]}}");
5493  // and json as well
5494  CHECK(tostr_json(tree, emit_defaults) ==
5495  "{\"map\": {\"seq\": [0,1,2,3,[40,41]]}}");
5496  // you can also force spaces everywhere without adding
5497  // FLOW_SPC in individual containers:
5498  const ryml::EmitOptions with_spaces = ryml::EmitOptions{}
5499  .force_flow_spc(true);
5500  CHECK(tostr(tree, with_spaces) ==
5501  "{map: {seq: [0, 1, 2, 3, [40, 41]]}}");
5502  // and json as well
5503  CHECK(tostr_json(tree, with_spaces) ==
5504  "{\"map\": {\"seq\": [0, 1, 2, 3, [40, 41]]}}");
5505  }
5506  // or you can still have the default detection of flow_ml, but set
5507  // it to pick FLOW_MLN (multiline, n values per line), instead of
5508  // the default FLOW_ML1 (multiline, 1 values per line)
5509  {
5512  const ryml::Tree tree = ryml::parse_in_arena(yaml, opts);
5513  CHECK(tree["map"].is_flow_mln());
5514  CHECK(tree["map"]["seq"][4].is_flow_sl()); // [40,41] is FLOW_SL
5515  CHECK(tostr(tree, emit_defaults) ==
5516  "{" "\n"
5517  " map: {" "\n"
5518  " seq: [" "\n"
5519  " 0,1,2,3,[40,41]" "\n"
5520  " ]" "\n"
5521  " }" "\n"
5522  "}" "\n");
5523  CHECK(tostr_json(tree, emit_defaults) ==
5524  "{" "\n"
5525  " \"map\": {" "\n"
5526  " \"seq\": [" "\n"
5527  " 0,1,2,3,[40,41]" "\n"
5528  " ]" "\n"
5529  " }" "\n"
5530  "}" "\n");
5531  // now with spaces:
5532  const ryml::EmitOptions with_spaces = ryml::EmitOptions{}
5533  .force_flow_spc(true);
5534  CHECK(tostr(tree, with_spaces) ==
5535  "{" "\n"
5536  " map: {" "\n"
5537  " seq: [" "\n"
5538  " 0, 1, 2, 3, [40, 41]" "\n"
5539  " ]" "\n"
5540  " }" "\n"
5541  "}" "\n");
5542  CHECK(tostr_json(tree, with_spaces) ==
5543  "{" "\n"
5544  " \"map\": {" "\n"
5545  " \"seq\": [" "\n"
5546  " 0, 1, 2, 3, [40, 41]" "\n"
5547  " ]" "\n"
5548  " }" "\n"
5549  "}" "\n");
5550  }
5551  // you can also disable indentation of both FLOW_ML1 and FLOW_MLN
5552  // (see more details in @ref sample_style_flow_ml_indent())
5553  {
5554  const ryml::EmitOptions noindent = ryml::EmitOptions{}
5555  .indent_flow_ml(false);
5556  const ryml::Tree tree = ryml::parse_in_arena(yaml);
5557  CHECK(tree["map"].is_flow_ml1());
5558  CHECK(tree["map"]["seq"][4].is_flow_sl()); // [40,41] is FLOW_SL
5559  CHECK(tostr(tree, noindent) == ""
5560  "{" "\n"
5561  "map: {" "\n"
5562  "seq: [" "\n"
5563  "0," "\n"
5564  "1," "\n"
5565  "2," "\n"
5566  "3," "\n"
5567  "[40,41]" "\n"
5568  "]" "\n"
5569  "}" "\n"
5570  "}" "\n"
5571  "");
5572  CHECK(tostr_json(tree, noindent) == ""
5573  "{" "\n"
5574  "\"map\": {" "\n"
5575  "\"seq\": [" "\n"
5576  "0," "\n"
5577  "1," "\n"
5578  "2," "\n"
5579  "3," "\n"
5580  "[40,41]" "\n"
5581  "]" "\n"
5582  "}" "\n"
5583  "}" "\n"
5584  "");
5585  }
5586  // finally, you can control the number of columns in FLOW_MLN:
5587  {
5588  // let's pick a different example to make this clearer
5589  ryml::csubstr yaml2 = ""
5590  "[" "\n"
5591  " 0, 1, 2, 3, 4, 5, 6, 7, 8, 9," "\n"
5592  " 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, " "\n"
5593  " 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, " "\n"
5594  " 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, " "\n"
5595  " 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, " "\n"
5596  " 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, " "\n"
5597  " 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, " "\n"
5598  " 70, 71, 72, 73, 74, 75, 76, 77, 78, 79 " "\n"
5599  "]";
5600  // Let's force the parser to pick FLOW_MLN instead of
5601  // FLOW_ML1. We're doing that because wrapping is only done in
5602  // FLOW_MLN and -- as their names imply -- FLOW_SL is
5603  // single-line, and FLOW_ML1 is 1 value per line.
5606  const ryml::Tree tree = ryml::parse_in_arena(yaml2, opts);
5607  CHECK(tree.rootref().type().is_flow_mln());
5608  // default max columns is 80:
5609  CHECK(tostr(tree, emit_defaults) == ""
5610  "[\n"
5611  " 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,\n"
5612  " 30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,\n"
5613  " 56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79\n"
5614  "]\n"
5615  "");
5616  CHECK(tostr_json(tree, emit_defaults) == ""
5617  "[\n"
5618  " 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,\n"
5619  " 29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,\n"
5620  " 55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79\n"
5621  "]\n"
5622  "");
5623  // let's try setting max columns to 40:
5624  const ryml::EmitOptions maxcols40 = ryml::EmitOptions{}
5625  .max_cols(40);
5626  CHECK(tostr(tree, maxcols40) == ""
5627  "[\n"
5628  " 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,\n"
5629  " 16,17,18,19,20,21,22,23,24,25,26,27,28,\n"
5630  " 29,30,31,32,33,34,35,36,37,38,39,40,41,\n"
5631  " 42,43,44,45,46,47,48,49,50,51,52,53,54,\n"
5632  " 55,56,57,58,59,60,61,62,63,64,65,66,67,\n"
5633  " 68,69,70,71,72,73,74,75,76,77,78,79\n"
5634  "]\n"
5635  "");
5636  CHECK(tostr_json(tree, maxcols40) == ""
5637  "[\n"
5638  " 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,\n"
5639  " 16,17,18,19,20,21,22,23,24,25,26,27,28,\n"
5640  " 29,30,31,32,33,34,35,36,37,38,39,40,41,\n"
5641  " 42,43,44,45,46,47,48,49,50,51,52,53,54,\n"
5642  " 55,56,57,58,59,60,61,62,63,64,65,66,67,\n"
5643  " 68,69,70,71,72,73,74,75,76,77,78,79\n"
5644  "]\n"
5645  "");
5646  // Note that you can globally force spaces everywhere through
5647  // the emit options:
5648  const ryml::EmitOptions with_spaces = ryml::EmitOptions{}
5649  .force_flow_spc(true);
5650  CHECK(tostr(tree, with_spaces) == ""
5651  "[\n"
5652  " 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,\n"
5653  " 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,\n"
5654  " 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,\n"
5655  " 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79\n"
5656  "]\n"
5657  "");
5658  CHECK(tostr_json(tree, with_spaces) == ""
5659  "[\n"
5660  " 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,\n"
5661  " 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,\n"
5662  " 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,\n"
5663  " 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79\n"
5664  "]\n"
5665  "");
5666  // and you can combine spaces with max columns:
5667  const ryml::EmitOptions maxcols40_spc = ryml::EmitOptions{}
5668  .max_cols(40)
5669  .force_flow_spc(true);
5670  CHECK(tostr(tree, maxcols40_spc) == ""
5671  "[\n"
5672  " 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,\n"
5673  " 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,\n"
5674  " 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\n"
5675  " 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,\n"
5676  " 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,\n"
5677  " 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,\n"
5678  " 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,\n"
5679  " 72, 73, 74, 75, 76, 77, 78, 79\n"
5680  "]\n"
5681  "");
5682  CHECK(tostr_json(tree, maxcols40_spc) == ""
5683  "[\n"
5684  " 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,\n"
5685  " 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,\n"
5686  " 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,\n"
5687  " 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,\n"
5688  " 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,\n"
5689  " 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,\n"
5690  " 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,\n"
5691  " 72, 73, 74, 75, 76, 77, 78, 79\n"
5692  "]\n"
5693  "");
5694  // and you can combine spaces with max columns with no indentation:
5695  const ryml::EmitOptions maxcols40_spc_noindent = ryml::EmitOptions{}
5696  .max_cols(40)
5697  .force_flow_spc(true)
5698  .indent_flow_ml(false);
5699  CHECK(tostr(tree, maxcols40_spc_noindent) == ""
5700  "[\n"
5701  "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,\n"
5702  "13, 14, 15, 16, 17, 18, 19, 20, 21, 22,\n"
5703  "23, 24, 25, 26, 27, 28, 29, 30, 31, 32,\n"
5704  "33, 34, 35, 36, 37, 38, 39, 40, 41, 42,\n"
5705  "43, 44, 45, 46, 47, 48, 49, 50, 51, 52,\n"
5706  "53, 54, 55, 56, 57, 58, 59, 60, 61, 62,\n"
5707  "63, 64, 65, 66, 67, 68, 69, 70, 71, 72,\n"
5708  "73, 74, 75, 76, 77, 78, 79\n"
5709  "]\n"
5710  "");
5711  CHECK(tostr_json(tree, maxcols40_spc_noindent) == ""
5712  "[\n"
5713  "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,\n"
5714  "13, 14, 15, 16, 17, 18, 19, 20, 21, 22,\n"
5715  "23, 24, 25, 26, 27, 28, 29, 30, 31, 32,\n"
5716  "33, 34, 35, 36, 37, 38, 39, 40, 41, 42,\n"
5717  "43, 44, 45, 46, 47, 48, 49, 50, 51, 52,\n"
5718  "53, 54, 55, 56, 57, 58, 59, 60, 61, 62,\n"
5719  "63, 64, 65, 66, 67, 68, 69, 70, 71, 72,\n"
5720  "73, 74, 75, 76, 77, 78, 79\n"
5721  "]\n"
5722  "");
5723  }
5724  // Note that FLOW_SPC is /not/ detected by the parser, and that
5725  // depending on the parse options, either FLOW_ML1 or FLOW_MLN
5726  // will be used for /all/ multiline flow containers.
5727 }
5728 
5729 
5730 //-----------------------------------------------------------------------------
5731 
5732 /** control the indentation of emitted flow multiline containers */
5734 {
5735  // we will be using this helper throughout this function
5736  auto tostr = [](ryml::ConstNodeRef n, ryml::EmitOptions opts) {
5737  return ryml::emitrs_yaml<std::string>(n, opts);
5738  };
5739  ryml::csubstr yaml = "{map: {seq: [0, 1, 2, 3, [40, 41]]}}";
5740  ryml::Tree tree = ryml::parse_in_arena(yaml);
5741  ryml::EmitOptions defaults = {};
5743  CHECK(tostr(tree, defaults) == "{map: {seq: [0,1,2,3,[40,41]]}}");
5744  // let's now set the style to FLOW_ML1 (it was FLOW_SL)
5746  tree["map"].set_container_style(ryml::FLOW_ML1);
5747  tree["map"]["seq"].set_container_style(ryml::FLOW_ML1);
5748  tree["map"]["seq"][4].set_container_style(ryml::FLOW_ML1);
5749  // by default FLOW_ML1 prints one value per line, indented:
5750  CHECK(tostr(tree, defaults) ==
5751  "{" "\n"
5752  " map: {" "\n"
5753  " seq: [" "\n"
5754  " 0," "\n"
5755  " 1," "\n"
5756  " 2," "\n"
5757  " 3," "\n"
5758  " [" "\n"
5759  " 40," "\n"
5760  " 41" "\n"
5761  " ]" "\n"
5762  " ]" "\n"
5763  " }" "\n"
5764  "}" "\n"
5765  "");
5766  // if we use the noindent options, then each value is put at the
5767  // beginning of the line
5768  CHECK(tostr(tree, noindent) ==
5769  "{" "\n"
5770  "map: {" "\n"
5771  "seq: [" "\n"
5772  "0," "\n"
5773  "1," "\n"
5774  "2," "\n"
5775  "3," "\n"
5776  "[" "\n"
5777  "40," "\n"
5778  "41" "\n"
5779  "]" "\n"
5780  "]" "\n"
5781  "}" "\n"
5782  "}" "\n"
5783  "");
5784  // Note that the noindent option will safely respect any prior
5785  // indent level from enclosing block containers! For example:
5787  CHECK(tostr(tree, noindent) == ""// notice it is indented at the map level
5788  "map: {" "\n"
5789  " seq: [" "\n"
5790  " 0," "\n"
5791  " 1," "\n"
5792  " 2," "\n"
5793  " 3," "\n"
5794  " [" "\n"
5795  " 40," "\n"
5796  " 41" "\n"
5797  " ]" "\n"
5798  " ]" "\n"
5799  " }" "\n"
5800  "");
5801  // Let's set it one BLOCK level further:
5802  tree["map"].set_container_style(ryml::BLOCK);
5803  CHECK(tostr(tree, noindent) == // notice it is indented one more level
5804  "map:" "\n"
5805  " seq: [" "\n"
5806  " 0," "\n"
5807  " 1," "\n"
5808  " 2," "\n"
5809  " 3," "\n"
5810  " [" "\n"
5811  " 40," "\n"
5812  " 41" "\n"
5813  " ]" "\n"
5814  " ]" "\n"
5815  "");
5816 }
5817 
5818 
5819 //-----------------------------------------------------------------------------
5820 
5821 /** shows how to parse and emit JSON.
5822  *
5823  * To emit YAML parsed from JSON, see also @ref sample_style() for
5824  * info on clearing the style flags (example below). */
5826 {
5827  ryml::csubstr json = ""
5828  "{" "\n"
5829  " \"doe\": \"a deer, a female deer\"," "\n"
5830  " \"ray\": \"a drop of golden sun\"," "\n"
5831  " \"me\": \"a name, I call myself\"," "\n"
5832  " \"far\": \"a long long way to go\"" "\n"
5833  "}" "\n"
5834  "";
5835  // Since JSON is a subset of YAML, parsing JSON is just the
5836  // same as YAML:
5837  ryml::Tree tree = ryml::parse_in_arena(json);
5838  // If you are sure the source is valid json, you can use the
5839  // appropriate parse_json overload, which is stricter and faster
5840  // because json has a much smaller grammar:
5841  ryml::Tree json_tree = ryml::parse_json_in_arena(json);
5842  // to emit JSON:
5843  CHECK(ryml::emitrs_json<std::string>(tree) == json);
5844  CHECK(ryml::emitrs_json<std::string>(json_tree) == json);
5845  // to emit JSON to a stream:
5846  std::stringstream ss;
5847  ss << ryml::as_json(tree); // <- mark it like this
5848  CHECK(ss.str() == json);
5849  // Note the following limitations on the json emitter:
5850  //
5851  // - YAML streams are emitted as seqs by default. If you want to
5852  // flag this, and want it to be an error, there is a setting to
5853  // control this in EmitOptions.
5854  //
5855  // - YAML tags cannot be emitted as JSON, and are allowed only if the
5856  // relevant setting is enabled in EmitOptions.
5857  //
5858  // - Likewise, anchors and references cannot be emitted as JSON
5859  // and are allowed only if the relevant setting is enabled in
5860  // EmitOptions.
5861  //
5862 
5863  // Note that when parsing JSON, ryml will set the style of each
5864  // node in the JSON. This means that if you emit YAML from a tree
5865  // parsed from JSON, it will look mostly the same as the original
5866  // JSON:
5867  CHECK(ryml::emitrs_yaml<std::string>(json_tree) == json);
5868  // If you want to avoid this, you will need to clear the style.
5869  json_tree.rootref().clear_style(); // clear the style of the map (without recursing)
5870  // note that this is now block mode. That is because when no
5871  // style is set, ryml defaults to emitting in block mode.
5872  CHECK(ryml::emitrs_yaml<std::string>(json_tree) == ""
5873  "\"doe\": \"a deer, a female deer\"" "\n"
5874  "\"ray\": \"a drop of golden sun\"" "\n"
5875  "\"me\": \"a name, I call myself\"" "\n"
5876  "\"far\": \"a long long way to go\"" "\n"
5877  "");
5878  // if you don't want the double quotes in the scalar, you can
5879  // recurse:
5880  json_tree.rootref().clear_style(/*recurse*/true);
5881  // so now when emitting you will get this:
5882  CHECK(ryml::emitrs_yaml<std::string>(json_tree) == ""
5883  "doe: a deer, a female deer" "\n"
5884  "ray: a drop of golden sun" "\n"
5885  "me: a name, I call myself" "\n"
5886  "far: a long long way to go" "\n"
5887  "");
5888  // see in particular sample_style() for more examples
5889 }
5890 
5891 
5892 //-----------------------------------------------------------------------------
5893 
5894 /** demonstrates usage with anchors and alias references.
5895 
5896 Note that dereferencing is opt-in; after parsing, you have to call
5897 @ref c4::yml::Tree::resolve() explicitly if you want resolved
5898 references in the tree. This method will resolve all references and
5899 substitute the anchored values in place of the reference.
5900 
5901 The @ref c4::yml::Tree::resolve() method first does a full traversal
5902 of the tree to gather all anchors and references in a separate
5903 collection, then it goes through that collection to locate the names,
5904 which it does by obeying the YAML standard diktat that
5905 
5906  > an alias node refers to the most recent node in
5907  > the serialization having the specified anchor
5908 
5909 So, depending on the number of anchor/alias nodes, this is a
5910 potentially expensive operation, with a best-case linear complexity
5911 (from the initial traversal) and a worst-case quadratic complexity (if
5912 every node has an alias/anchor). This potential cost is the reason for
5913 requiring an explicit call to @ref c4::yml::Tree::resolve() */
5915 {
5916  std::string unresolved = ""
5917  "base: &base" "\n"
5918  " name: Everyone has same name" "\n"
5919  "foo: &foo" "\n"
5920  " <<: *base" "\n"
5921  " age: 10" "\n"
5922  "bar: &bar" "\n"
5923  " <<: *base" "\n"
5924  " age: 20" "\n"
5925  "bill_to: &id001" "\n"
5926  " street: |-" "\n"
5927  " 123 Tornado Alley" "\n"
5928  " Suite 16" "\n"
5929  " city: East Centerville" "\n"
5930  " state: KS" "\n"
5931  "ship_to: *id001" "\n"
5932  "&keyref key: &valref val" "\n"
5933  "*valref : *keyref" "\n"
5934  "";
5935  std::string resolved = ""
5936  "base:" "\n"
5937  " name: Everyone has same name" "\n"
5938  "foo:" "\n"
5939  " name: Everyone has same name" "\n"
5940  " age: 10" "\n"
5941  "bar:" "\n"
5942  " name: Everyone has same name" "\n"
5943  " age: 20" "\n"
5944  "bill_to:" "\n"
5945  " street: |-" "\n"
5946  " 123 Tornado Alley" "\n"
5947  " Suite 16" "\n"
5948  " city: East Centerville" "\n"
5949  " state: KS" "\n"
5950  "ship_to:" "\n"
5951  " street: |-" "\n"
5952  " 123 Tornado Alley" "\n"
5953  " Suite 16" "\n"
5954  " city: East Centerville" "\n"
5955  " state: KS" "\n"
5956  "key: val" "\n"
5957  "val: key" "\n"
5958  "";
5959 
5960  ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(unresolved));
5961  // by default, references are not resolved when parsing:
5962  CHECK( ! tree["base"].has_key_anchor());
5963  CHECK( tree["base"].has_val_anchor());
5964  CHECK( tree["base"].val_anchor() == "base");
5965  CHECK( tree["key"].key_anchor() == "keyref");
5966  CHECK( tree["key"].val_anchor() == "valref");
5967  CHECK( tree["*valref"].is_key_ref());
5968  CHECK( tree["*valref"].is_val_ref());
5969  CHECK( tree["*valref"].key_ref() == "valref");
5970  CHECK( tree["*valref"].val_ref() == "keyref");
5971 
5972  // to resolve references, simply call tree.resolve(),
5973  // which will perform the reference instantiations:
5974  tree.resolve();
5975 
5976  // all the anchors and references are substistuted and then removed:
5977  CHECK( ! tree["base"].has_key_anchor());
5978  CHECK( ! tree["base"].has_val_anchor());
5979  CHECK( ! tree["base"].has_val_anchor());
5980  CHECK( ! tree["key"].has_key_anchor());
5981  CHECK( ! tree["key"].has_val_anchor());
5982  CHECK( ! tree["val"].is_key_ref()); // notice *valref is now turned to val
5983  CHECK( ! tree["val"].is_val_ref()); // notice *valref is now turned to val
5984 
5985  CHECK(tree["ship_to"]["city"].val() == "East Centerville");
5986  CHECK(tree["ship_to"]["state"].val() == "KS");
5987 }
5988 
5989 /** demonstrates how to use the API to programatically create anchors
5990  * and aliases */
5992 {
5993  // part 1: anchor/ref
5994  {
5995  ryml::Tree t;
5997  t["kanchor"].set_val("2");
5998  t["kanchor"].set_key_anchor("kanchor");
5999  t["vanchor"].set_val("3");
6000  t["vanchor"].set_val_anchor("vanchor");
6001  // to set a reference, need to call .set_val_ref()/.set_key_ref()
6002  t["kref"].set_val_ref("kanchor");
6003  t["vref"].set_val_ref("vanchor");
6004  t["nref"].set_val("*vanchor"); // NOTE: this is not set as a reference in the tree!
6005  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
6006  "&kanchor kanchor: 2" "\n"
6007  "vanchor: &vanchor 3" "\n"
6008  "kref: *kanchor" "\n"
6009  "vref: *vanchor" "\n"
6010  // note that ryml emits nref with quotes to disambiguate
6011  // (because no style was set)
6012  "nref: '*vanchor'" "\n"
6013  "");
6014  t.resolve();
6015  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
6016  "kanchor: 2" "\n"
6017  "vanchor: 3" "\n"
6018  "kref: kanchor" "\n"
6019  "vref: 3" "\n"
6020  // note that nref was not resolved
6021  "nref: '*vanchor'" "\n"
6022  "");
6023  }
6024 
6025  // part 2: simple inheritance (ie, adding `<<: *anchor` nodes)
6026  {
6028  "orig: &orig {foo: bar, baz: bat}" "\n"
6029  "copy: {}" "\n"
6030  "notcopy: {}" "\n"
6031  "notref: {}" "\n"
6032  "");
6033  t["copy"]["<<"].set_val_ref("orig");
6034  t["notcopy"]["test"].set_val_ref("orig");
6035  t["notcopy"]["<<"].set_val_ref("orig");
6036  t["notref"]["<<"].set_val("*orig"); // not a reference! .set_val_ref() was not called
6037  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
6038  "orig: &orig {foo: bar,baz: bat}" "\n"
6039  "copy: {<<: *orig}" "\n"
6040  "notcopy: {test: *orig,<<: *orig}" "\n"
6041  "notref: {<<: '*orig'}" "\n"
6042  "");
6043  t.resolve();
6044  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
6045  "orig: {foo: bar,baz: bat}" "\n"
6046  "copy: {foo: bar,baz: bat}" "\n"
6047  "notcopy: {test: {foo: bar,baz: bat},foo: bar,baz: bat}" "\n"
6048  "notref: {<<: '*orig'}" "\n"
6049  "");
6050  }
6051 
6052  // part 3: multiple inheritance (ie, `<<: [*ref1,*ref2,*etc]`)
6053  {
6055  "orig1: &orig1 {foo: bar}" "\n"
6056  "orig2: &orig2 {baz: bat}" "\n"
6057  "orig3: &orig3 {and: more}" "\n"
6058  "copy: {}" "\n"
6059  "");
6060  ryml::NodeRef seq = t["copy"]["<<"];
6061  seq.set_seq();
6062  seq.append_child().set_val_ref("orig1");
6063  seq.append_child().set_val_ref("orig2");
6064  seq.append_child().set_val_ref("orig3");
6065  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
6066  "orig1: &orig1 {foo: bar}" "\n"
6067  "orig2: &orig2 {baz: bat}" "\n"
6068  "orig3: &orig3 {and: more}" "\n"
6069  "copy: {<<: [*orig1,*orig2,*orig3]}" "\n"
6070  "");
6071  t.resolve();
6072  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
6073  "orig1: {foo: bar}" "\n"
6074  "orig2: {baz: bat}" "\n"
6075  "orig3: {and: more}" "\n"
6076  "copy: {foo: bar,baz: bat,and: more}" "\n");
6077  }
6078 }
6079 
6080 
6081 //-----------------------------------------------------------------------------
6082 
6084 {
6085  const std::string yaml = ""
6086  "--- !!map" "\n"
6087  "a: 0" "\n"
6088  "b: 1" "\n"
6089  "--- !map" "\n"
6090  "a: b" "\n"
6091  "--- !!seq" "\n"
6092  "- a" "\n"
6093  "- b" "\n"
6094  "--- !!str a b" "\n"
6095  "--- !!str 'a: b'" "\n"
6096  "---" "\n"
6097  "!!str a: b" "\n"
6098  "--- !!set" "\n"
6099  "? a" "\n"
6100  "? b" "\n"
6101  "--- !!set" "\n"
6102  "a:" "\n"
6103  "--- !!seq" "\n"
6104  "- !!int 0" "\n"
6105  "- !!str 1" "\n"
6106  "";
6107  const ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(yaml));
6108  const ryml::ConstNodeRef root = tree.rootref();
6109  CHECK(root.is_stream());
6110  CHECK(root.num_children() == 9);
6111  for(ryml::ConstNodeRef doc : root.children())
6112  CHECK(doc.is_doc());
6113  // tags are kept verbatim from the source:
6114  CHECK(root[0].has_val_tag());
6115  CHECK(root[0].val_tag() == "!!map"); // valid only if the node has a val tag
6116  CHECK(root[1].val_tag() == "!map");
6117  CHECK(root[2].val_tag() == "!!seq");
6118  CHECK(root[3].val_tag() == "!!str");
6119  CHECK(root[4].val_tag() == "!!str");
6120  CHECK(root[5]["a"].has_key_tag());
6121  CHECK(root[5]["a"].key_tag() == "!!str"); // valid only if the node has a key tag
6122  CHECK(root[6].val_tag() == "!!set");
6123  CHECK(root[7].val_tag() == "!!set");
6124  CHECK(root[8].val_tag() == "!!seq");
6125  CHECK(root[8][0].val_tag() == "!!int");
6126  CHECK(root[8][1].val_tag() == "!!str");
6127  // ryml also provides a complete toolbox to deal with tags.
6128  // there is an enumeration for the standard YAML tags:
6129  CHECK(ryml::to_tag("!map") == ryml::TAG_NONE);
6130  CHECK(ryml::to_tag("!!map") == ryml::TAG_MAP);
6131  CHECK(ryml::to_tag("!!seq") == ryml::TAG_SEQ);
6132  CHECK(ryml::to_tag("!!str") == ryml::TAG_STR);
6133  CHECK(ryml::to_tag("!!int") == ryml::TAG_INT);
6134  CHECK(ryml::to_tag("!!set") == ryml::TAG_SET);
6135  // given a tag enum, you can fetch the short tag string:
6137  CHECK(ryml::from_tag(ryml::TAG_MAP) == "!!map");
6138  CHECK(ryml::from_tag(ryml::TAG_SEQ) == "!!seq");
6139  CHECK(ryml::from_tag(ryml::TAG_STR) == "!!str");
6140  CHECK(ryml::from_tag(ryml::TAG_INT) == "!!int");
6141  CHECK(ryml::from_tag(ryml::TAG_SET) == "!!set");
6142  // you can also fetch the long tag string:
6144  CHECK(ryml::from_tag_long(ryml::TAG_MAP) == "<tag:yaml.org,2002:map>");
6145  CHECK(ryml::from_tag_long(ryml::TAG_SEQ) == "<tag:yaml.org,2002:seq>");
6146  CHECK(ryml::from_tag_long(ryml::TAG_STR) == "<tag:yaml.org,2002:str>");
6147  CHECK(ryml::from_tag_long(ryml::TAG_INT) == "<tag:yaml.org,2002:int>");
6148  CHECK(ryml::from_tag_long(ryml::TAG_SET) == "<tag:yaml.org,2002:set>");
6149  // and likewise:
6150  CHECK(ryml::to_tag("!map") == ryml::TAG_NONE);
6151  CHECK(ryml::to_tag("<tag:yaml.org,2002:map>") == ryml::TAG_MAP);
6152  CHECK(ryml::to_tag("<tag:yaml.org,2002:seq>") == ryml::TAG_SEQ);
6153  CHECK(ryml::to_tag("<tag:yaml.org,2002:str>") == ryml::TAG_STR);
6154  CHECK(ryml::to_tag("<tag:yaml.org,2002:int>") == ryml::TAG_INT);
6155  CHECK(ryml::to_tag("<tag:yaml.org,2002:set>") == ryml::TAG_SET);
6156  // to normalize a tag as much as possible, use normalize_tag():
6157  CHECK(ryml::normalize_tag("!!map") == "!!map");
6158  CHECK(ryml::normalize_tag("!<tag:yaml.org,2002:map>") == "!!map");
6159  CHECK(ryml::normalize_tag("<tag:yaml.org,2002:map>") == "!!map");
6160  CHECK(ryml::normalize_tag("tag:yaml.org,2002:map") == "!!map");
6161  CHECK(ryml::normalize_tag("!<!!map>") == "<!!map>");
6162  CHECK(ryml::normalize_tag("!map") == "!map");
6163  CHECK(ryml::normalize_tag("!my!foo") == "!my!foo");
6164  // and also for the long form:
6165  CHECK(ryml::normalize_tag_long("!!map") == "<tag:yaml.org,2002:map>");
6166  CHECK(ryml::normalize_tag_long("!<tag:yaml.org,2002:map>") == "<tag:yaml.org,2002:map>");
6167  CHECK(ryml::normalize_tag_long("<tag:yaml.org,2002:map>") == "<tag:yaml.org,2002:map>");
6168  CHECK(ryml::normalize_tag_long("tag:yaml.org,2002:map") == "<tag:yaml.org,2002:map>");
6169  CHECK(ryml::normalize_tag_long("!<!!map>") == "<!!map>");
6170  CHECK(ryml::normalize_tag_long("!map") == "!map");
6171  // The tree provides the following methods applying to every node
6172  // with a key and/or val tag:
6173  ryml::Tree normalized_tree = tree;
6174  normalized_tree.normalize_tags(); // normalize all tags in short form
6175  CHECK(ryml::emitrs_yaml<std::string>(normalized_tree) == ""
6176  "--- !!map" "\n"
6177  "a: 0" "\n"
6178  "b: 1" "\n"
6179  "--- !map" "\n"
6180  "a: b" "\n"
6181  "--- !!seq" "\n"
6182  "- a" "\n"
6183  "- b" "\n"
6184  "--- !!str a b" "\n"
6185  "--- !!str 'a: b'" "\n"
6186  "---" "\n"
6187  "!!str a: b" "\n"
6188  "--- !!set" "\n"
6189  "a: " "\n"
6190  "b: " "\n"
6191  "--- !!set" "\n"
6192  "a: " "\n"
6193  "--- !!seq" "\n"
6194  "- !!int 0" "\n"
6195  "- !!str 1" "\n"
6196  "");
6197  ryml::Tree normalized_tree_long = tree;
6198  normalized_tree_long.normalize_tags_long(); // normalize all tags in short form
6199  CHECK(ryml::emitrs_yaml<std::string>(normalized_tree_long) == ""
6200  "--- !<tag:yaml.org,2002:map>" "\n"
6201  "a: 0" "\n"
6202  "b: 1" "\n"
6203  "--- !map" "\n"
6204  "a: b" "\n"
6205  "--- !<tag:yaml.org,2002:seq>" "\n"
6206  "- a" "\n"
6207  "- b" "\n"
6208  "--- !<tag:yaml.org,2002:str> a b" "\n"
6209  "--- !<tag:yaml.org,2002:str> 'a: b'" "\n"
6210  "---" "\n"
6211  "!<tag:yaml.org,2002:str> a: b" "\n"
6212  "--- !<tag:yaml.org,2002:set>" "\n"
6213  "a: " "\n"
6214  "b: " "\n"
6215  "--- !<tag:yaml.org,2002:set>" "\n"
6216  "a: " "\n"
6217  "--- !<tag:yaml.org,2002:seq>" "\n"
6218  "- !<tag:yaml.org,2002:int> 0" "\n"
6219  "- !<tag:yaml.org,2002:str> 1" "\n"
6220  "");
6221 }
6222 
6223 
6224 //-----------------------------------------------------------------------------
6225 
6227 {
6228  const std::string yaml = ""
6229  "%TAG !m! !my-" "\n"
6230  "--- # Bulb here" "\n"
6231  "!m!light fluorescent" "\n"
6232  "..." "\n"
6233  "%TAG !m! !meta-" "\n"
6234  "--- # Color here" "\n"
6235  "!m!light green" "\n"
6236  "";
6237  // tags are not resolved by default:
6239  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
6240  "%TAG !m! !my-" "\n"
6241  "--- !m!light fluorescent" "\n"
6242  "..." "\n"
6243  "%TAG !m! !meta-" "\n"
6244  "--- !m!light green" "\n"
6245  "");
6246  // Use Tree::resolve_tags() to accomplish this in an existing
6247  // tree:
6248  ryml::TagCache tag_cache; // reduces memory requirements by reusing resolved tags
6249  tree.resolve_tags(tag_cache);
6250  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
6251  "%TAG !m! !my-" "\n"
6252  "--- !<!my-light> fluorescent" "\n"
6253  "..." "\n"
6254  "%TAG !m! !meta-" "\n"
6255  "--- !<!meta-light> green" "\n"
6256  "");
6257  // You can also Use ParserOptions to force resolution of tags
6258  // while parsing:
6260  ryml::Tree resolved_tree = ryml::parse_in_arena(ryml::to_csubstr(yaml), opts);
6261  CHECK(ryml::emitrs_yaml<std::string>(resolved_tree) == ""
6262  "%TAG !m! !my-" "\n"
6263  "--- !<!my-light> fluorescent" "\n"
6264  "..." "\n"
6265  "%TAG !m! !meta-" "\n"
6266  "--- !<!meta-light> green" "\n"
6267  "");
6268  // see also tree.normalize_tags()
6269  // see also tree.normalize_tags_long()
6270 }
6271 
6272 
6273 //-----------------------------------------------------------------------------
6274 
6276 {
6277  ryml::csubstr yml = ""
6278  "---" "\n"
6279  "a: 0" "\n"
6280  "b: 1" "\n"
6281  "---" "\n"
6282  "c: 2" "\n"
6283  "d: 3" "\n"
6284  "---" "\n"
6285  "- 4" "\n"
6286  "- 5" "\n"
6287  "- 6" "\n"
6288  "- 7" "\n"
6289  "";
6290  ryml::Tree tree = ryml::parse_in_arena(yml);
6291  CHECK(ryml::emitrs_yaml<std::string>(tree) == yml);
6292 
6293  // iteration through docs
6294  {
6295  // using the node API
6296  const ryml::ConstNodeRef stream = tree.rootref();
6297  CHECK(stream.is_root());
6298  CHECK(stream.is_stream());
6299  CHECK(!stream.is_doc());
6300  CHECK(stream.num_children() == 3);
6301  for(const ryml::ConstNodeRef doc : stream.children())
6302  CHECK(doc.is_doc());
6303  CHECK(tree.docref(0).id() == stream.child(0).id());
6304  CHECK(tree.docref(1).id() == stream.child(1).id());
6305  CHECK(tree.docref(2).id() == stream.child(2).id());
6306  // equivalent: using the lower level index API
6307  const ryml::id_type stream_id = tree.root_id();
6308  CHECK(tree.is_root(stream_id));
6309  CHECK(tree.is_stream(stream_id));
6310  CHECK(!tree.is_doc(stream_id));
6311  CHECK(tree.num_children(stream_id) == 3);
6312  for(ryml::id_type doc_id = tree.first_child(stream_id);
6313  doc_id != ryml::NONE;
6314  doc_id = tree.next_sibling(stream_id))
6315  CHECK(tree.is_doc(doc_id));
6316  CHECK(tree.doc(0) == tree.child(stream_id, 0));
6317  CHECK(tree.doc(1) == tree.child(stream_id, 1));
6318  CHECK(tree.doc(2) == tree.child(stream_id, 2));
6319 
6320  // using the node API
6321  CHECK(stream[0].is_doc());
6322  CHECK(stream[0].is_map());
6323  CHECK(stream[0]["a"].val() == "0");
6324  CHECK(stream[0]["b"].val() == "1");
6325  // equivalent: using the index API
6326  const ryml::id_type doc0_id = tree.first_child(stream_id);
6327  CHECK(tree.is_doc(doc0_id));
6328  CHECK(tree.is_map(doc0_id));
6329  CHECK(tree.val(tree.find_child(doc0_id, "a")) == "0");
6330  CHECK(tree.val(tree.find_child(doc0_id, "b")) == "1");
6331 
6332  // using the node API
6333  CHECK(stream[1].is_doc());
6334  CHECK(stream[1].is_map());
6335  CHECK(stream[1]["c"].val() == "2");
6336  CHECK(stream[1]["d"].val() == "3");
6337  // equivalent: using the index API
6338  const ryml::id_type doc1_id = tree.next_sibling(doc0_id);
6339  CHECK(tree.is_doc(doc1_id));
6340  CHECK(tree.is_map(doc1_id));
6341  CHECK(tree.val(tree.find_child(doc1_id, "c")) == "2");
6342  CHECK(tree.val(tree.find_child(doc1_id, "d")) == "3");
6343 
6344  // using the node API
6345  CHECK(stream[2].is_doc());
6346  CHECK(stream[2].is_seq());
6347  CHECK(stream[2][0].val() == "4");
6348  CHECK(stream[2][1].val() == "5");
6349  CHECK(stream[2][2].val() == "6");
6350  CHECK(stream[2][3].val() == "7");
6351  // equivalent: using the index API
6352  const ryml::id_type doc2_id = tree.next_sibling(doc1_id);
6353  CHECK(tree.is_doc(doc2_id));
6354  CHECK(tree.is_seq(doc2_id));
6355  CHECK(tree.val(tree.child(doc2_id, 0)) == "4");
6356  CHECK(tree.val(tree.child(doc2_id, 1)) == "5");
6357  CHECK(tree.val(tree.child(doc2_id, 2)) == "6");
6358  CHECK(tree.val(tree.child(doc2_id, 3)) == "7");
6359  }
6360 
6361  // Note: ryml emits streams as a JSON seq by default:
6362  CHECK(ryml::emitrs_json<std::string>(tree) ==
6363  "[" "\n"
6364  " {" "\n"
6365  " \"a\": 0," "\n"
6366  " \"b\": 1" "\n"
6367  " }," "\n"
6368  " {" "\n"
6369  " \"c\": 2," "\n"
6370  " \"d\": 3" "\n"
6371  " }," "\n"
6372  " [" "\n"
6373  " 4," "\n"
6374  " 5," "\n"
6375  " 6," "\n"
6376  " 7" "\n"
6377  " ]" "\n"
6378  "]\n"
6379  "");
6380  // ... but you can use EmitOptions{} to set the emitter to fail if
6381  // it finds a stream:
6382  {
6383  ScopedErrorHandlerExample errh; // calls ryml::set_callbacks()
6384  ryml::Tree err_tree = ryml::parse_in_arena(yml);
6385  CHECK(err_tree.callbacks() == errh.callbacks());
6386  auto err_opts = ryml::EmitOptions{}.json_err_on_stream(true);
6387  CHECK(errh.check_error_occurs([&]{
6388  return ryml::emitrs_json<std::string>(err_tree, err_opts);
6389  }));
6390  // in which case, you can avoid the error by emitting the
6391  // documents one-by-one:
6392  const std::string expected_json[] = {
6393  "{" "\n"
6394  " \"a\": 0," "\n"
6395  " \"b\": 1" "\n"
6396  "}" "\n"
6397  ,
6398  "{" "\n"
6399  " \"c\": 2," "\n"
6400  " \"d\": 3" "\n"
6401  "}" "\n"
6402  ,
6403  "[" "\n"
6404  " 4," "\n"
6405  " 5," "\n"
6406  " 6," "\n"
6407  " 7" "\n"
6408  "]" "\n"
6409  };
6410  ryml::id_type count = 0;
6411  const ryml::ConstNodeRef stream = err_tree;
6412  CHECK(stream.num_children() == (ryml::id_type)C4_COUNTOF(expected_json));
6413  for(ryml::ConstNodeRef doc : stream.children())
6414  CHECK(ryml::emitrs_json<std::string>(doc, err_opts) == expected_json[count++]);
6415  }
6416 }
6417 
6418 
6419 //-----------------------------------------------------------------------------
6420 
6421 // To avoid imposing a particular type of error handling, ryml uses
6422 // error handler callbacks. This enables users to use exceptions, or
6423 // setjmp()/longjmp(), or plain calls to abort(), as they see fit.
6424 //
6425 // However, it is important to note that the error callbacks must never
6426 // return to the caller! Otherwise, an infinite loop or program crash
6427 // will likely occur.
6428 //
6429 // For this reason, to recover from an error when exceptions are disabled,
6430 // then a non-local jump must be performed using setjmp()/longjmp().
6431 // The code below demonstrates both flows.
6432 //
6433 // ryml provides default error handlers, which call
6434 // std::abort(). You can use the cmake option and the macro
6435 // RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS to have the default error
6436 // handler throw an exception instead.
6437 
6438 /** demonstrates how to set a custom error handler for ryml */
6440 {
6441  ErrorHandlerExample errh; // browse the implementation of this
6442  // class to understand more details
6443  errh.check_disabled();
6444  // set the global error handlers. Note the error callbacks must
6445  // never return: they must either throw an exception, use setjmp()
6446  // and longjmp(), or abort. Otherwise, the parser will enter into
6447  // an infinite loop, or the program may crash.
6449  errh.check_enabled();
6450  CHECK(errh.check_error_occurs([&]{
6451  ryml::Tree tree = ryml::parse_in_arena("errorhandler.yml", "[a: b\n}");
6452  }));
6453  ryml::set_callbacks(errh.original_callbacks); // restore defaults.
6454  errh.check_disabled();
6455 }
6456 
6457 
6458 //-----------------------------------------------------------------------------
6459 
6461 {
6462  auto cause_basic_error = []{
6463  ryml::Tree t;
6464  ryml::csubstr tag_handle = {}, tag_prefix = {}; // invalid, not filled
6465  t.add_tag_directive(tag_handle, tag_prefix, 0);
6466  };
6467  {
6468  ScopedErrorHandlerExample errh; // set the example callbacks (scoped)
6469  CHECK(errh.check_error_occurs(cause_basic_error));
6470  }
6471 #ifdef RYML_WITH_EXCEPTIONS_
6472  bool gotit = false;
6473  try
6474  {
6475  cause_basic_error();
6476  }
6477  catch(ryml::ExceptionBasic const& exc)
6478  {
6479  gotit = true;
6480  ryml::csubstr msg = ryml::to_csubstr(exc.what());
6482  CHECK(!msg.empty());
6483  }
6484  CHECK(gotit);
6485 #endif
6486 }
6487 
6488 
6490 {
6491  ryml::csubstr ymlsrc = ""
6492  "{" "\n"
6493  " a: b" "\n"
6494  " [" "\n"
6495  "";
6496  ryml::csubstr ymlfile = "file.yml";
6497  auto cause_parse_error = [&]{
6498  return ryml::parse_in_arena(ymlfile, ymlsrc);
6499  };
6500  // the YAML in ymlsrc must cause a parse error while it is being
6501  // parsed. We use our error handler to catch that error, and save
6502  // the error info:
6503  ErrorHandlerExample errh;
6504  {
6506  CHECK(errh.check_error_occurs(cause_parse_error));
6507  // the handler in errh saves the error info in itself. Let's
6508  // use that to see the messages we get.
6509  //
6510  // this message is the short message passed into the parse
6511  // error handler:
6512  CHECK(errh.saved_msg_short == "invalid character: '['");
6513  // this message was created inside the handler, by calling
6514  // ryml::err_parse_format():
6515  CHECK(ryml::to_csubstr(errh.saved_msg_full).begins_with("file.yml:3: col=4 (12B): ERROR: [parse] invalid character: '['"));
6516  // If you keep the YAML source buffer around, you can also use
6517  // it to create/print a larger error message showing the
6518  // YAML source code context which causes the error:
6519  std::string msg_ctx = errh.saved_msg_full + "\n";
6521  msg_ctx.append(s.str, s.len);
6522  }, errh.saved_parse_loc, ymlsrc, "err");
6523  CHECK(ryml::to_csubstr(msg_ctx).begins_with("file.yml:3: col=4 (12B): ERROR: [parse] invalid character: '['"));
6524  CHECK(ryml::to_csubstr(msg_ctx).ends_with(
6525  "file.yml:3: col=4 (12B): err:" "\n"
6526  "err:" "\n"
6527  "err: [" "\n"
6528  "err: |" "\n"
6529  "err: (here)" "\n"
6530  "err:" "\n"
6531  "err: see region:" "\n"
6532  "err:" "\n"
6533  "err: {" "\n"
6534  "err: a: b" "\n"
6535  "err: [" "\n"
6536  "err: |" "\n"
6537  "err: (here)" "\n"
6538  ""));
6539  //
6540  // Let's now check the location (see the message above):
6541  CHECK(errh.saved_parse_loc.name == ymlfile);
6542  CHECK(errh.saved_parse_loc.line == 3);
6543  CHECK(errh.saved_parse_loc.col == 4);
6544  CHECK(errh.saved_parse_loc.offset == 12);
6545  CHECK(errh.saved_parse_loc.offset <= ymlsrc.len);
6546  // ... and this is the location in the ryml source code file where
6547  // this error was found:
6548  CHECK(!errh.saved_basic_loc.name.empty());
6549  CHECK(errh.saved_basic_loc.line > 0);
6550  CHECK(errh.saved_basic_loc.col > 0);
6551  CHECK(errh.saved_basic_loc.offset > 0);
6553  }
6554  // A parse error is also a basic error. If no parse error handler
6555  // is set, then ryml falls back to a basic error:
6556  {
6557  ryml::Callbacks cb = errh.callbacks();
6558  cb.m_error_parse = nullptr;
6559  ryml::set_callbacks(cb);
6560  CHECK(ryml::get_callbacks().m_error_parse == nullptr);
6561  CHECK(errh.check_error_occurs(cause_parse_error));
6562  // we got a basic error instead of a parse error:
6563  CHECK(errh.saved_msg_short == "invalid character: '['");
6564  // notice that the full message now displays this as a basic
6565  // error:
6566  CHECK(errh.saved_msg_full == "file.yml:3: col=4 (12B): ERROR: [basic] invalid character: '['");
6567  // the yml location is now in the location saved from the basic error
6568  CHECK(errh.saved_basic_loc.name == ymlfile);
6569  CHECK(errh.saved_basic_loc.line == 3);
6570  CHECK(errh.saved_basic_loc.col == 4);
6571  CHECK(errh.saved_basic_loc.offset == 12);
6572  CHECK(errh.saved_basic_loc.offset <= ymlsrc.len);
6578  }
6579 #ifdef RYML_WITH_EXCEPTIONS_
6580  bool gotit = false;
6581  try
6582  {
6583  cause_parse_error();
6584  }
6585  catch(ryml::ExceptionParse const& exc)
6586  {
6587  gotit = true;
6588  ryml::csubstr msg = ryml::to_csubstr(exc.what());
6589  CHECK(exc.errdata_parse.ymlloc.name == ymlfile);
6590  CHECK(exc.errdata_parse.ymlloc.line == 3);
6591  CHECK(exc.errdata_parse.ymlloc.col == 4);
6592  CHECK(exc.errdata_parse.ymlloc.offset == 12);
6593  CHECK(exc.errdata_parse.ymlloc.offset <= ymlsrc.len);
6594  // the message saved in the exception is just the concrete error description:
6595  CHECK(msg == "invalid character: '['");
6596  // to print richer error messages, ryml provides helpers to
6597  // format that description into a complete error message,
6598  // containing location and source context indication:
6599  std::string full;
6600  auto dumpfn = [&full](ryml::csubstr s) { full.append(s.str, s.len); };
6601  ryml::err_parse_format(dumpfn, msg, exc.errdata_parse);
6602  full += '\n';
6603  ryml::location_format_with_context(dumpfn, exc.errdata_parse.ymlloc, ymlsrc, "err", 3);
6604  CHECK(ryml::to_csubstr(full).begins_with("file.yml:3: col=4 (12B): ERROR: [parse] invalid character: '['"));
6605  CHECK(ryml::to_csubstr(full).ends_with(
6606  "file.yml:3: col=4 (12B): err:" "\n"
6607  "err:" "\n"
6608  "err: [" "\n"
6609  "err: |" "\n"
6610  "err: (here)" "\n"
6611  "err:" "\n"
6612  "err: see region:" "\n"
6613  "err:" "\n"
6614  "err: {" "\n"
6615  "err: a: b" "\n"
6616  "err: [" "\n"
6617  "err: |" "\n"
6618  "err: (here)" "\n"
6619  ""));
6620  }
6621  CHECK(gotit);
6622  gotit = false;
6623  try
6624  {
6625  cause_parse_error();
6626  }
6627  catch(ryml::ExceptionBasic const& exc) // use references! don't slice the exception
6628  {
6629  gotit = true;
6630  ryml::csubstr msg = ryml::to_csubstr(exc.what());
6632  CHECK(!msg.empty());
6633  }
6634  CHECK(gotit);
6635 #endif
6636 }
6637 
6638 
6639 /** Visit errors happen when an error is triggered while reading from
6640  * a node. */
6642 {
6643  ryml::csubstr ymlfile = "file.yml";
6644  ryml::csubstr ymlsrc = "float: 123.456";
6645  ErrorHandlerExample errh;
6646  {
6648  ryml::Tree tree = ryml::parse_in_arena(ymlfile, ymlsrc);
6649  CHECK(errh.check_error_occurs([&]{
6650  int intval = 0;
6651  tree["float"].load(&intval); // cannot deserialize 123.456 to int
6652  }));
6653  // the handler in errh saves the error info in itself. Let's
6654  // use that to see the messages we get.
6655  //
6656  // this message is the short message passed into the visit error
6657  CHECK(errh.saved_msg_short == "could not deserialize node");
6658  // this message was created inside the handler, by calling
6659  // ryml::err_visit_format():
6660  CHECK(ryml::csubstr::npos != ryml::to_csubstr(errh.saved_msg_full).find("ERROR: [visit] could not deserialize node"));
6661  // The location of the visit error is of the C++ source file where
6662  // the error was detected -- NOT of the YAML source file:
6663  CHECK(errh.saved_basic_loc.name != ymlfile);
6664  // However, note that the tree and node id are available:
6665  CHECK(errh.saved_visit_tree == &tree);
6666  CHECK(errh.saved_visit_id == tree["float"].id());
6667  // see sample_error_visit_location() for an example on how
6668  // to extract the location.
6669  }
6670  // visit errors also fall back to basic errors when the visit
6671  // handler is not set (similar to the behavior of ExceptionVisit):
6672  {
6673  ryml::Callbacks cb = errh.callbacks();
6674  cb.m_error_visit = nullptr;
6675  ryml::set_callbacks(cb);
6676  CHECK(ryml::get_callbacks().m_error_visit == nullptr);
6677  ryml::Tree tree = ryml::parse_in_arena(ymlfile, ymlsrc);
6678  CHECK(errh.check_error_occurs([&]{
6679  int intval = 0;
6680  tree["float"].load(&intval); // cannot deserialize 123.456 to int
6681  }));
6682  // we got a basic error instead of a visit error:
6683  CHECK(errh.saved_msg_short == "could not deserialize node");
6684  // notice that the full message now displays this as a basic
6685  // error:
6686  CHECK(ryml::csubstr::npos != ryml::to_csubstr(errh.saved_msg_full).find("ERROR: [basic] could not deserialize node"));
6687  // the tree and id are not set, because this was called as a basic error
6688  CHECK(errh.saved_visit_tree == nullptr);
6689  CHECK(errh.saved_visit_id == ryml::NONE);
6691  }
6692 #ifdef RYML_WITH_EXCEPTIONS_
6693  // when using the default ryml callbacks (see
6694  // RYML_NO_DEFAULT_CALLBACKS), and
6695  // RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, the ryml
6696  // parse handler throws an exception of type ryml::ExceptionVisit,
6697  // which is derived from ryml::ExceptionBasic.
6698  {
6699  const ryml::Tree tree = ryml::parse_in_arena(ymlfile, ymlsrc);
6700  bool gotit = false;
6701  try
6702  {
6703  int intval = 0;
6704  tree["float"].load(&intval); // cannot deserialize 123.456 to int
6705  }
6706  catch(ryml::ExceptionVisit const& exc)
6707  {
6708  gotit = true;
6709  ryml::csubstr msg = ryml::to_csubstr(exc.what());
6711  CHECK(exc.errdata_visit.tree == &tree);
6712  CHECK(exc.errdata_visit.node == tree["float"].id());
6713  CHECK(!msg.empty());
6714  }
6715  CHECK(gotit);
6716  }
6717  // you can also catch the exception as its base,
6718  // ryml::ExceptionBasic:
6719  {
6720  const ryml::Tree tree = ryml::parse_in_arena(ymlfile, ymlsrc);
6721  bool gotit = false;
6722  try
6723  {
6724  int intval = 0;
6725  tree["float"].load(&intval); // cannot deserialize 123.456 to int
6726  }
6727  catch(ryml::ExceptionBasic const& exc) // use references! don't slice the exception
6728  {
6729  gotit = true;
6730  ryml::csubstr msg = ryml::to_csubstr(exc.what());
6732  CHECK(!msg.empty());
6733  }
6734  CHECK(gotit);
6735  }
6736 #endif
6737 }
6738 
6739 
6740 /** It is possible to obtain the YAML location from a visit
6741  * error: when the tree is obtained from parsing YAML, the messages
6742  * may be enriched by using a parser set to track the locations. See
6743  * @ref sample_location_tracking() for more details on how to use
6744  * locations. */
6746 {
6748  // we will use locations to show the YAML source context of the
6749  // node where the visit error was triggered. This is a very
6750  // convenient feature to show detailed messages when deserializing
6751  // data read from a file (but do note this is opt-in, and it is
6752  // not mandatory). See sample_location_tracking() for more details
6753  // on location tracking.
6755  ryml::EventHandlerTree evt_handler{};
6756  ryml::Parser parser(&evt_handler, opts);
6757  ryml::csubstr ymlfile = "file.yml";
6758  ryml::csubstr ymlsrc = ""
6759  "foo: bar" "\n"
6760  "char: a" "\n"
6761  "int: a" "\n"
6762  "float: 123.456" "\n"
6763  "";
6764  const ryml::Tree tree = ryml::parse_in_arena(&parser, ymlfile, ymlsrc);
6765  // This function will cause a visit error when being called:
6766  auto cause_visit_error = [&]{
6767  int intval = 0;
6768  tree["float"].load(&intval); // cannot deserialize 123.456 to int
6769  };
6770  // Like with the parse error, we will use our error handler to
6771  // catch that visit error, and save the error info:
6772  CHECK(evt_handler.callbacks() == errh.callbacks());
6773  CHECK(parser.callbacks() == errh.callbacks());
6774  CHECK(tree.callbacks() == errh.callbacks());
6775  {
6776  CHECK(errh.check_error_occurs(cause_visit_error));
6777  // the handler in errh saves the error info in itself. Let's
6778  // use that to see the messages we get.
6779  //
6780  // this message is the short message passed into the visit error
6781  CHECK(errh.saved_msg_short == "could not deserialize node");
6782  // this message was created inside the handler, by calling
6783  // ryml::err_visit_format():
6784  CHECK(ryml::csubstr::npos != ryml::to_csubstr(errh.saved_msg_full).find("ERROR: [visit] could not deserialize node"));
6785  // The location of the visit error is of the C++ source file where
6786  // the error was detected -- NOT of the YAML source file:
6787  CHECK(errh.saved_basic_loc.name != ymlfile);
6788  // However, note that the tree and node id are available:
6789  CHECK(errh.saved_visit_tree == &tree);
6790  CHECK(errh.saved_visit_id == tree["float"].id());
6791  // ... which we can use to get the location in the YAML source
6792  // from the parser (but see @ref sample_location_tracking()):
6793  ryml::Location ymlloc = errh.saved_visit_tree->location(parser, errh.saved_visit_id);
6794  CHECK(ymlloc.name == ymlfile);
6795  // In turn, we can use format_location_context() to
6796  // print/create an error message pointing at the YAML source
6797  // code:
6798  std::string msg = errh.saved_msg_full;
6800  msg.append(s.str, s.len);
6801  }, ymlloc, ymlsrc, "err", /*number of lines to show before the error*/3);
6802  CHECK(ryml::to_csubstr(msg).ends_with(
6803  "file.yml:3: col=0 (24B): err:" "\n"
6804  "err:" "\n"
6805  "err: float: 123.456" "\n"
6806  "err: |" "\n"
6807  "err: (here)" "\n"
6808  "err:" "\n"
6809  "err: see region:" "\n"
6810  "err:" "\n"
6811  "err: foo: bar" "\n"
6812  "err: char: a" "\n"
6813  "err: int: a" "\n"
6814  "err: float: 123.456" "\n"
6815  "err: |" "\n"
6816  "err: (here)" "\n"
6817  ""));
6818  }
6819 }
6820 
6821 
6822 //-----------------------------------------------------------------------------
6823 
6824 // Please note the following about the use of custom allocators with
6825 // ryml. Due to [the static initialization order
6826 // fiasco](https://en.cppreference.com/w/cpp/language/siof), if you
6827 // use static ryml trees or parsers, you need to make sure that their
6828 // callbacks have the same lifetime. So you can't use ryml's default
6829 // callbacks structure, as it is declared in a ryml file, and the standard
6830 // provides no guarantee on the relative initialization order, such
6831 // that it is constructed before and destroyed after your
6832 // variables. In fact you are pretty much guaranteed to see this
6833 // fail. So please carefully consider your choices, and ponder
6834 // whether you really need to use ryml static trees and parsers. If
6835 // you do need this, then you will need to declare and use a ryml
6836 // callbacks structure that outlives the tree and/or parser.
6837 //
6838 // See also sample_static_trees() for an example on how to use
6839 // trees with static lifetime.
6840 
6841 /** @addtogroup doc_quickstart_helpers
6842  * @{ */
6844 {
6845  std::vector<char> memory_pool = std::vector<char>(10u * 1024u); // 10KB
6846  size_t num_allocs = 0, alloc_size = 0, corr_size = 0;
6847  size_t num_deallocs = 0, dealloc_size = 0;
6848 
6849  void *allocate(size_t len)
6850  {
6851  void *ptr = &memory_pool[alloc_size];
6852  alloc_size += len;
6853  ++num_allocs;
6854  // ensure the ptr is aligned
6855  uintptr_t uptr = (uintptr_t)ptr;
6856  const uintptr_t align = alignof(max_align_t);
6857  if (uptr % align)
6858  {
6859  uintptr_t prev = uptr - (uptr % align);
6860  uintptr_t next = prev + align;
6861  uintptr_t corr = next - uptr;
6862  ptr = (void*)(((char*)ptr) + corr);
6863  corr_size += corr;
6864  }
6865  C4_CHECK_MSG(alloc_size + corr_size <= memory_pool.size(),
6866  "out of memory! requested=%zu+%zu available=%zu\n",
6867  alloc_size, corr_size, memory_pool.size());
6868  return ptr;
6869  }
6870 
6871  void free(void *mem, size_t len)
6872  {
6873  CHECK((char*)mem >= &memory_pool.front() && (char*)mem < &memory_pool.back());
6874  CHECK((char*)mem+len >= &memory_pool.front() && (char*)mem+len <= &memory_pool.back());
6875  dealloc_size += len;
6876  ++num_deallocs;
6877  // no need to free here
6878  }
6879 
6880  // bridge
6882  {
6883  ryml::Callbacks cb = {};
6884  return cb.set_user_data(this)
6887  }
6888  static void* s_allocate(size_t len, void* /*hint*/, void *this_)
6889  {
6890  return ((GlobalAllocatorExample*)this_)->allocate(len);
6891  }
6892  static void s_free(void *mem, size_t len, void *this_)
6893  {
6894  ((GlobalAllocatorExample*)this_)->free(mem, len);
6895  }
6896 
6897  // checking
6899  {
6900  check_and_reset();
6901  }
6903  {
6904  std::cout << "size: alloc=" << alloc_size << " dealloc=" << dealloc_size << std::endl;
6905  std::cout << "count: #allocs=" << num_allocs << " #deallocs=" << num_deallocs << std::endl;
6906  CHECK(num_allocs == num_deallocs);
6907  CHECK(alloc_size >= dealloc_size); // failure here means a double free
6908  CHECK(alloc_size == dealloc_size); // failure here means a leak
6909  num_allocs = 0;
6910  num_deallocs = 0;
6911  alloc_size = 0;
6912  dealloc_size = 0;
6913  }
6914 };
6915 /** @} */
6916 
6917 
6918 /** demonstrates how to set the global allocator for ryml */
6920 {
6922 
6923  // save the existing callbacks for restoring
6924  ryml::Callbacks defaults = ryml::get_callbacks();
6925 
6926  // set to our callbacks
6928 
6929  // verify that the allocator is in effect
6930  ryml::Callbacks const& current = ryml::get_callbacks();
6931  CHECK(current.m_allocate == &mem.s_allocate);
6932  CHECK(current.m_free == &mem.s_free);
6933 
6934  // so far nothing was allocated
6935  CHECK(mem.alloc_size == 0);
6936 
6937  // parse one tree and check
6938  (void)ryml::parse_in_arena(R"({foo: bar})");
6939  mem.check_and_reset();
6940 
6941  // parse another tree and check
6942  (void)ryml::parse_in_arena(R"([a, b, c, d, {foo: bar, money: pennys}])");
6943  mem.check_and_reset();
6944 
6945  // verify that by reserving we save allocations
6946  {
6947  ryml::EventHandlerTree evt_handler;
6948  ryml::Parser parser(&evt_handler); // reuse a parser
6949  ryml::Tree tree; // reuse a tree
6950 
6951  tree.reserve(10); // reserve the number of nodes
6952  tree.reserve_arena(100); // reserve the arena size
6953  parser.reserve_stack(10); // reserve the parser depth.
6954 
6955  // since the parser stack uses Small Storage Optimization,
6956  // allocations will only happen with capacities higher than 16.
6957  CHECK(mem.num_allocs == 2); // tree, tree_arena and NOT the parser
6958 
6959  parser.reserve_stack(20); // reserve the parser depth.
6960  CHECK(mem.num_allocs == 3); // tree, tree_arena and now the parser as well
6961 
6962  // verify that no other allocations occur when parsing
6963  size_t size_before = mem.alloc_size;
6964  parse_in_arena(&parser, "", R"([a, b, c, d, {foo: bar, money: pennys}])", &tree);
6965  CHECK(mem.alloc_size == size_before);
6966  CHECK(mem.num_allocs == 3);
6967  }
6968  mem.check_and_reset();
6969 
6970  // restore defaults.
6971  ryml::set_callbacks(defaults);
6972 }
6973 
6974 
6975 //-----------------------------------------------------------------------------
6976 
6977 /** @addtogroup doc_quickstart_helpers
6978  * @{ */
6979 
6980 /** an example for a per-tree memory allocator */
6982 {
6983  std::vector<char> memory_pool = std::vector<char>(10u * 1024u); // 10KB
6984  size_t num_allocs = 0, alloc_size = 0;
6985  size_t num_deallocs = 0, dealloc_size = 0;
6986 
6988  {
6989  // Above we used static functions to bridge to our methods.
6990  // To show a different approach, we employ lambdas here.
6991  // Note that there can be no captures in the lambdas
6992  // because these are C-style function pointers.
6993  ryml::Callbacks cb;
6994  cb.m_user_data = (void*) this;
6995  cb.m_allocate = [](size_t len, void *, void *data){ return ((PerTreeMemoryExample*) data)->allocate(len); };
6996  cb.m_free = [](void *mem, size_t len, void *data){ return ((PerTreeMemoryExample*) data)->free(mem, len); };
6997  return cb;
6998  }
6999 
7000  void *allocate(size_t len)
7001  {
7002  void *ptr = &memory_pool[alloc_size];
7003  alloc_size += len;
7004  ++num_allocs;
7005  if C4_UNLIKELY(alloc_size > memory_pool.size())
7006  {
7007  std::cerr << "out of memory! requested=" << alloc_size << " vs " << memory_pool.size() << " available" << std::endl; // LCOV_EXCL_LINE
7008  std::abort(); // LCOV_EXCL_LINE
7009  }
7010  return ptr;
7011  }
7012 
7013  void free(void *mem, size_t len)
7014  {
7015  CHECK((char*)mem >= &memory_pool.front() && (char*)mem < &memory_pool.back());
7016  CHECK((char*)mem+len >= &memory_pool.front() && (char*)mem+len <= &memory_pool.back());
7017  dealloc_size += len;
7018  ++num_deallocs;
7019  // no need to free here
7020  }
7021 
7022  // checking
7024  {
7025  check_and_reset();
7026  }
7028  {
7029  std::cout << "size: alloc=" << alloc_size << " dealloc=" << dealloc_size << std::endl;
7030  std::cout << "count: #allocs=" << num_allocs << " #deallocs=" << num_deallocs << std::endl;
7031  CHECK(num_allocs == num_deallocs);
7032  CHECK(alloc_size >= dealloc_size); // failure here means a double free
7033  CHECK(alloc_size == dealloc_size); // failure here means a leak
7034  num_allocs = 0;
7035  num_deallocs = 0;
7036  alloc_size = 0;
7037  dealloc_size = 0;
7038  }
7039 };
7040 /** @} */
7041 
7043 {
7047 
7048  // the trees will use the memory in the resources above,
7049  // with each tree using a separate resource
7050  {
7051  // Watchout: ensure that the lifetime of the callbacks target
7052  // exceeds the lifetime of the tree.
7053  ryml::EventHandlerTree evt_handler(mrp.callbacks());
7054  ryml::Parser parser(&evt_handler);
7055  ryml::Tree tree1(mr1.callbacks());
7056  ryml::Tree tree2(mr2.callbacks());
7057 
7058  ryml::csubstr yml1 = "{a: b}";
7059  ryml::csubstr yml2 = "{c: d, e: f, g: [h, i, 0, 1, 2, 3]}";
7060 
7061  parse_in_arena(&parser, "file1.yml", yml1, &tree1);
7062  parse_in_arena(&parser, "file2.yml", yml2, &tree2);
7063  }
7064 
7065  CHECK(mrp.num_allocs == 0); // YAML depth not large enough to warrant a parser allocation
7066  CHECK(mr1.alloc_size <= mr2.alloc_size); // because yml2 has more nodes
7067 }
7068 
7069 
7070 //-----------------------------------------------------------------------------
7071 
7072 
7073 /** shows how to work around the static initialization order fiasco
7074  * when using a static-duration ryml tree
7075  * @see https://en.cppreference.com/w/cpp/language/siof */
7077 {
7078  // Static trees may incur a static initialization order
7079  // problem. This happens because a default-constructed tree will
7080  // obtain the callbacks from the current global setting, which may
7081  // not have been initialized due to undefined static
7082  // initialization order:
7083  //
7084  // ERROR! depends on ryml::get_callbacks() which may not have been initialized.
7085  //static ryml::Tree tree;
7086  //
7087  // To work around the issue, declare static callbacks
7088  // to explicitly initialize the static tree:
7089  static ryml::Callbacks callbacks = default_callbacks(); // use default callback members
7090  static ryml::Tree tree(callbacks); // OK
7091  // now you can use the tree as normal:
7092  ryml::parse_in_arena(R"(doe: "a deer, a female deer")", &tree);
7093  CHECK(tree["doe"].val() == "a deer, a female deer");
7094 }
7095 
7096 
7097 //-----------------------------------------------------------------------------
7098 //-----------------------------------------------------------------------------
7099 //-----------------------------------------------------------------------------
7100 
7101 
7102 /** @addtogroup doc_quickstart_helpers
7103  * @{ */
7104 
7105 namespace /*anon*/ {
7106 static int num_checks = 0;
7107 static int num_failed_checks = 0;
7108 static bool quiet_mode = false;
7109 } // namespace /*anon*/
7110 
7111 void handle_args(int argc, const char* argv[])
7112 {
7113  auto arg_matches = [](const char *arg, const char *shortform, const char *longform) {
7114  return (0 == strcmp(arg, shortform) || 0 == strcmp(arg, longform));
7115  };
7116  for(int i = 1; i < argc; ++i)
7117  if(arg_matches(argv[i], "-q", "--quiet"))
7118  quiet_mode = true;
7119 }
7120 
7121 bool report_check(int line, const char *predicate, bool result)
7122 {
7123  ++num_checks;
7124  const char *msg = predicate ? "OK! " : "OK!";
7125  if(!result)
7126  {
7127  ++num_failed_checks; // LCOV_EXCL_LINE
7128  msg = predicate ? "FAIL: " : "FAIL"; // LCOV_EXCL_LINE
7129  }
7130  if(!result || !quiet_mode)
7131  {
7132  std::cout << __FILE__ << ':' << line << ": " << msg << (predicate ? predicate : "") << std::endl;
7133  }
7134  return result;
7135 }
7136 
7137 
7139 {
7140  std::cout << "Completed " << num_checks << " checks." << std::endl;
7141  if(num_failed_checks)
7142  std::cout << "ERROR: " << num_failed_checks << '/' << num_checks << " checks failed." << std::endl; // LCOV_EXCL_LINE
7143  else
7144  std::cout << "SUCCESS!" << std::endl;
7145  return num_failed_checks;
7146 }
7147 
7148 
7149 //-----------------------------------------------------------------------------
7150 // methods to provide default callbacks (needed when
7151 // RYML_NO_DEFAULT_CALLBACKS is defined)
7152 
7153 namespace {
7154 // LCOV_EXCL_START
7155 /** dump (part of an) error message to terminal
7156  * @ingroup doc_quickstart_helpers */
7157 void errdump(ryml::csubstr s)
7158 {
7159  if(s.len)
7160  fwrite(s.str, 1, s.len, stderr); // NOLINT
7161 }
7162 /** finish printing an error message, and flush
7163  * @ingroup doc_quickstart_helpers */
7164 void errend()
7165 {
7166  fputc('\n', stderr); // NOLINT
7167  fflush(NULL); // NOLINT
7168 }
7169 // LCOV_EXCL_STOP
7170 } // namespace
7171 
7172 
7173 /** set up a bare-bones implementation of the callbacks
7174  * @ingroup doc_quickstart_helpers */
7176 {
7177  return ryml::Callbacks{}
7178  .set_allocate([](size_t len, void* , void *){
7179  return malloc(len); // NOLINT
7180  })
7181  .set_free([](void* mem, size_t, void *){
7182  free(mem); // NOLINT
7183  })
7184  ///
7185  /// The default error callbacks won't be called in this
7186  /// quickstart, because no errors are expected. But we
7187  /// implement them here to show how a bare-bones implementation
7188  /// looks like, and also because they are needed when
7189  /// @ref RYML_NO_DEFAULT_CALLBACKS is defined.
7190  ///
7191  /// For a different (more involved) implementation of the error
7192  /// callbacks, see the implementation of @ref ErrorHandlerExample
7193  /// below.
7194  ///
7195  // LCOV_EXCL_START
7196  .set_error_basic([](ryml::csubstr msg, ryml::ErrorDataBasic const& errdata, void *){
7197  ryml::err_basic_format(errdump, msg, errdata); // format the message, printing to stderr
7198  errend(); // print newline and flush
7199  abort(); // abort (must never return: abort, or exception or setjmp)
7200  })
7201  .set_error_parse([](ryml::csubstr msg, ryml::ErrorDataParse const& errdata, void *){
7202  ryml::err_parse_format(errdump, msg, errdata); // format the message, printing to out
7203  errend(); // print newline and flush
7204  abort(); // abort (must never return: abort, or exception or setjmp)
7205  })
7206  .set_error_visit([](ryml::csubstr msg, ryml::ErrorDataVisit const& errdata, void *){
7207  ryml::err_visit_format(errdump, msg, errdata); // format the message, printing to out
7208  errend(); // print newline and flush
7209  abort(); // abort (must never return: abort, or exception or setjmp)
7210  });
7211  // LCOV_EXCL_STOP
7212 }
7213 
7214 /** set up default callbacks when ryml does not provide them
7215  * (ie when @ref RYML_NO_DEFAULT_CALLBACKS is defined)
7216  * @ingroup doc_quickstart_helpers */
7218 {
7219 #ifdef RYML_NO_DEFAULT_CALLBACKS
7221 #endif
7222 }
7223 
7224 
7225 //-----------------------------------------------------------------------------
7226 // methods for the example error handler
7227 
7228 #ifndef C4_EXCEPTIONS
7229 /*environment for setjmp*/
7230 static std::jmp_buf s_jmp_env;
7231 static std::string s_jmp_msg;
7232 #endif
7233 
7234 /** checking that an assertion occurs while calling fn. assertions are
7235  * enabled if @ref RYML_USE_ASSERT is defined.
7236  * @ingroup doc_quickstart_helpers */
7237 template<class Fn>
7239 {
7240  #if RYML_USE_ASSERT
7241  return check_error_occurs(std::forward<Fn>(fn));
7242  #else
7243  (void)fn; // do nothing otherwise, as there would be undefined behavior
7244  return true;
7245  #endif
7246 }
7247 /** checking that an error occurs while calling fn
7248  * @ingroup doc_quickstart_helpers */
7249 template<class Fn>
7251 {
7252  RYML_SAVE_TEST_EXPFAIL_(); // a dev helper to dump all tests
7253  saved_msg_short.clear();
7254  saved_msg_full.clear();
7256  saved_basic_loc = {};
7257  saved_parse_loc = {};
7258  saved_visit_tree = {};
7260  bool got_error = false;
7261  #ifdef C4_EXCEPTIONS
7262  try
7263  {
7264  std::forward<Fn>(fn)();
7265  }
7266  catch(std::exception const&)
7267  {
7268  got_error = true;
7269  }
7270  #else
7271  if(setjmp(s_jmp_env) == 0)
7272  {
7273  std::forward<Fn>(fn)();
7274  }
7275  else
7276  {
7277  got_error = true;
7278  }
7279  #endif
7280  return got_error;
7281 }
7282 
7283 namespace {
7284 /** interrupt execution
7285  * @ingroup doc_quickstart_helpers */
7286 [[noreturn]] void stopexec(std::string const& s)
7287 {
7288  #ifdef C4_EXCEPTIONS
7289  throw std::runtime_error(s);
7290  #else
7291  s_jmp_msg = s;
7292  std::longjmp(s_jmp_env, 1); // jump to the corresponding call to setjmp().
7293  #endif
7294 }
7295 } // namespace
7296 /** this is where the callback implementation goes. Remember that it must not return.
7297  * @ingroup doc_quickstart_helpers
7298  * */
7300 {
7301  saved_msg_short.assign(msg.str, msg.len);
7302  // build a full error message with location
7304  saved_msg_full.append(s.str, s.len);
7305  }, msg, errdata);
7306  // Save the error params for subsequent testing in the quickstart.
7308  saved_basic_loc = errdata.location;
7309  stopexec(saved_msg_short);
7310 }
7311 /** this is where the callback implementation goes. Remember that it must not return.
7312  * @ingroup doc_quickstart_helpers
7313  * @see ryml::format_location_context
7314  * */
7316 {
7317  saved_msg_short.assign(msg.str, msg.len);
7318  // build a full error message with location
7320  saved_msg_full.append(s.str, s.len);
7321  }, msg, errdata);
7322  // Save the error params for subsequent testing in the quickstart.
7323  //
7324  // To add the source context, the source buffer is required. If
7325  // the caller is interested in enriching the full message with the
7326  // source buffer context, he can ensure that the source buffer is
7327  // kept, and then arrange the handler to access it.
7328  // For now, we assign the full message without context:
7330  saved_basic_loc = errdata.cpploc;
7331  saved_parse_loc = errdata.ymlloc;
7332  stopexec(saved_msg_full);
7333 }
7334 /** this is where the callback implementation goes. Remember that it must not return.
7335  * @ingroup doc_quickstart_helpers
7336  * */
7338 {
7339  saved_msg_short.assign(msg.str, msg.len);
7340  // build a full error message with location
7342  saved_msg_full.append(s.str, s.len);
7343  }, msg, errdata);
7344  // Save the error params for subsequent testing in the quickstart.
7345  //
7346  // To add the source context, the source buffer is required. If
7347  // the caller is interested in enriching the full message with the
7348  // source buffer context, he can ensure that the source buffer is
7349  // kept, and then arrange the handler to access it.
7350  // For now, we assign the full message without context:
7352  saved_basic_loc = errdata.cpploc;
7353  saved_visit_tree = errdata.tree;
7354  saved_visit_id = errdata.node;
7355  stopexec(saved_msg_full);
7356 }
7357 
7358 /** trampoline function to call the object's method */
7359 [[noreturn]] void ErrorHandlerExample::s_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const& errdata, void *this_)
7360 {
7361  static_cast<ErrorHandlerExample*>(this_)->on_error_basic(msg, errdata);
7362 }
7363 /** trampoline function to call the object's method */
7364 [[noreturn]] void ErrorHandlerExample::s_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const& errdata, void *this_)
7365 {
7366  static_cast<ErrorHandlerExample*>(this_)->on_error_parse(msg, errdata);
7367 }
7368 /** trampoline function to call the object's method */
7369 [[noreturn]] void ErrorHandlerExample::s_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const& errdata, void *this_)
7370 {
7371  static_cast<ErrorHandlerExample*>(this_)->on_error_visit(msg, errdata);
7372 }
7373 
7374 
7375 /** a helper to create the Callbacks object for the custom error handler
7376  * @ingroup doc_quickstart_helpers
7377  * */
7379 {
7381  copy.set_user_data(this)
7385  return copy;
7386 }
7387 
7388 /** test that this handler is currently set */
7390 {
7391  ryml::Callbacks const& current = ryml::get_callbacks();
7392  CHECK(current.m_error_basic == &s_error_basic);
7393  CHECK(current.m_error_parse == &s_error_parse);
7394  CHECK(current.m_error_visit == &s_error_visit);
7396  CHECK(current.m_free == original_callbacks.m_free);
7397 }
7398 /** test that this handler is currently not set */
7400 {
7401  ryml::Callbacks const& current = ryml::get_callbacks();
7402  CHECK(current.m_error_basic != &s_error_basic);
7403  CHECK(current.m_error_parse != &s_error_parse);
7404  CHECK(current.m_error_visit != &s_error_visit);
7406  CHECK(current.m_free == original_callbacks.m_free);
7407 }
7408 
7409 
7410 /** @} */ // doc_quickstart_helpers
7411 
7412 
7413 C4_SUPPRESS_WARNING_GCC_CLANG_POP
void parse_json_in_arena(Parser *parser, csubstr filename, csubstr json, Tree *tree, id_type node_id)
(1) parse JSON into an existing tree node. The filename will be used in any error messages arising du...
Definition: parse.cpp:231
csubstr const & key(id_type node) const
Definition: tree.hpp:454
csubstr const & val(id_type node) const
Definition: tree.hpp:460
print the real number in hexadecimal format (like a)
Definition: charconv.hpp:200
bool is_seed() const noexcept
true if the object is not invalid and in seed state.
Definition: node.hpp:1136
void on_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const &errdata)
this is where the callback implementation goes.
bool indent_flow_ml() const noexcept
Indent the contents of FLOW_ML1 and FLOW_MLN containers.
void set_val(id_type node, csubstr val) RYML_NOEXCEPT
Definition: tree.hpp:688
void sample_fundamental_types()
serialize/deserialize fundamental types
mark val scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:156
basic_substring trimr(const C c) const
trim the character c from the right
Definition: substr.hpp:653
void sample_style_flow_ml_indent()
control indentation of FLOW_ML1 and FLOW_MLN containers
bool invalid() const noexcept
true if the object is not referring to any existing or seed node.
Definition: node.hpp:1134
bool is_key_plain() const RYML_NOEXCEPT
Forward to Tree::is_key_plain().
Definition: node.hpp:262
bool scalar_is_null(csubstr s) noexcept
YAML-sense query of nullity.
ConstNodeRef last_sibling() const RYML_NOEXCEPT
Forward to Tree::last_sibling().
Definition: node.hpp:861
bool is_doc(id_type node) const
Definition: tree.hpp:478
ryml::Location saved_basic_loc
Definition: quickstart.cpp:359
bool begins_with_any(ro_substr chars) const noexcept
true if the first character of the string is any of the given chars
Definition: substr.hpp:883
basic_substring trim(const C c) const
trim the character c left and right
Definition: substr.hpp:677
bool is_super(ro_substr const that) const noexcept
true if that is a substring of *this (ie, from the same buffer)
Definition: substr.hpp:484
void check_disabled() const
test that this handler is currently not set
Location cpploc
location in the C++ source file where the error was detected.
Definition: common.hpp:270
void sample_user_container_types()
serialize/deserialize container (map or seq) types
Callbacks & set_error_parse(pfn_error_parse error_parse=nullptr)
Set or reset the error_parse callback.
Definition: common.cpp:177
example user container type: map-like
an index to none
Definition: common.hpp:131
void load_key(id_type node, T *k, bool check_readable=true) const
(1) deserialize the node's key (necessarily a scalar) to the given variable, forwarding to the user-o...
Definition: tree.hpp:912
id_type first_child(id_type node) const
Definition: tree.hpp:577
csubstr formatrs_append(CharOwningContainer *cont, csubstr fmt, Args const &...args)
format+resize+append: like c4::format(), but receives a container, and appends the arguments...
Definition: format.hpp:1330
basic_substring stripr(ro_substr pattern) const
remove a pattern from the right
Definition: substr.hpp:699
void sample_error_visit_location()
obtaining the YAML location from a visit error
bool is_map() const RYML_NOEXCEPT
Forward to Tree::is_map().
Definition: node.hpp:207
static void s_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const &errdata, void *this_)
trampoline function to call the object's method
void sample_error_basic()
handler for basic errors, and obtain a full error message with basic context
void check_enabled() const
test that this handler is currently set
mark key scalar as double quoted "
Definition: node_type.hpp:153
size_t offset
number of bytes from the beginning of the source buffer
Definition: common.hpp:230
bool overlaps(ro_substr const that) const noexcept
true if there is overlap of at least one element between that and *this
Definition: substr.hpp:493
my_map_type< int, int > map
A lightweight truthy type, used to enable reporting the offending node when a deserializing error hap...
Definition: common.hpp:161
Callbacks & set_allocate(pfn_allocate allocate=nullptr)
Set or reset the allocate callback.
Definition: common.cpp:150
ParserOptions & detect_flow_ml(bool enabled) noexcept
enable/disable detection of flow multiline container style.
!!float Mathematical integers.
Definition: tag.hpp:45
substr copy_to_arena(csubstr s)
copy the given string to the tree's arena, growing the arena by the required size.
Definition: tree.hpp:1370
void catseprs(CharOwningContainer *cont, Sep const &sep, Args const &...args)
catsep+resize: like c4::catsep(), but receives a container, and resizes it as needed to contain the r...
Definition: format.hpp:1152
void * allocate(size_t len)
vec3< int > v3
id_type num_siblings() const RYML_NOEXCEPT
O(num_children).
Definition: node.hpp:303
bool is_stream(id_type node) const
Definition: tree.hpp:477
size_t uncatsep(csubstr buf, csubstr sep, Arg &a, Args &...more)
deserialize the arguments from the given buffer, using a separator.
Definition: format.hpp:819
Callbacks & set_free(pfn_free free=nullptr)
Set or reset the free callback.
Definition: common.cpp:159
mark val scalar as single quoted '
Definition: node_type.hpp:152
ryml::Callbacks callbacks()
a helper to create the Callbacks object for the custom error handler
void location_format_with_context(DumpFn &&dumpfn, Location const &location, csubstr source_buffer, csubstr call, size_t num_lines_before, size_t num_lines_after, size_t first_col_highlight, size_t last_col_highlight, size_t maxlen)
Generic formatting of a location, printing the source code buffer region around the location...
Definition: error.def.hpp:86
Location location(Parser const &parser) const
Definition: node.hpp:318
void normalize_tags_long()
Definition: tree.cpp:1578
bool is_seq() const RYML_NOEXCEPT
Forward to Tree::is_seq().
Definition: node.hpp:208
example scalar type, serialized and deserialized
ConstNodeRef parent() const RYML_NOEXCEPT
Forward to Tree::parent().
Definition: node.hpp:853
bool check_assertion_occurs(Fn &&fn)
checking that an assertion occurs while calling fn.
size_t col
column
Definition: common.hpp:232
bool is_root() const RYML_NOEXCEPT
Forward to Tree::is_root().
Definition: node.hpp:278
void set_map(id_type node) RYML_NOEXCEPT
Definition: tree.hpp:731
ConstNodeRef last_child() const RYML_NOEXCEPT
Forward to Tree::last_child().
Definition: node.hpp:855
void sample_emit_nested_node()
pick a nested node as the root when emitting
void sample_error_parse()
handler for parse errors, and obtain a full error message with parse context
print the real number in flexible format (like g)
Definition: charconv.hpp:198
substr emitrs_yaml(Tree const &t, id_type id, EmitOptions const &opts, CharOwningContainer *cont, bool append=false)
(1) emit+resize: emit YAML to the given std::string/std::vector-like container, resizing it as needed to fit the emitted YAML.
void * allocate(size_t len)
void sample_lightning_overview()
lightning overview of most common features
Definition: quickstart.cpp:409
void reserve_stack(id_type capacity)
Reserve a certain capacity for the parsing stack.
example scalar type, serialized only
Shows how to create a scoped error handler.
Definition: quickstart.cpp:368
The event handler to create a ryml Tree.
print the real number in scientific format (like e)
Definition: charconv.hpp:196
size_t to_chars(ryml::substr buf, vec2< T > v)
size_t unformat(csubstr buf, csubstr fmt, Arg &a, Args &...more)
using a format string, deserialize the arguments from the given buffer.
Definition: format.hpp:987
id_type id() const noexcept
Definition: node.hpp:822
children_view children() RYML_NOEXCEPT
get an iterable view over children
Definition: node.hpp:1843
bool is_flow_mln() const noexcept
Definition: node_type.hpp:325
void set_callbacks(Callbacks const &c)
set the global callbacks for the library; after a call to this function, these callbacks will be used...
Definition: common.cpp:89
csubstr name
name of the file
Definition: common.hpp:233
substr format_sub(substr buf, csubstr fmt, Args const &...args)
like c4::format() but return a substr instead of a size
Definition: format.hpp:956
C * str
a restricted pointer to the first character of the substring
Definition: substr.hpp:216
size_t first_of(const C c, size_t start=0) const
Definition: substr.hpp:934
example scalar type, serialized and deserialized
provides type-safe facilities for formatting arguments to string buffers
an example for a per-tree memory allocator
void sample_substr()
about ryml's string views (from c4core)
A c-style callbacks class to customize behavior on errors or allocation.
Definition: common.hpp:373
Callbacks & set_user_data(void *user_data)
Set the user data.
Definition: common.cpp:144
Exception thrown by the default visit error implementation.
Definition: error.hpp:510
void sample_anchors_and_aliases_create()
how to create YAML anchors and aliases
size_t format(substr buf, csubstr fmt, Arg const &a, Args const &...more)
Using a format string, serialize the arguments into the given fixed-size buffer.
Definition: format.hpp:936
void sample_parse_in_arena()
parse a read-only YAML source buffer
void sample_iterate_tree()
visit individual nodes and iterate through trees
Exception thrown by the default basic error implementation.
Definition: error.hpp:474
vec4< int > v4
auto fill(C val) -> typename std::enable_if< !std::is_const< U >::value, void >::type
fill the entire contents with the given val
Definition: substr.hpp:2153
void set_val_ref(id_type node, csubstr ref)
Definition: tree.hpp:748
bool ends_with(const C c) const noexcept
true if the last character of the string is c
Definition: substr.hpp:894
id_type first_sibling(id_type node) const
Definition: tree.hpp:592
void check_eq(my_seq_type const &that) const
void load(id_type node, T *v, bool check_readable=true) const
(1) deserialize the node's contents (val or container) to the given variable, forwarding to the user-...
Definition: tree.hpp:871
bool has_key() const RYML_NOEXCEPT
Forward to Tree::has_key().
Definition: node.hpp:210
Inner inner[2]
substr to_substr(char(&s)[N]) noexcept
Definition: substr.hpp:2376
void sample_create_tree_style()
set node styles while creating trees
const char * str
Definition: node_type.cpp:70
void sample_parse_reuse_parser()
reuse an existing parser
ryml::Location saved_parse_loc
Definition: quickstart.cpp:360
integral_< intptr_t > hex(std::nullptr_t)
format null as an hexadecimal value
Definition: format.hpp:160
mask of CONTAINER_STYLE_FLOW|CONTAINER_STYLE_BLOCK : all container style flags
Definition: node_type.hpp:182
void load(T *v, bool check_readable=true) const
(1) deserialize the node's contents (val or container) to the given variable, forwarding to the user-...
Definition: node.hpp:345
void check_eq(my_type const &that) const
basic_substring right_of(size_t pos) const noexcept
return [pos+1, len[
Definition: substr.hpp:574
mark val scalar as double quoted "
Definition: node_type.hpp:154
Location ymlloc
location in the YAML source buffer where the error was detected.
Definition: common.hpp:271
bool is_doc() const RYML_NOEXCEPT
Forward to Tree::is_doc().
Definition: node.hpp:205
std::string saved_msg_full
Definition: quickstart.cpp:357
void catrs(CharOwningContainer *cont, Args const &...args)
cat+resize: like c4::cat(), but receives a container, and resizes it as needed to contain the result...
Definition: format.hpp:1040
auto replace(C value, C repl, size_t pos=0) -> typename std::enable_if< !std::is_const< U >::value, size_t >::type
replace every occurrence of character value with the character repl
Definition: substr.hpp:2272
bool is_map(id_type node) const
Definition: tree.hpp:480
void sample_tags()
deal with YAML type tags
std::map< K, V > map_member
bool invalid() const noexcept
Definition: node.hpp:790
const_raw_wrapper craw(cblob data, size_t alignment=alignof(max_align_t))
mark a variable to be written in raw binary format, using memcpy
Definition: format.hpp:412
void sample_serialize_basic()
serialize/deserialize fundamental types
csubstr arena() const
get the current arena
Definition: tree.hpp:1317
void sample_parse_style()
shows how rapidyaml retains the style of parsed YAML
size_t cat(substr buf, Arg const &a, Args const &...more)
serialize the arguments, concatenating them to the given fixed-size buffer.
Definition: format.hpp:649
std::vector< char > memory_pool
Accelerator structure to reduce memory requirements by enabling reuse of resolved tags...
Definition: tag.hpp:70
ConstNodeRef next_sibling() const RYML_NOEXCEPT
Forward to Tree::next_sibling().
Definition: node.hpp:859
ErrorDataVisit errdata_visit
Definition: error.hpp:513
void set_key_serialized(id_type node, T const &key) RYML_NOEXCEPT
Definition: tree.hpp:837
print the real number in floating point format (like f)
Definition: charconv.hpp:194
Data for a parse error.
Definition: common.hpp:268
basic_substring first(size_t num) const noexcept
return the first num elements: [0,num[
Definition: substr.hpp:529
mark container with multi-line flow style, n elements per line, < wrapped (as set by EmitOptions::max...
Definition: node_type.hpp:90
void clear_arena()
Definition: tree.hpp:340
void sample_per_tree_allocator()
set per-tree allocators
std::vector< T > seq_member
void sample_docs()
deal with YAML docs
id_type doc(id_type i) const
gets the i document node index.
Definition: tree.hpp:605
Location cpploc
location in the C++ source file where the error was detected.
Definition: common.hpp:280
Tree const * tree
tree where the error was detected
Definition: common.hpp:281
void sample_tree_arena()
interact with the tree's serialization arena
void sample_std_types()
serialize/deserialize STL containers
id_type num_children() const RYML_NOEXCEPT
O(num_children).
Definition: node.hpp:302
void err_visit_format(DumpFn &&dumpfn, csubstr msg, ErrorDataVisit const &errdata)
Given an error message and associated visit error data, format it fully as a visit error message...
Definition: error.def.hpp:350
serializes as a map
C * data() noexcept
Definition: substr.hpp:366
static void s_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const &errdata, void *this_)
trampoline function to call the object's method
ReadResult deserialize(T *v) const
(1) deserialize the node's contents (val or container) to the given variable, forwarding to the user-...
Definition: node.hpp:428
size_t last_not_of(const C c) const
Definition: substr.hpp:1014
real_< T > real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT)
Definition: format.hpp:362
void set_val(csubstr val)
Definition: node.hpp:1251
example scalar type, serialized and deserialized
example scalar type, deserialized only
void set_key_tag(id_type node, csubstr tag)
Definition: tree.hpp:742
void parse_in_arena(Parser *parser, csubstr filename, csubstr yaml, Tree *tree, id_type node_id)
(1) parse YAML into an existing tree node. The filename will be used in any error messages arising du...
Definition: parse.cpp:209
void sample_parse_reuse_tree_and_parser()
how to reuse existing trees and parsers
ConstNodeRef first_sibling() const RYML_NOEXCEPT
Forward to Tree::first_sibling().
Definition: node.hpp:860
id_type root_id() const
Get the id of the root node. The tree must not be empty. The tree can be empty only when constructed ...
Definition: tree.hpp:386
ConstNodeRef first_child() const RYML_NOEXCEPT
Forward to Tree::first_child().
Definition: node.hpp:854
ErrorDataBasic errdata_basic
error data
Definition: error.hpp:478
bool from_chars(ryml::csubstr buf, vec2< T > *v)
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
Definition: node_type.hpp:34
size_t last_of(const C c, size_t start=npos) const
Definition: substr.hpp:946
example scalar type, serialized only
ConstNodeRef crootref() const
Get the root as a ConstNodeRef . Note that Tree implicitly converts to ConstNodeRef.
Definition: tree.cpp:65
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition: node.hpp:1062
id_type append_child(id_type parent)
create and insert a node as the last child of parent
Definition: tree.hpp:1180
Location location
location where the error was detected (may be from YAML or C++ source code)
Definition: common.hpp:261
ParserOptions const & options() const
Get the options used to build this parser object.
size_t arena_capacity() const
get the current capacity of the tree's internal arena
Definition: tree.hpp:1311
integral_< intptr_t > bin(std::nullptr_t)
format null as a binary 0-1 value
Definition: format.hpp:212
csubstr from_tag(YamlTag_e tag)
Definition: tag.cpp:170
ryml::id_type saved_visit_id
Definition: quickstart.cpp:362
Callbacks & set_error_basic(pfn_error_basic error_basic=nullptr)
Set or reset the error_basic callback.
Definition: common.cpp:168
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
Definition: common.hpp:124
size_t size() const noexcept
Definition: substr.hpp:358
const char * what() const noexcept override
Definition: error.hpp:477
basic_substring stripl(ro_substr pattern) const
remove a pattern from the left
Definition: substr.hpp:690
Options to give to the ParseEngine to control its behavior.
void set_val_anchor(id_type node, csubstr anchor)
Definition: tree.hpp:746
ryml::Callbacks original_callbacks
Definition: quickstart.cpp:334
ReadResult deserialize_child(csubstr child_key, T *v) const
(1) find a child by name and deserialize its contents to the given variable (ie call ...
Definition: node.hpp:485
integral_< intptr_t > oct(std::nullptr_t)
format null as an octal value
Definition: format.hpp:185
void resolve(ReferenceResolver *rr, bool clear_anchors=true)
Resolve references (aliases <- anchors), by forwarding to ReferenceResolver::resolve(); refer to Refe...
Definition: tree.cpp:1206
mark val scalar as multiline, block folded >
Definition: node_type.hpp:150
id_type num_children(id_type node) const
O(num_children)
Definition: tree.cpp:1216
void sample_style()
query/set node styles
std::string saved_msg_full_with_context
Definition: quickstart.cpp:358
void sample_user_container_types_brief()
serialize/deserialize container (map or seq) types: brief version
mark key scalar as multiline, block folded >
Definition: node_type.hpp:149
void reserve_locations(size_t num_source_lines)
Reserve a certain capacity for the array used to track node locations in the source buffer...
void sample_create_tree()
programatically create trees
auto reverse() -> typename std::enable_if< !std::is_const< U >::value, void >::type
reverse in place
Definition: substr.hpp:2199
csubstr normalize_tag_long(csubstr tag)
Definition: tag.cpp:31
size_t count(const C c, size_t pos=0) const
count the number of occurrences of c
Definition: substr.hpp:744
void * m_user_data
data to be forwarded in every call to a callback
Definition: common.hpp:375
auto reverse_sub(size_t ifirst, size_t num) -> typename std::enable_if< !std::is_const< U >::value, void >::type
revert a subpart in place
Definition: substr.hpp:2209
void reserve_arena(size_t arena_cap=RYML_DEFAULT_TREE_ARENA_CAPACITY)
ensure the tree's internal string arena is at least the given capacity
Definition: tree.hpp:1409
bool in_arena(csubstr s) const
return true if the given substring is part of the tree's string arena
Definition: tree.hpp:1327
basic_substring sub(size_t first) const noexcept
return [first,len[
Definition: substr.hpp:502
bool is_seq(id_type node) const
Definition: tree.hpp:481
void clear_style(id_type node, bool recurse=false)
Definition: tree.cpp:1404
bool is_block() const RYML_NOEXCEPT
Forward to Tree::is_block().
Definition: node.hpp:242
csubstr from_tag_long(YamlTag_e tag)
Definition: tag.cpp:130
pfn_error_parse m_error_parse
a pointer to a parse error handler function
Definition: common.hpp:379
void sample_formatting()
control formatting when serializing/deserializing
void save(T const &k)
Definition: node.hpp:1337
Callbacks const & callbacks() const
Definition: tree.hpp:349
csubstr to_csubstr(const char(&s)[N]) noexcept
Definition: substr.hpp:2380
id_type id() const noexcept
Definition: node.hpp:1208
EmitOptions & emit_nonroot_dash(bool enabled) noexcept
When emit starts on a nested (non-root) node, emit a leading dash.
void sample_static_trees()
how to use static trees in ryml
csubstr normalize_tag(csubstr tag)
Definition: tag.cpp:19
EmitOptions & force_flow_spc(bool enabled) noexcept
Force everywhere a space after comma in flow mode, overriding the FLOW_SPC status of individual conta...
int report_checks()
bool is_stream() const RYML_NOEXCEPT
Forward to Tree::is_stream().
Definition: node.hpp:204
my_seq_type< int > seq
Parser parser
Definition: parse.cpp:117
basic_substring< char > substr
a mutable string view
Definition: substr.hpp:2355
a null string position
Definition: common.hpp:138
basic_substring< const char > csubstr
an immutable string view
Definition: substr.hpp:2356
substr cat_sub(substr buf, Args const &...args)
like c4::cat() but return a substr instead of a size
Definition: format.hpp:659
static void s_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const &errdata, void *this_)
trampoline function to call the object's method
bool readable() const noexcept
because a ConstNodeRef cannot be used to write to the tree, readable() has the same meaning as !inval...
Definition: node.hpp:793
pfn_free m_free
a pointer to a free handler function
Definition: common.hpp:377
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition: common.hpp:228
void sample_anchors_and_aliases()
deal with YAML anchors and aliases
ryml::Callbacks callbacks() const
csubstr catrs_append(CharOwningContainer *cont, Args const &...args)
cat+resize+append: like c4::cat(), but receives a container, and appends to it instead of overwriting...
Definition: format.hpp:1109
ReadResult deserialize(id_type node, T *v) const
(1) deserialize a node's contents to a variable
Definition: tree.hpp:954
ParserOptions & resolve_tags(bool enabled) noexcept
enable/disable resolution of YAML tags during parsing.
#define CHECK(predicate)
a testing assertion, used only in this quickstart
Definition: quickstart.cpp:310
example scalar type, deserialized only
id_type child(id_type node, id_type pos) const
find child by position, or NONE if there are less than pos children posi
Definition: tree.cpp:1237
Data for a visit error.
Definition: common.hpp:278
mark container with block style < - seqs as <
Definition: node_type.hpp:136
ConstNodeRef prev_sibling() const RYML_NOEXCEPT
Forward to Tree::prev_sibling().
Definition: node.hpp:858
Definitions of error utilities used by ryml.
basic_substring offs(size_t left, size_t right) const noexcept
offset from the ends: return [left,len-right[ ; ie, trim a number of characters from the left and rig...
Definition: substr.hpp:547
auto toupper() -> typename std::enable_if< !std::is_const< U >::value, void >::type
convert the string to upper-case
Definition: substr.hpp:2127
Definition: ryml.hpp:6
Holds a pointer to an existing tree, and a node id.
Definition: node.hpp:736
the scalar to the left of : in a map's member
Definition: node_type.hpp:33
void sample_parse_file()
ready-to-go example of parsing a file from disk
void add_tag_directive(csubstr handle, csubstr prefix, id_type id)
Definition: tree.cpp:1444
static std::jmp_buf s_jmp_env
basic_substring range(size_t first, size_t last=npos) const noexcept
return [first,last[.
Definition: substr.hpp:519
void sample_location_tracking()
track node YAML source locations in the parsed tree
mask of KEYQUO|KEY_PLAIN : all the key scalar styles for key (not container styles!) ...
Definition: node_type.hpp:176
basic_substring triml(const C c) const
trim left
Definition: substr.hpp:629
id_type next_sibling(id_type node) const
Definition: tree.hpp:572
void set_serialized(id_type node, T const &val) RYML_NOEXCEPT
Definition: tree.hpp:823
pfn_error_basic m_error_basic
a pointer to a basic error handler function
Definition: common.hpp:378
!!set Unordered set of non-equal values.
Definition: tag.hpp:39
ConstNodeRef child(id_type pos) const RYML_NOEXCEPT
Forward to Tree::child().
Definition: node.hpp:856
auto reverse_range(size_t ifirst, size_t ilast) -> typename std::enable_if< !std::is_const< U >::value, void >::type
revert a range in place
Definition: substr.hpp:2221
void sample_parse_in_place()
parse a mutable YAML source buffer
EmitOptions & max_cols(id_type cols) noexcept
Set max columns for the emitted YAML in FLOW_MLN mode.
ParserOptions & locations(bool enabled) noexcept
enable/disable source location tracking.
size_t len
the length of the substring
Definition: substr.hpp:218
void set_key_style(type_bits style)
Definition: node.hpp:1268
basic_substring select(const C c, size_t pos=0) const
get the substr consisting of the first occurrence of c after pos, or an empty substr if none occurs ...
Definition: substr.hpp:772
substr emit_yaml(Tree const &t, EmitOptions const &opts, substr buf, bool error_on_excess)
(1) emit YAML to the given buffer.
Definition: emit_buf.cpp:28
csubstr catseprs_append(CharOwningContainer *cont, Sep const &sep, Args const &...args)
catsep+resize+append: like c4::catsep(), but receives a container, and appends the arguments...
Definition: format.hpp:1220
void sample_float_precision()
control precision of serialized floats
void sample_tag_directives()
deal with YAML tag namespace directives
mark container with spaces after comma when in flow mode. < Applies to both FLOW_SL and FLOW_MLN (but...
Definition: node_type.hpp:132
size_t catsep(substr buf, Sep const &sep, Arg const &a, Args const &...more)
serialize the arguments, concatenating them to the given fixed-size buffer, using a separator between...
Definition: format.hpp:774
example user container type with nested user types.
void set_style_conditionally(id_type node, NodeType type_mask, NodeType rem_style_flags, NodeType add_style_flags, bool recurse=false)
Definition: tree.cpp:1414
void formatrs(CharOwningContainer *cont, csubstr fmt, Args const &...args)
format+resize: like c4::format(), but receives a container, and resizes it as needed to contain the r...
Definition: format.hpp:1263
NodeRef rootref()
Get the root as a NodeRef . Note that a non-const Tree implicitly converts to NodeRef.
Definition: tree.cpp:56
This is the main driver of parsing logic: it scans the YAML or JSON source for tokens, and emits the appropriate sequence of parsing events to its event handler.
Definition: fwd.hpp:17
pfn_error_visit m_error_visit
a pointer to a visit error handler function
Definition: common.hpp:380
Exception thrown by the default parse error implementation.
Definition: error.hpp:493
id_type node
node where the error was detected
Definition: common.hpp:282
void normalize_tags()
Definition: tree.cpp:1571
A lightweight object containing options to be used when emitting.
pfn_allocate m_allocate
a pointer to an allocate handler function
Definition: common.hpp:376
void sample_global_allocator()
set a global allocator for ryml
void write(ryml::NodeRef &n, Inner const &inner)
serializes as a seq
Utilities for formatting data as base64.
!!map Unordered set of key: value pairs without duplicates.
Definition: tag.hpp:36
void on_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const &errdata)
this is where the callback implementation goes.
boolalpha_ boolalpha(T const &val=false)
tag function to mark a variable to be written as an alphabetic boolean, ie as either true or false ...
Definition: format.hpp:70
example scalar type, serialized only
std::string saved_msg_short
Definition: quickstart.cpp:356
void set_container_style(type_bits style)
Definition: node.hpp:1267
bool is_val_plain() const RYML_NOEXCEPT
Forward to Tree::is_val_plain().
Definition: node.hpp:263
Callbacks const & get_callbacks()
get the global callbacks
Definition: common.cpp:94
auto tolower() -> typename std::enable_if< !std::is_const< U >::value, void >::type
convert the string to lower-case
Definition: substr.hpp:2139
void sample_parse_reuse_tree()
parse into an existing tree, maybe into a node
bool empty() const noexcept
Definition: substr.hpp:356
void sample_emit_to_container()
emit to memory, eg a string or vector-like container
mark val scalar as multiline, block literal |
Definition: node_type.hpp:148
!!seq Sequence of arbitrary values.
Definition: tag.hpp:40
ryml::Callbacks callbacks()
void set_key_anchor(id_type node, csubstr anchor)
Definition: tree.hpp:745
csubstr val() const RYML_NOEXCEPT
Forward to Tree::val().
Definition: node.hpp:179
void sample_style_flow_formatting()
control formatting of flow containers
bool check_error_occurs(Fn &&fn)
checking that an error occurs while calling fn
substr alloc_arena(size_t sz)
grow the tree's string arena by the given size and return a substr of the added portion ...
Definition: tree.hpp:1397
void resolve_tags(TagCache &cache, bool all=true)
Resolve tags in the tree such as "!!str" -> "", "!foo" -> "" and custom ...
Definition: tree.cpp:1553
const_base64_wrapper base64(csubstr s, size_t *reqsize=nullptr)
a tag function to mark a csubstr payload to be encoded in base64 format
void ensure_callbacks()
set up default callbacks when ryml does not provide them (ie when RYML_NO_DEFAULT_CALLBACKS is define...
void on_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const &errdata)
this is where the callback implementation goes.
Callbacks & set_error_visit(pfn_error_visit error_visit=nullptr)
Set or reset the error_visit callback.
Definition: common.cpp:186
NodeRef docref(id_type i)
get the i-th document of the stream
Definition: tree.cpp:104
csubstr location_contents(Location const &loc) const
Get the string starting at a particular location, to the end of the parsed source buffer...
void sample_user_scalar_types()
serialize/deserialize scalar (leaf/scalar) types
const_children_view children() const RYML_NOEXCEPT
get an iterable view over children
Definition: node.hpp:987
static void * s_allocate(size_t len, void *, void *this_)
Callbacks const & callbacks() const
Get the current callbacks in the parser.
basic_substring last(size_t num) const noexcept
return the last num elements: [len-num,len[
Definition: substr.hpp:536
void sample_quick_overview()
quick overview of most common features
Definition: quickstart.cpp:464
void free(void *mem, size_t len)
size_t find(const C c, size_t start_pos=0) const
Definition: substr.hpp:713
example scalar type, deserialized only
void set_key(id_type node, csubstr key) RYML_NOEXCEPT
Definition: tree.hpp:705
ryml::ReadResult read(ryml::ConstNodeRef const &n, Inner *inner)
NodeRef append_child()
Definition: node.hpp:1715
static std::string s_jmp_msg
void clear()
clear the tree and zero every node
Definition: tree.cpp:330
void set_val_ref(csubstr val_ref)
Definition: node.hpp:1265
ReadResult deserialize_key(id_type node, T *v) const
(1) deserialize a node's key to a variable
Definition: tree.hpp:975
bool is_root(id_type node) const
Definition: tree.hpp:529
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition: node_type.hpp:35
ParserOptions & flow_ml_style(NodeType style) noexcept
choose the default style of multiline flow containers, when a container is detected as flow multiline...
bool is_val() const RYML_NOEXCEPT
Forward to Tree::is_val().
Definition: node.hpp:211
void parse_in_place(Parser *parser, csubstr filename, substr yaml, Tree *tree, id_type node_id)
(1) parse YAML into an existing tree node.
Definition: parse.cpp:165
Data for a basic error.
Definition: common.hpp:259
example user container type: seq-like
EmitOptions & emit_nonroot_key(bool enabled) noexcept
When emit starts on a nested (non-root) node, emit the node's key as well.
an error handler used by some of the quickstart examples.
Definition: quickstart.cpp:331
int main(int argc, const char *argv[])
Definition: quickstart.cpp:240
void set_serialized(T const &v)
serialize a variable to this node.
Definition: node.hpp:1375
void sample_base64()
encode/decode base64
void sample_empty_null_values()
serialize/deserialize/query empty or null values
size_t first_not_of(const C c) const
Definition: substr.hpp:993
mark key scalar as single quoted '
Definition: node_type.hpp:151
vec2< int > v2
ReadResult deserialize_child(id_type node, csubstr child_key, T *v) const
(1) find a child by name and deserialize its contents to the given variable (ie call ...
Definition: tree.hpp:1011
void sample_emit_to_stream()
emit to a stream, eg std::ostream
void set_key_serialized(T const &k)
serialize a variable, then assign the result to the node's key
Definition: node.hpp:1396
void reserve(id_type node_capacity=RYML_DEFAULT_TREE_CAPACITY)
Definition: tree.cpp:292
id_type id(NodeData const *n) const
get the id of a node belonging to this tree. n can be nullptr, in which case NONE is returned n must ...
Definition: tree.hpp:392
YamlTag_e to_tag(csubstr tag)
Definition: tag.cpp:68
!!str A sequence of zero or more Unicode characters.
Definition: tag.hpp:48
void err_parse_format(DumpFn &&dumpfn, csubstr msg, ErrorDataParse const &errdata)
Given an error message and associated parse error data, format it fully as a parse error message...
Definition: error.def.hpp:331
substr catsep_sub(substr buf, Args &&...args)
like c4::catsep() but return a substr instead of a size
Definition: format.hpp:786
bool begins_with(const C c) const noexcept
true if the first character of the string is c
Definition: substr.hpp:850
C & back() noexcept
Definition: substr.hpp:375
mark container with multi-line flow style, 1 element per line < - seqs as <
Definition: node_type.hpp:75
bool ends_with_any(ro_substr chars) const noexcept
true if the last character of the string is any of the given chars
Definition: substr.hpp:922
void handle_args(int argc, const char *argv[])
static void s_free(void *mem, size_t len, void *this_)
id_type size() const
Definition: tree.hpp:345
integral_padded_< T > zpad(T val, size_t num_digits)
pad the argument with zeroes on the left, with decimal radix
Definition: format.hpp:232
ErrorDataParse errdata_parse
Definition: error.hpp:496
mask of VALQUO|VAL_PLAIN : all the val scalar styles for val (not container styles!) ...
Definition: node_type.hpp:177
EmitOptions & json_err_on_stream(bool enabled) noexcept
Whether to trigger an error (or emit as seq) when finding a stream in json mode.
void sample_error_visit()
handler for visit errors, and obtain a full error message with visit context
void set_val_style(type_bits style)
Definition: node.hpp:1269
void set_seq()
Definition: node.hpp:1254
void file_put_contents(void const *buf, size_t sz, FILE *file, const char *filename=nullptr)
save a contiguous buffer into a file
Definition: file.hpp:57
ryml::Tree const * saved_visit_tree
Definition: quickstart.cpp:361
a seq: a parent of VAL/SEQ/MAP nodes
Definition: node_type.hpp:36
void sample_deserialize_error()
shows error on deserializing nested nodes
void set_seq(id_type node) RYML_NOEXCEPT
Definition: tree.hpp:720
left_< T > left(T val, size_t width, char padchar=' ')
tag type to mark an argument to be aligned left.
Definition: format.hpp:515
void err_basic_format(DumpFn &&dumpfn, csubstr msg, ErrorDataBasic const &errdata)
Given an error message and associated basic error data, format it fully as a basic error message...
Definition: error.def.hpp:318
basic_substring left_of(size_t pos) const noexcept
return [0, pos[ .
Definition: substr.hpp:556
const_raw_wrapper raw(cblob data, size_t alignment=alignof(max_align_t))
mark a variable to be written in raw binary format, using memcpy
Definition: format.hpp:418
void sample_json()
JSON parsing and emitting.
std::vector< char > memory_pool
void sample_emit_to_file()
emit to a FILE*
void set_val_tag(id_type node, csubstr tag)
Definition: tree.hpp:743
id_type find_child(id_type node, csubstr const &key) const
find child by name, or NONE if no child is found with this key like Tree::child(), but return a ReadResult with the status
Definition: tree.cpp:1249
void sample_error_handler()
set custom error handlers
bool has_val() const RYML_NOEXCEPT
Forward to Tree::has_val().
Definition: node.hpp:209
void free(void *mem, size_t len)
void set_container_style(id_type node, type_bits style)
Definition: tree.hpp:658
right_< T > right(T val, size_t width, char padchar=' ')
tag function to mark an argument to be aligned right
Definition: format.hpp:553
csubstr to_arena(T const &a)
serialize the given variable to the tree's arena, growing it as needed to accomodate the serializatio...
Definition: tree.hpp:1349
tag type to mark a tree or node to be emitted as yaml when using operator<<, with options...
Location location(Parser const &p, id_type node) const
Get the location of a node from the parse used to parse this tree.
Definition: tree.cpp:1913
ryml::Callbacks default_callbacks()
set up a bare-bones implementation of the callbacks
bool is_sub(ro_substr const that) const noexcept
true if *this is a substring of that (ie, from the same buffer)
Definition: substr.hpp:478
mark container with single-line flow style < - seqs as <
Definition: node_type.hpp:56
bool report_check(int line, const char *predicate, bool result)
used by CHECK()
size_t uncat(csubstr buf, Arg &a, Args &...more)
deserialize the arguments from the given buffer.
Definition: format.hpp:690
NodeType type() const RYML_NOEXCEPT
Forward to Tree::type().
Definition: node.hpp:172
void set_style_conditionally(NodeType type_mask, NodeType rem_style_flags, NodeType add_style_flags, bool recurse=false)
Definition: node.hpp:1311
void save_key(id_type node, T const &key)
Definition: tree.hpp:794
size_t line
line
Definition: common.hpp:231
void check_eq(my_map_type const &that) const
csubstr key() const RYML_NOEXCEPT
Forward to Tree::key().
Definition: node.hpp:174
void set_map()
Definition: node.hpp:1257
void clear_style(bool recurse=false)
Definition: node.hpp:1305
void save(id_type node, T const &val)
Definition: tree.hpp:772