rapidyaml  0.13.0
parse and emit YAML, and do it fast
quickstart.cpp
Go to the documentation of this file.
1 // ryml: quickstart
2 
3 /** @addtogroup doc_quickstart
4  *
5  * This file does a quick tour of ryml. It has multiple self-contained
6  * and well-commented samples that illustrate how to use ryml, and how
7  * it works.
8  *
9  * Although this is not a unit test, the samples are written as a
10  * sequence of actions and predicate checks to better convey what is
11  * the expected result at any stage. And to ensure the code here is
12  * correct and up to date, it's also run as part of the CI tests.
13  *
14  * If something is unclear, please open an issue or send a pull
15  * request at https://github.com/biojppm/rapidyaml . If you have an
16  * issue while using ryml, it is also encouraged to try to reproduce
17  * the issue here, or look first through the relevant section.
18  *
19  * Happy ryml'ing!
20  *
21  *
22  * ### Note on use of raw strings
23  *
24  * This sample uses multiline C strings instead of C++11 R"(raw
25  * strings)" because the doxygen parser is badly broken when handling
26  * raw strings.
27  *
28  *
29  * ### Some guidance on building
30  *
31  * The directories that exist side-by-side with this file contain
32  * several examples on how to build this with cmake, such that you can
33  * hit the ground running. See [the relevant section of the main
34  * README](https://github.com/biojppm/rapidyaml/tree/v0.10.0?tab=readme-ov-file#quickstart-samples)
35  * for an overview of the different choices. I suggest starting first
36  * with the `add_subdirectory` example, treating it just like any
37  * other self-contained cmake project.
38  *
39  * Or very quickly, to build and run this sample on your PC, start by
40  * creating this `CMakeLists.txt`:
41  * ```cmake
42  * cmake_minimum_required(VERSION 3.13)
43  * project(ryml-quickstart LANGUAGES CXX)
44  * include(FetchContent)
45  * FetchContent_Declare(ryml
46  * GIT_REPOSITORY https://github.com/biojppm/rapidyaml.git
47  * GIT_TAG v0.13.0
48  * GIT_SHALLOW FALSE # ensure submodules are checked out
49  * )
50  * FetchContent_MakeAvailable(ryml)
51  * add_executable(ryml-quickstart ${ryml_SOURCE_DIR}/samples/quickstart.cpp)
52  * target_link_libraries(ryml-quickstart ryml::ryml)
53  * add_custom_target(run ryml-quickstart
54  * COMMAND $<TARGET_FILE:ryml-quickstart>
55  * DEPENDS ryml-quickstart
56  * COMMENT "running: $<TARGET_FILE:ryml-quickstart>")
57  * ```
58  * Now run the following commands in the same folder:
59  * ```bash
60  * # configure the project
61  * cmake -S . -B build
62  * # build and run
63  * cmake --build build --target ryml-quickstart -j
64  * # optionally, open in your IDE
65  * cmake --open build
66  * ```
67  *
68  * @{ */
69 
70 /** @cond dev */
71 void handle_args(int, const char*[]);
72 int report_checks();
73 void ensure_callbacks();
74 /** @endcond */
75 
76 
77 /** @defgroup doc_sample_helpers Sample helpers
78  *
79  * Helper utilities used in the sample.
80  */
81 
82 //-----------------------------------------------------------------------------
83 
84 // ryml can be used as a single header, or as a simple library:
85 #if defined(RYML_SINGLE_HEADER) // using the single header directly in the executable
86  #define RYML_SINGLE_HDR_DEFINE_NOW
87  #include <ryml_all.hpp>
88 #elif defined(RYML_SINGLE_HEADER_LIB) // using the single header from a library
89  #include <ryml_all.hpp>
90 #else
91  // umbrella header bringing all of ryml. for compilation speed,
92  // prefer using individual includes.
93  #include <ryml.hpp>
94  // ryml itself does not use any STL container, so <ryml_std.hpp>
95  // is needed if interop with std containers is desired.
96  // This is an umbrella include bringing all the interop
97  // headers implemented in ryml; again, for compilation speed,
98  // prefer using individual includes.
99  #include <ryml_std.hpp> // optional header, provided for std:: interop
100  #include <c4/format.hpp> // needed for the examples below
101  // optional header, definitions for error utilities to implement
102  // user-defined error callbacks:
103  #include <c4/yml/error.def.hpp>
104 #endif
105 
106 // these are needed for the examples below
107 #include <iostream>
108 #include <sstream>
109 #include <vector>
110 #include <map>
111 #ifdef C4_EXCEPTIONS
112 #include <stdexcept>
113 #else
114 #include <csetjmp>
115 #endif
116 
117 
118 //-----------------------------------------------------------------------------
119 
120 /** @cond dev */
121 // CONTENTS:
122 //
123 // (Each function addresses a topic and is fully self-contained. Jump
124 // to the function to find out about its topic.)
125 void sample_lightning_overview(); ///< lightning overview of most common features
126 void sample_quick_overview(); ///< quick overview of most common features
127 void sample_substr(); ///< about ryml's string views (from c4core)
128 void sample_parse_file(); ///< ready-to-go example of parsing a file from disk
129 void sample_parse_in_place(); ///< parse a mutable YAML source buffer
130 void sample_parse_in_arena(); ///< parse a read-only YAML source buffer
131 void sample_parse_reuse_tree(); ///< parse into an existing tree, maybe into a node
132 void sample_parse_reuse_parser(); ///< reuse an existing parser
133 void sample_parse_reuse_tree_and_parser(); ///< how to reuse existing trees and parsers
134 void sample_iterate_trees(); ///< visit individual nodes and iterate through trees
135 void sample_create_trees(); ///< programatically create trees
136 void sample_tree_arena(); ///< interact with the tree's serialization arena
137 void sample_fundamental_types(); ///< serialize/deserialize fundamental types
138 void sample_empty_null_values(); ///< serialize/deserialize/query empty or null values
139 void sample_formatting(); ///< control formatting when serializing/deserializing
140 void sample_base64(); ///< encode/decode base64
141 void sample_user_scalar_types(); ///< serialize/deserialize scalar (leaf/string) types
142 void sample_user_container_types(); ///< serialize/deserialize container (map or seq) types
143 void sample_std_types(); ///< serialize/deserialize STL containers
144 void sample_float_precision(); ///< control precision of serialized floats
145 void sample_emit_to_container(); ///< emit to memory, eg a string or vector-like container
146 void sample_emit_to_stream(); ///< emit to a stream, eg std::ostream
147 void sample_emit_to_file(); ///< emit to a FILE*
148 void sample_emit_nested_node(); ///< pick a nested node as the root when emitting
149 void sample_style(); ///< query/set node styles
150 void sample_style_flow_ml_indent(); ///< control indentation of FLOW_ML containers
151 void sample_style_flow_ml_filter(); ///< set the parser to pick FLOW_SL even if the container is multiline
152 void sample_json(); ///< JSON parsing and emitting
153 void sample_anchors_and_aliases(); ///< deal with YAML anchors and aliases
154 void sample_anchors_and_aliases_create(); ///< how to create YAML anchors and aliases
155 void sample_tags(); ///< deal with YAML type tags
156 void sample_tag_directives(); ///< deal with YAML tag namespace directives
157 void sample_docs(); ///< deal with YAML docs
158 void sample_error_handler(); ///< set custom error handlers
159 void sample_error_basic(); ///< handler for basic errors, and obtain a full error message with basic context
160 void sample_error_parse(); ///< handler for parse errors, and obtain a full error message with parse context
161 void sample_error_visit(); ///< handler for visit errors, and obtain a full error message with visit context
162 void sample_error_visit_location(); ///< obtaining the YAML location from a visit error
163 void sample_global_allocator(); ///< set a global allocator for ryml
164 void sample_per_tree_allocator(); ///< set per-tree allocators
165 void sample_static_trees(); ///< how to use static trees in ryml
166 void sample_location_tracking(); ///< track node YAML source locations in the parsed tree
167 
168 int main(int argc, const char* argv[])
169 {
170  handle_args(argc, argv);
174  sample_substr();
187  sample_base64();
196  sample_style();
199  sample_json();
202  sample_tags();
204  sample_docs();
214  return report_checks();
215 }
216 
217 /** @endcond */
218 
219 
220 //-----------------------------------------------------------------------------
221 //-----------------------------------------------------------------------------
222 //-----------------------------------------------------------------------------
223 
224 C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
225 C4_SUPPRESS_WARNING_GCC_CLANG("-Wcast-qual")
226 C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
227 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
228 
229 //-----------------------------------------------------------------------------
230 // first, some helpers used in this quickstart
231 
232 /** @addtogroup doc_sample_helpers
233  *
234  * @{ */
235 
236 
237 /** an example error handler, required for some of the quickstart
238  * examples.
239  * @ingroup doc_sample_helpers */
241 {
242  ErrorHandlerExample() : original_callbacks(ryml::get_callbacks()) {}
243  ryml::Callbacks original_callbacks; // saves the original callbacks
244 public:
245  // utilities used below
246  template<class Fn> bool check_error_occurs(Fn &&fn);
247  template<class Fn> bool check_assertion_occurs(Fn &&fn);
248  void check_enabled() const;
249  void check_disabled() const;
250  ryml::Callbacks callbacks();
251 public:
252  // these are the functions that we want to execute on error
253  [[noreturn]] void on_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const& errdata);
254  [[noreturn]] void on_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const& errdata);
255  [[noreturn]] void on_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const& errdata);
256 public:
257  // these are the trampoline functions that we set ryml to call
258  [[noreturn]] static void s_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const& errdata, void *this_);
259  [[noreturn]] static void s_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const& errdata, void *this_);
260  [[noreturn]] static void s_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const& errdata, void *this_);
261 public:
262  // the handlers save these fields to be able to check on them
263  // after the error is triggered. They are only valid after an
264  // error, and before the next error.
265  std::string saved_msg_short;
266  std::string saved_msg_full;
272 };
273 
274 
275 /** Shows how to create a scoped error handler.
276  * @ingroup doc_sample_helpers */
278 {
280  {
282  check_enabled();
283  }
285  {
287  check_disabled();
288  }
289 };
290 
291 
292 // needed to setup the callbacks when ryml does not provide them
293 void ensure_callbacks();
295 
296 
297 // helper functions for sample_parse_file()
298 template<class CharContainer> CharContainer file_get_contents(const char *filename);
299 template<class CharContainer> size_t file_get_contents(const char *filename, CharContainer *v);
300 template<class CharContainer> void file_put_contents(const char *filename, CharContainer const& v, const char* access="wb");
301 void file_put_contents(const char *filename, const char *buf, size_t sz, const char* access);
302 
303 
304 bool report_check(int line, const char *predicate, bool result);
305 
306 
307 #if defined(__DOXYGEN__) || defined(_DOXYGEN_)
308 /// a quick'n'dirty assertion to verify a predicate
309 # define CHECK(predicate) assert(predicate) // enable doxygen to link to the functions called inside CHECK()
310 #else
311 # if !(defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
312  /// a quick'n'dirty assertion to verify a predicate
313 # define CHECK(predicate) do { if(!report_check(__LINE__, #predicate, (predicate))) { RYML_DEBUG_BREAK(); } } while(0)
314 # else // GCC 4.8 has a problem with the CHECK() macro
315 # define CHECK CheckPredicate{__FILE__, __LINE__}
316  struct CheckPredicate
317  {
318  const char *file;
319  const int line;
320  void operator() (bool result) const
321  {
322  if (!report_check(line, nullptr, result))
323  {
324  RYML_DEBUG_BREAK();
325  }
326  }
327  };
328 # endif // GCC 4.8
329 #endif // doxygen
330 
331 /** @} */ // doc_sample_helpers
332 
333 
334 //-----------------------------------------------------------------------------
335 //-----------------------------------------------------------------------------
336 //-----------------------------------------------------------------------------
337 
338 /** a lightning tour over most features
339  * see @ref sample_quick_overview */
341 {
342  // Parse YAML code in place, potentially mutating the buffer:
343  char yml_buf[] = "{foo: 1, bar: [2, 3], john: doe}";
344  ryml::Tree tree = ryml::parse_in_place(yml_buf);
345 
346  // read from the tree:
347  ryml::NodeRef bar = tree["bar"];
348  CHECK(bar[0].val() == "2");
349  CHECK(bar[1].val() == "3");
350  CHECK(bar[0].val().str == yml_buf + 15); // points at the source buffer
351  CHECK(bar[1].val().str == yml_buf + 18);
352 
353  // deserializing:
354  int bar0 = 0, bar1 = 0;
355  bar[0] >> bar0;
356  bar[1] >> bar1;
357  CHECK(bar0 == 2);
358  CHECK(bar1 == 3);
359 
360  // serializing:
361  bar[0] << 10; // creates a string in the tree's arena
362  bar[1] << 11;
363  CHECK(bar[0].val() == "10");
364  CHECK(bar[1].val() == "11");
365 
366  // add nodes
367  bar.append_child() << 12; // see also operator= (explanation below)
368  CHECK(bar[2].val() == "12");
369 
370  // emit tree
371  std::string expected = "{foo: 1,bar: [10,11,12],john: doe}";
372  // emit tree to std::string
373  CHECK(ryml::emitrs_yaml<std::string>(tree) == expected);
374  // emit tree to FILE*
375  ryml::emit_yaml(tree, stdout); printf("\n");
376  // emit tree to ostream
377  std::cout << tree << "\n";
378 
379  // emit node
380  ryml::ConstNodeRef foo = tree["foo"];
381  expected = "foo: 1\n";
382  // emit node to std::string
383  CHECK(ryml::emitrs_yaml<std::string>(foo) == expected);
384  // emit node to FILE*
385  ryml::emit_yaml(foo, stdout);
386  // emit node to ostream
387  std::cout << foo;
388 }
389 
390 
391 //-----------------------------------------------------------------------------
392 
393 /** a brief tour over most features */
395 {
396  // Parse YAML code in place, potentially mutating the buffer:
397  char yml_buf[] = ""
398  "foo: 1" "\n"
399  "bar: [2, 3]" "\n"
400  "john: doe" "\n"
401  "";
402  ryml::Tree tree = ryml::parse_in_place(yml_buf);
403 
404  // The resulting tree contains only views to the parsed string. If
405  // the string was parsed in place, then the string must outlive
406  // the tree! This works here because both `yml_buf` and `tree`
407  // live on the same scope, so have the same lifetime.
408 
409  // It is also possible to:
410  //
411  // - parse a read-only buffer using parse_in_arena(). This
412  // copies the YAML buffer to the tree's arena, and spares the
413  // headache of the string's lifetime.
414  //
415  // - reuse an existing tree (advised)
416  //
417  // - reuse an existing parser (advised)
418  //
419  // - parse into an existing node deep in a tree
420  //
421  // Note: it will always be significantly faster to parse in place
422  // and reuse tree+parser.
423  //
424  // Below you will find samples that show how to achieve reuse; but
425  // please note that for brevity and clarity, many of the examples
426  // here are parsing in the arena, and not reusing tree or parser.
427 
428 
429  //------------------------------------------------------------------
430  // API overview
431 
432  // ryml has a two-level API:
433  //
434  // The lower level index API is based on the indices of nodes,
435  // where the node's id is the node's position in the tree's data
436  // array. This API is very efficient, but somewhat difficult to use:
437  ryml::id_type root_id = tree.root_id();
438  ryml::id_type bar_id = tree.find_child(root_id, "bar"); // need to get the index right
439  CHECK(tree.is_map(root_id)); // all of the index methods are in the tree
440  CHECK(tree.is_seq(bar_id)); // ... and receive the subject index
441 
442  // The node API is a lightweight abstraction sitting on top of the
443  // index API, but offering a much more convenient interaction:
444  ryml::ConstNodeRef root = tree.rootref(); // a const node reference
445  ryml::ConstNodeRef bar = tree["bar"];
446  CHECK(root.is_map());
447  CHECK(bar.is_seq());
448 
449  // A node ref is a lightweight handle to the tree and associated id:
450  CHECK(root.tree() == &tree); // a node ref points at its tree, WITHOUT refcount
451  CHECK(root.id() == root_id); // a node ref's id is the index of the node
452  CHECK(bar.id() == bar_id); // a node ref's id is the index of the node
453 
454  // The node API translates very cleanly to the index API, so most
455  // of the code examples below are using the node API.
456 
457  // WARNING. A node ref holds a raw pointer to the tree. Care must
458  // be taken to ensure the lifetimes match, so that a node will
459  // never access the tree after the goes out of scope.
460 
461 
462  //------------------------------------------------------------------
463  // To read the parsed tree
464 
465  // ConstNodeRef::operator[] does a lookup, is O(num_children[node]).
466  CHECK(tree["foo"].is_keyval());
467  CHECK(tree["foo"].val() == "1"); // get the val of a node (must be leaf node, otherwise it is a container and has no val)
468  CHECK(tree["foo"].key() == "foo"); // get the key of a node (must be child of a map, otherwise it has no key)
469  CHECK(tree["bar"].is_seq());
470  CHECK(tree["bar"].has_key());
471  CHECK(tree["bar"].key() == "bar");
472  // maps use string keys, seqs use index keys:
473  CHECK(tree["bar"][0].val() == "2");
474  CHECK(tree["bar"][1].val() == "3");
475  CHECK(tree["john"].val() == "doe");
476  // An index key is the position of the child within its parent,
477  // so even maps can also use int keys, if the key position is
478  // known.
479  CHECK(tree[0].id() == tree["foo"].id());
480  CHECK(tree[1].id() == tree["bar"].id());
481  CHECK(tree[2].id() == tree["john"].id());
482  // Tree::operator[](int) searches a ***root*** child by its position.
483  CHECK(tree[0].id() == tree["foo"].id()); // 0: first child of root
484  CHECK(tree[1].id() == tree["bar"].id()); // 1: second child of root
485  CHECK(tree[2].id() == tree["john"].id()); // 2: third child of root
486  // NodeRef::operator[](int) searches a ***node*** child by its position:
487  CHECK(bar[0].val() == "2"); // 0 means first child of bar
488  CHECK(bar[1].val() == "3"); // 1 means second child of bar
489  // NodeRef::operator[](string):
490  // A string key is the key of the node: lookup is by name. So it
491  // is only available for maps, and it is NOT available for seqs,
492  // since seq members do not have keys.
493  CHECK(tree["foo"].key() == "foo");
494  CHECK(tree["bar"].key() == "bar");
495  CHECK(tree["john"].key() == "john");
496  CHECK(bar.is_seq());
497  // CHECK(bar["BOOM!"].is_seed()); // error, seqs do not have key lookup
498 
499  // Note that maps can also use index keys as well as string keys:
500  CHECK(root["foo"].id() == root[0].id());
501  CHECK(root["bar"].id() == root[1].id());
502  CHECK(root["john"].id() == root[2].id());
503 
504  // IMPORTANT. The ryml tree uses an index-based linked list for
505  // storing children, so the complexity of
506  // `Tree::operator[csubstr]` and `Tree::operator[id_type]` is O(n),
507  // linear on the number of root children. If you use
508  // `Tree::operator[]` with a large tree where the root has many
509  // children, you will see a performance hit.
510  //
511  // To avoid this hit, you can create your own accelerator
512  // structure. For example, before doing a lookup, do a single
513  // traverse at the root level to fill an `map<csubstr,id_type>`
514  // mapping key names to node indices; with a node index, a lookup
515  // (via `Tree::get()`) is O(1), so this way you can get O(log n)
516  // lookup from a key. (But please do not use `std::map` if you
517  // care about performance; use something else like a flat map or
518  // sorted vector).
519  //
520  // As for node refs, the difference from `NodeRef::operator[]` and
521  // `ConstNodeRef::operator[]` to `Tree::operator[]` is that the
522  // latter refers to the root node, whereas the former are invoked
523  // on their target node. But the lookup process works the same for
524  // both and their algorithmic complexity is the same: they are
525  // both linear in the number of direct children. But of course,
526  // depending on the data, that number may be very different from
527  // one to another.
528 
529 
530  //------------------------------------------------------------------
531  // Hierarchy:
532 
533  {
534  ryml::ConstNodeRef foo = root.first_child();
535  ryml::ConstNodeRef john = root.last_child();
536  CHECK(tree.size() == 6); // O(1) number of nodes in the tree
537  CHECK(root.num_children() == 3); // O(num_children[root])
538  CHECK(foo.num_siblings() == 3); // O(num_children[parent(foo)])
539  CHECK(foo.parent().id() == root.id()); // parent() is O(1)
540  CHECK(root.first_child().id() == root["foo"].id()); // first_child() is O(1)
541  CHECK(root.last_child().id() == root["john"].id()); // last_child() is O(1)
542  CHECK(john.first_sibling().id() == foo.id());
543  CHECK(foo.last_sibling().id() == john.id());
544  // prev_sibling(), next_sibling(): (both are O(1))
545  CHECK(foo.num_siblings() == root.num_children());
546  CHECK(foo.prev_sibling().id() == ryml::NONE); // foo is the first_child()
547  CHECK(foo.next_sibling().key() == "bar");
548  CHECK(foo.next_sibling().next_sibling().key() == "john");
549  CHECK(foo.next_sibling().next_sibling().next_sibling().id() == ryml::NONE); // john is the last_child()
550  }
551 
552 
553  //------------------------------------------------------------------
554  // Iterating:
555  {
556  ryml::csubstr expected_keys[] = {"foo", "bar", "john"};
557  // iterate children using the high-level node API:
558  {
559  ryml::id_type count = 0;
560  for(ryml::ConstNodeRef const& child : root.children())
561  CHECK(child.key() == expected_keys[count++]);
562  }
563  // iterate siblings using the high-level node API:
564  {
565  ryml::id_type count = 0;
566  for(ryml::ConstNodeRef const& child : root["foo"].siblings())
567  CHECK(child.key() == expected_keys[count++]);
568  }
569  // iterate children using the lower-level tree index API:
570  {
571  ryml::id_type count = 0;
572  for(ryml::id_type child_id = tree.first_child(root_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
573  CHECK(tree.key(child_id) == expected_keys[count++]);
574  }
575  // iterate siblings using the lower-level tree index API:
576  // (notice the only difference from above is in the loop
577  // preamble, which calls tree.first_sibling(bar_id) instead of
578  // tree.first_child(root_id))
579  {
580  ryml::id_type count = 0;
581  for(ryml::id_type child_id = tree.first_sibling(bar_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
582  CHECK(tree.key(child_id) == expected_keys[count++]);
583  }
584  }
585 
586 
587  //------------------------------------------------------------------
588  // Gotchas:
589 
590  // ryml uses assertions to prevent you from trying to obtain
591  // things that do not exist. For example:
592 
593  {
594  ryml::ConstNodeRef seq_node = tree["bar"];
595  ryml::ConstNodeRef val_node = seq_node[0];
596 
597  CHECK(seq_node.is_seq()); // seq is a container
598  CHECK(!seq_node.has_val()); // ... so it has no val
599  //CHECK(seq_node.val() == BOOM!); // ... so attempting to get a val is undefined behavior
600 
601  CHECK(val_node.parent() == seq_node); // belongs to a seq
602  CHECK(!val_node.has_key()); // ... so it has no key
603  //CHECK(val_node.key() == BOOM!); // ... so attempting to get a key is undefined behavior
604 
605  CHECK(val_node.is_val()); // this node is a val
606  //CHECK(val_node.first_child() == BOOM!); // ... so attempting to get a child is undefined behavior
607 
608  // assertions are also present in methods that /may/ read the val:
609  CHECK(seq_node.is_seq()); // seq is a container
610  //CHECK(seq_node.val_is_null() BOOM!); // so cannot get the val to check
611  }
612 
613  // By default, assertions are enabled unless the NDEBUG macro is
614  // defined (which happens in release builds).
615  //
616  // This adheres to the pay-only-for-what-you-use philosophy: if
617  // you are sure that your intent is correct, why would you need to
618  // pay the runtime cost for the assertions?
619  //
620  // The downside, of course, is that you may be unsure, or your
621  // code may be wrong; and then release builds may end up doing
622  // something wrong.
623  //
624  // So in that case, you can use the appropriate ryml predicates to
625  // check your intent (as in the examples above), or you can
626  // explicitly enable/disable assertions, by defining the macro
627  // RYML_USE_ASSERT to a proper value (see c4/yml/common.hpp). This
628  // will make problematic code trigger a call to the appropriate
629  // error callback.
630  //
631  // Also, to be clear, this does not apply to parse errors
632  // occurring when the YAML is parsed. Parse errors are always
633  // detected by the parser: this is not done through assertions,
634  // but through the appropriate error checking mechanism. So
635  // checking for these errors is always enabled and cannot be
636  // switched off.
637 
638 
639  //------------------------------------------------------------------
640  // Deserializing: use operator>>
641  {
642  int foo = 0, bar0 = 0, bar1 = 0;
643  std::string john_str;
644  std::string bar_str;
645  root["foo"] >> foo;
646  root["bar"][0] >> bar0;
647  root["bar"][1] >> bar1;
648  root["john"] >> john_str; // requires from_chars(std::string). see serialization samples below.
649  root["bar"] >> ryml::key(bar_str); // to deserialize the key, use the tag function ryml::key()
650  CHECK(foo == 1);
651  CHECK(bar0 == 2);
652  CHECK(bar1 == 3);
653  CHECK(john_str == "doe");
654  CHECK(bar_str == "bar");
655  }
656 
657 
658  //------------------------------------------------------------------
659  // Modifying existing nodes: operator= vs operator<<
660 
661  // As implied by its name, ConstNodeRef is a reference to a const
662  // node. It can be used to read from the node, but not write to it
663  // or modify the hierarchy of the node. If any modification is
664  // desired then a NodeRef must be used instead:
665  ryml::NodeRef wroot = tree.rootref(); // writeable root
666 
667  // operator= assigns an existing string to the receiving node.
668  // The contents are NOT copied, and the string pointer will be in
669  // effect until the tree goes out of scope! So BEWARE to only
670  // assign from strings outliving the tree.
671  wroot["foo"] = "says you";
672  wroot["bar"][0] = "-2";
673  wroot["bar"][1] = "-3";
674  wroot["john"] = "ron";
675  // Now the tree is _pointing_ at the memory of the strings above.
676  // In this case it is OK because those are static strings, located
677  // in the executable's static section, and will outlive the tree.
678  CHECK(root["foo"].val() == "says you");
679  CHECK(root["bar"][0].val() == "-2");
680  CHECK(root["bar"][1].val() == "-3");
681  CHECK(root["john"].val() == "ron");
682  // But WATCHOUT: do not assign from temporary objects:
683  // {
684  // std::string crash("will dangle");
685  // root["john"] = ryml::to_csubstr(crash);
686  // }
687  // CHECK(root["john"] == "dangling"); // CRASH! the string was deallocated
688 
689  // operator<<: for cases where the lifetime of the string is
690  // problematic WRT the tree, you can create and save a string in
691  // the tree using operator<<. It first serializes values to a
692  // string arena owned by the tree, then assigns the serialized
693  // string to the receiving node. This avoids constraints with the
694  // lifetime, since the arena lives with the tree.
695  CHECK(tree.arena().empty());
696  wroot["foo"] << "says who"; // requires to_chars(). see serialization samples below.
697  wroot["bar"][0] << 20;
698  wroot["bar"][1] << 30;
699  wroot["john"] << "deere";
700  CHECK(root["foo"].val() == "says who");
701  CHECK(root["bar"][0].val() == "20");
702  CHECK(root["bar"][1].val() == "30");
703  CHECK(root["john"].val() == "deere");
704  CHECK(tree.arena() == "says who2030deere"); // the result of serializations to the tree arena
705  // using operator<< instead of operator=, the crash above is avoided:
706  {
707  std::string ok("in_scope");
708  // root["john"] = ryml::to_csubstr(ok); // don't, will dangle
709  wroot["john"] << ryml::to_csubstr(ok); // OK, copy to the tree's arena
710  }
711  CHECK(root["john"].val() == "in_scope"); // OK! val is now in the tree's arena
712  // serializing floating points:
713  wroot["float"] << 2.4f;
714  // to force a particular precision or float format:
715  // (see sample_float_precision() and sample_formatting())
716  wroot["digits"] << ryml::fmt::real(2.4, /*num_digits*/6, ryml::FTOA_FLOAT);
717  CHECK(tree.arena() == "says who2030deerein_scope2.42.400000"); // the result of serializations to the tree arena
718 
719 
720  //------------------------------------------------------------------
721  // Adding new nodes:
722 
723  // adding a keyval node to a map:
724  CHECK(root.num_children() == 5);
725  wroot["newkeyval"] = "shiny and new"; // using these strings
726  wroot.append_child() << ryml::key("newkeyval (serialized)") << "shiny and new (serialized)"; // serializes and assigns the serialization
727  CHECK(root.num_children() == 7);
728  CHECK(root["newkeyval"].key() == "newkeyval");
729  CHECK(root["newkeyval"].val() == "shiny and new");
730  CHECK(root["newkeyval (serialized)"].key() == "newkeyval (serialized)");
731  CHECK(root["newkeyval (serialized)"].val() == "shiny and new (serialized)");
732  CHECK( ! tree.in_arena(root["newkeyval"].key())); // it's using directly the static string above
733  CHECK( ! tree.in_arena(root["newkeyval"].val())); // it's using directly the static string above
734  CHECK( tree.in_arena(root["newkeyval (serialized)"].key())); // it's using a serialization of the string above
735  CHECK( tree.in_arena(root["newkeyval (serialized)"].val())); // it's using a serialization of the string above
736  // adding a val node to a seq:
737  CHECK(root["bar"].num_children() == 2);
738  wroot["bar"][2] = "oh so nice";
739  wroot["bar"][3] << "oh so nice (serialized)";
740  CHECK(root["bar"].num_children() == 4);
741  CHECK(root["bar"][2].val() == "oh so nice");
742  CHECK(root["bar"][3].val() == "oh so nice (serialized)");
743  // adding a seq node:
744  CHECK(root.num_children() == 7);
745  wroot["newseq"] |= ryml::SEQ;
746  wroot.append_child() << ryml::key("newseq (serialized)") |= ryml::SEQ;
747  CHECK(root.num_children() == 9);
748  CHECK(root["newseq"].num_children() == 0);
749  CHECK(root["newseq"].is_seq());
750  CHECK(root["newseq (serialized)"].num_children() == 0);
751  CHECK(root["newseq (serialized)"].is_seq());
752  // adding a map node:
753  CHECK(root.num_children() == 9);
754  wroot["newmap"] |= ryml::MAP;
755  wroot.append_child() << ryml::key("newmap (serialized)") |= ryml::MAP;
756  CHECK(root.num_children() == 11);
757  CHECK(root["newmap"].num_children() == 0);
758  CHECK(root["newmap"].is_map());
759  CHECK(root["newmap (serialized)"].num_children() == 0);
760  CHECK(root["newmap (serialized)"].is_map());
761  //
762  // When the tree is const, operator[] searches the tree for a
763  // matching key/position, and returns it, or raises an error if
764  // none is found.
765  //
766  // When the tree is mutable, operator[] will not mutate the tree
767  // until the returned node is written to. operator[] first
768  // searches the tree for a node matching the key/position and
769  // returns it if it exists; otherwise, the result is a NodeRef
770  // object which keeps in itself the required information to write
771  // to the proper place in the tree. This is called being in a
772  // "seed" state.
773  //
774  // This means that passing a key/position which does not exist
775  // will not mutate the tree, but will instead store (in the
776  // returned NodeRef) the proper place of the tree to be able to do
777  // so, if and when it is required. This is why the node is said to
778  // be in "seed" state - it allows creating the entry in the tree
779  // in the future.
780  //
781  // This is a significant difference from eg, the behavior of
782  // std::map, which mutates the map immediately within the call to
783  // operator[].
784  //
785  // All of the points above apply only if the tree is mutable. If
786  // the tree is const, then a NodeRef cannot be obtained from it;
787  // only a ConstNodeRef, which can never be used to mutate the
788  // tree.
789  //
790  CHECK(!root.has_child("I am not nothing"));
791  ryml::NodeRef nothing;
792  CHECK(nothing.invalid()); // invalid because it points at nothing
793  nothing = wroot["I am nothing"];
794  CHECK(!nothing.invalid()); // points at the tree, and a specific place in the tree
795  CHECK(nothing.is_seed()); // ... but nothing is there yet.
796  CHECK(!root.has_child("I am nothing")); // same as above
797  CHECK(!nothing.readable()); // ... and this node cannot be used to
798  // read anything from the tree
799  ryml::NodeRef something = wroot["I am something"];
800  ryml::ConstNodeRef constsomething = wroot["I am something"];
801  CHECK(!root.has_child("I am something")); // same as above
802  CHECK(!something.invalid());
803  CHECK(something.is_seed()); // same as above
804  CHECK(!something.readable()); // same as above
805  CHECK(constsomething.invalid()); // NOTE: because a ConstNodeRef cannot be
806  // used to mutate a tree, it is only valid()
807  // if it is pointing at an existing node.
808  something = "indeed"; // this will commit the seed to the tree, mutating at the proper place
809  CHECK(root.has_child("I am something"));
810  CHECK(root["I am something"].val() == "indeed");
811  CHECK(!something.invalid()); // it was already valid
812  CHECK(!something.is_seed()); // now the tree has this node, so the
813  // ref is no longer a seed
814  CHECK(something.readable()); // and it is now readable
815  //
816  // now the constref is also valid (but it needs to be reassigned):
817  ryml::ConstNodeRef constsomethingnew = wroot["I am something"];
818  CHECK(!constsomethingnew.invalid());
819  CHECK(constsomethingnew.readable());
820  // note that the old constref is now stale, because it only keeps
821  // the state at creation:
822  CHECK(constsomething.invalid());
823  CHECK(!constsomething.readable());
824  //
825  // -----------------------------------------------------------
826  // Remember: a seed node cannot be used to read from the tree!
827  // -----------------------------------------------------------
828  //
829  // The seed node needs to be created and become readable first.
830  //
831  // Trying to invoke any tree-reading method on a node that is not
832  // readable will cause an assertion (see RYML_USE_ASSERT).
833  //
834  // It is your responsibility to verify that the preconditions are
835  // met. If you are not sure about the structure of your data,
836  // write your code defensively to signify your full intent:
837  //
838  ryml::NodeRef wbar = wroot["bar"];
839  if(wbar.readable() && wbar.is_seq()) // .is_seq() requires .readable()
840  {
841  CHECK(wbar[0].readable() && wbar[0].val() == "20");
842  CHECK( ! wbar[100].readable());
843  CHECK( ! wbar[100].readable() || wbar[100].val() == "100"); // <- no crash because it is not .readable(), so never tries to call .val()
844  // this would work as well:
845  CHECK( ! wbar[0].is_seed() && wbar[0].val() == "20");
846  CHECK(wbar[100].is_seed() || wbar[100].val() == "100");
847  }
848 
849 
850  //------------------------------------------------------------------
851  // .operator[]() vs .at()
852 
853  // (Const)NodeRef::operator[] is an analogue to std::vector::operator[].
854  // (Const)NodeRef::at() is an analogue to std::vector::at()
855  //
856  // at() will always check the subject node is .readable().
857  //
858  // [] is meant for the happy path, and unverified in Release
859  // builds.
860  {
861  // in this example we will be checking errors, so set up a
862  // temporary error handler to catch them:
863  ScopedErrorHandlerExample errh; // calls ryml::set_callbacks()
864  // instantiate the tree after errh
865  ryml::Tree err_tree = ryml::parse_in_arena("{foo: bar}");
866  // ... so that the tree uses the current callbacks:
867  CHECK(err_tree.callbacks() == errh.callbacks());
868  // node does not exist, only a seed node
869  ryml::NodeRef seed_node = err_tree["this"];
870  // ... therefore not .readable()
871  CHECK(!seed_node.readable());
872  // using .at() reliably produces an error:
873  CHECK(errh.check_error_occurs([&]{
874  return seed_node.at("is").at("an").at("invalid").at("operation");
875  // ^
876  // error occurs here because it is unreadable
877  }));
878  // ... but using [] fails only when RYML_USE_ASSERT is
879  // defined. otherwise, it's the dreaded Undefined Behavior:
880  CHECK(errh.check_assertion_occurs([&]{
881  return seed_node["is"]["an"]["invalid"]["operation"];
882  // ^
883  // assertion occurs here because it is unreadable
884  }));
885  }
886 
887 
888  //------------------------------------------------------------------
889  // Emitting:
890 
891  ryml::csubstr expected_result =
892  "foo: says who" "\n"
893  "bar: [20,30,oh so nice,oh so nice (serialized)]" "\n"
894  "john: in_scope" "\n"
895  "float: 2.4" "\n"
896  "digits: 2.400000" "\n"
897  "newkeyval: shiny and new" "\n"
898  "newkeyval (serialized): shiny and new (serialized)" "\n"
899  "newseq: []" "\n"
900  "newseq (serialized): []" "\n"
901  "newmap: {}" "\n"
902  "newmap (serialized): {}" "\n"
903  "I am something: indeed" "\n"
904  "";
905 
906  // emit to a FILE*
907  ryml::emit_yaml(tree, stdout);
908  // emit to a stream
909  // (this is templated on the stream, so it works both with
910  // standard streams like std::ostream/std::stringstream, or with
911  // user-defined streams not inheriting from ostream)
912  std::stringstream ss;
913  ss << tree;
914  std::string stream_result = ss.str();
915  // emit to a buffer:
916  std::string str_result = ryml::emitrs_yaml<std::string>(tree);
917  // can emit to any given buffer:
918  char buf[1024];
919  ryml::csubstr buf_result = ryml::emit_yaml(tree, buf);
920  // now check
921  CHECK(buf_result == expected_result);
922  CHECK(str_result == expected_result);
923  CHECK(stream_result == expected_result);
924  // There are many possibilities to emit to buffer;
925  // please look at the emit sample functions below.
926 
927 
928  //------------------------------------------------------------------
929  // ConstNodeRef vs NodeRef
930 
931  ryml::NodeRef noderef = tree["bar"][0];
932  ryml::ConstNodeRef constnoderef = tree["bar"][0];
933 
934  // ConstNodeRef cannot be used to mutate the tree:
935  //constnoderef = "21"; // compile error
936  //constnoderef << "22"; // compile error
937  // ... but a NodeRef can:
938  noderef = "21"; // ok, can assign because it's not const
939  CHECK(tree["bar"][0].val() == "21");
940  noderef << "22"; // ok, can serialize and assign because it's not const
941  CHECK(tree["bar"][0].val() == "22");
942 
943  // it is not possible to obtain a NodeRef from a ConstNodeRef:
944  // noderef = constnoderef; // compile error
945 
946  // it is always possible to obtain a ConstNodeRef from a NodeRef:
947  constnoderef = noderef; // ok can assign const <- nonconst
948 
949  // If a tree is const, then only ConstNodeRef's can be
950  // obtained from that tree:
951  ryml::Tree const& consttree = tree;
952  //noderef = consttree["bar"][0]; // compile error
953  noderef = tree["bar"][0]; // ok
954  constnoderef = consttree["bar"][0]; // ok
955 
956  // ConstNodeRef and NodeRef can be compared for equality.
957  // Equality means they point at the same node.
958  CHECK(constnoderef == noderef);
959  CHECK(!(constnoderef != noderef));
960 
961 
962  //------------------------------------------------------------------
963  // Getting the location of nodes in the source:
964  //
965  // Location tracking is opt-in:
966  ryml::EventHandlerTree evt_handler = {};
967  ryml::Parser parser(&evt_handler, ryml::ParserOptions().locations(true));
968  // Now the parser will start by building the accelerator structure:
969  ryml::Tree tree2 = parse_in_arena(&parser, "expected.yml", expected_result);
970  // ... and use it when querying
971  ryml::ConstNodeRef subject_node = tree2["bar"][1];
972  CHECK(subject_node.val() == "30");
973  ryml::Location loc = subject_node.location(parser);
974  CHECK(parser.location_contents(loc).begins_with("30"));
975  CHECK(loc.line == 1u);
976  CHECK(loc.col == 9u);
977  // For further details in location tracking,
978  // refer to the sample function below.
979 
980  //------------------------------------------------------------------
981  // Dealing with UTF8
983  "#" "\n"
984  "# UTF8/16/32 can be placed directly in any scalar:" "\n"
985  "#" "\n"
986  "en: Planet (Gas)" "\n"
987  "fr: Planète (Gazeuse)" "\n"
988  "ru: Планета (Газ)" "\n"
989  "ja: 惑星(ガス)" "\n"
990  "zh: 行星(气体)" "\n"
991  "#" "\n"
992  "# UTF8 decoding only happens in double-quoted strings," "\n"
993  "# as per the YAML standard" "\n"
994  "#" "\n"
995  "decode this: \"\\u263A c\\x61f\\xE9\"" "\n"
996  "and this as well: \"\\u2705 \\U0001D11E\"" "\n"
997  "not decoded: '\\u263A \\xE2\\x98\\xBA'" "\n"
998  "neither this: '\\u2705 \\U0001D11E'" "\n");
999  // in-place UTF8 just works:
1000  CHECK(langs["en"].val() == "Planet (Gas)");
1001  CHECK(langs["fr"].val() == "Planète (Gazeuse)");
1002  CHECK(langs["ru"].val() == "Планета (Газ)");
1003  CHECK(langs["ja"].val() == "惑星(ガス)");
1004  CHECK(langs["zh"].val() == "行星(气体)");
1005  // and \x \u \U codepoints are decoded, but only when they appear
1006  // inside double-quoted strings, as dictated by the YAML
1007  // standard:
1008  CHECK(langs["decode this"].val() == "☺ café");
1009  CHECK(langs["and this as well"].val() == "✅ 𝄞");
1010  CHECK(langs["not decoded"].val() == "\\u263A \\xE2\\x98\\xBA");
1011  CHECK(langs["neither this"].val() == "\\u2705 \\U0001D11E");
1012 }
1013 
1014 
1015 //-----------------------------------------------------------------------------
1016 
1017 /** demonstrate usage of ryml::substr and ryml::csubstr
1018  *
1019  * These types are imported from the c4core library into the ryml
1020  * namespace You may have noticed above the use of a `csubstr`
1021  * class. This class is defined in another library,
1022  * [c4core](https://github.com/biojppm/c4core), which is imported by
1023  * ryml. This is a library I use with my projects consisting of
1024  * multiplatform low-level utilities. One of these is `c4::csubstr`
1025  * (the name comes from "constant substring") which is a non-owning
1026  * read-only string view, with many methods that make it practical to
1027  * use (I would certainly argue more practical than `std::string`). In
1028  * fact, `c4::csubstr` and its writeable counterpart `c4::substr` are
1029  * the workhorses of the ryml parsing and serialization code.
1030  *
1031  * @see doc_substr */
1033 {
1034  // substr is a mutable view: pointer and length to a string in memory.
1035  // csubstr is a const-substr (immutable).
1036 
1037  // construct from explicit args
1038  {
1039  const char foobar_str[] = "foobar";
1040  auto s = ryml::csubstr(foobar_str, strlen(foobar_str));
1041  CHECK(s == "foobar");
1042  CHECK(s.size() == 6);
1043  CHECK(s.data() == foobar_str);
1044  CHECK(s.size() == s.len);
1045  CHECK(s.data() == s.str);
1046  }
1047 
1048  // construct from a string array
1049  {
1050  const char foobar_str[] = "foobar";
1051  ryml::csubstr s = foobar_str;
1052  CHECK(s == "foobar");
1053  CHECK(s != "foobar0");
1054  CHECK(s.size() == 6); // does not include the terminating \0
1055  CHECK(s.data() == foobar_str);
1056  CHECK(s.size() == s.len);
1057  CHECK(s.data() == s.str);
1058  }
1059  // you can also declare directly in-place from an array:
1060  {
1061  ryml::csubstr s = "foobar";
1062  CHECK(s == "foobar");
1063  CHECK(s != "foobar0");
1064  CHECK(s.size() == 6);
1065  CHECK(s.size() == s.len);
1066  CHECK(s.data() == s.str);
1067  }
1068 
1069  // construct from a C-string:
1070  //
1071  // Since the input is only a pointer, the string length can only
1072  // be found with a call to strlen(). To make this cost evident, we
1073  // require calling to_csubstr():
1074  {
1075  const char *foobar_str = "foobar";
1076  ryml::csubstr s = ryml::to_csubstr(foobar_str);
1077  CHECK(s == "foobar");
1078  CHECK(s != "foobar0");
1079  CHECK(s.size() == 6);
1080  CHECK(s.size() == s.len);
1081  CHECK(s.data() == s.str);
1082  }
1083 
1084  // construct from a std::string: same approach as above.
1085  // requires inclusion of the <ryml/std/string.hpp> header
1086  // or of the umbrella header <ryml_std.hpp>.
1087  //
1088  // not including <string> in the default header is a deliberate
1089  // design choice to avoid including the heavy std:: allocation
1090  // machinery
1091  {
1092  std::string foobar_str = "foobar";
1093  ryml::csubstr s = ryml::to_csubstr(foobar_str); // defined in <ryml/std/string.hpp>
1094  CHECK(s == "foobar");
1095  CHECK(s != "foobar0");
1096  CHECK(s.size() == 6);
1097  CHECK(s.size() == s.len);
1098  CHECK(s.data() == s.str);
1099  }
1100 
1101  // convert substr -> csubstr
1102  {
1103  char buf[] = "foo";
1104  ryml::substr foo = buf;
1105  CHECK(foo.len == 3);
1106  CHECK(foo.data() == buf);
1107  ryml::csubstr cfoo = foo;
1108  CHECK(cfoo.data() == buf);
1109  }
1110  // cannot convert csubstr -> substr:
1111  {
1112  // ryml::substr foo2 = cfoo; // compile error: cannot write to csubstr
1113  }
1114 
1115  // construct from char[]/const char[]: mutable vs immutable memory
1116  {
1117  char const foobar_str_ro[] = "foobar"; // ro := read-only
1118  char foobar_str_rw[] = "foobar"; // rw := read-write
1119  static_assert(std::is_array<decltype(foobar_str_ro)>::value, "this is an array");
1120  static_assert(std::is_array<decltype(foobar_str_rw)>::value, "this is an array");
1121  // csubstr <- read-only memory
1122  {
1123  ryml::csubstr foobar = foobar_str_ro;
1124  CHECK(foobar.data() == foobar_str_ro);
1125  CHECK(foobar.size() == strlen(foobar_str_ro));
1126  CHECK(foobar == "foobar"); // AKA strcmp
1127  }
1128  // csubstr <- read-write memory: you can create an immutable csubstr from mutable memory
1129  {
1130  ryml::csubstr foobar = foobar_str_rw;
1131  CHECK(foobar.data() == foobar_str_rw);
1132  CHECK(foobar.size() == strlen(foobar_str_rw));
1133  CHECK(foobar == "foobar"); // AKA strcmp
1134  }
1135  // substr <- read-write memory.
1136  {
1137  ryml::substr foobar = foobar_str_rw;
1138  CHECK(foobar.data() == foobar_str_rw);
1139  CHECK(foobar.size() == strlen(foobar_str_rw));
1140  CHECK(foobar == "foobar"); // AKA strcmp
1141  }
1142  // substr <- ro is impossible.
1143  {
1144  //ryml::substr foobar = foobar_str_ro; // compile error!
1145  }
1146  }
1147 
1148  // construct from char*/const char*: mutable vs immutable memory.
1149  // use to_substr()/to_csubstr()
1150  {
1151  char const* foobar_str_ro = "foobar"; // ro := read-only
1152  char foobar_str_rw_[] = "foobar"; // rw := read-write
1153  char * foobar_str_rw = foobar_str_rw_; // rw := read-write
1154  static_assert(!std::is_array<decltype(foobar_str_ro)>::value, "this is a decayed pointer");
1155  static_assert(!std::is_array<decltype(foobar_str_rw)>::value, "this is a decayed pointer");
1156  // csubstr <- read-only memory
1157  {
1158  //ryml::csubstr foobar = foobar_str_ro; // compile error: length is not known
1159  ryml::csubstr foobar = ryml::to_csubstr(foobar_str_ro);
1160  CHECK(foobar.data() == foobar_str_ro);
1161  CHECK(foobar.size() == strlen(foobar_str_ro));
1162  CHECK(foobar == "foobar"); // AKA strcmp
1163  }
1164  // csubstr <- read-write memory: you can create an immutable csubstr from mutable memory
1165  {
1166  ryml::csubstr foobar = ryml::to_csubstr(foobar_str_rw);
1167  CHECK(foobar.data() == foobar_str_rw);
1168  CHECK(foobar.size() == strlen(foobar_str_rw));
1169  CHECK(foobar == "foobar"); // AKA strcmp
1170  }
1171  // substr <- read-write memory.
1172  {
1173  ryml::substr foobar = ryml::to_substr(foobar_str_rw);
1174  CHECK(foobar.data() == foobar_str_rw);
1175  CHECK(foobar.size() == strlen(foobar_str_rw));
1176  CHECK(foobar == "foobar"); // AKA strcmp
1177  }
1178  // substr <- read-only is impossible.
1179  {
1180  //ryml::substr foobar = ryml::to_substr(foobar_str_ro); // compile error!
1181  }
1182  }
1183 
1184  // substr is mutable, without changing the size:
1185  {
1186  char buf[] = "foobar";
1187  ryml::substr foobar = buf;
1188  CHECK(foobar == "foobar");
1189  foobar[0] = 'F'; CHECK(foobar == "Foobar");
1190  foobar.back() = 'R'; CHECK(foobar == "FoobaR");
1191  foobar.reverse(); CHECK(foobar == "RabooF");
1192  foobar.reverse(); CHECK(foobar == "FoobaR");
1193  foobar.reverse_sub(1, 4); CHECK(foobar == "FabooR");
1194  foobar.reverse_sub(1, 4); CHECK(foobar == "FoobaR");
1195  foobar.reverse_range(2, 5); CHECK(foobar == "FoaboR");
1196  foobar.reverse_range(2, 5); CHECK(foobar == "FoobaR");
1197  foobar.replace('o', '0'); CHECK(foobar == "F00baR");
1198  foobar.replace('a', '_'); CHECK(foobar == "F00b_R");
1199  foobar.replace("_0b", 'a'); CHECK(foobar == "FaaaaR");
1200  foobar.toupper(); CHECK(foobar == "FAAAAR");
1201  foobar.tolower(); CHECK(foobar == "faaaar");
1202  foobar.fill('.'); CHECK(foobar == "......");
1203  // see also:
1204  // - .erase()
1205  // - .replace_all()
1206  }
1207 
1208  // sub-views
1209  {
1210  ryml::csubstr s = "fooFOObarBAR";
1211  CHECK(s.len == 12u);
1212  // sub(): <- first,[num]
1213  CHECK(s.sub(0) == "fooFOObarBAR");
1214  CHECK(s.sub(0, 12) == "fooFOObarBAR");
1215  CHECK(s.sub(0, 3) == "foo" );
1216  CHECK(s.sub(3) == "FOObarBAR");
1217  CHECK(s.sub(3, 3) == "FOO" );
1218  CHECK(s.sub(6) == "barBAR");
1219  CHECK(s.sub(6, 3) == "bar" );
1220  CHECK(s.sub(9) == "BAR");
1221  CHECK(s.sub(9, 3) == "BAR");
1222  // first(): <- length
1223  CHECK(s.first(0) == "" );
1224  CHECK(s.first(1) == "f" );
1225  CHECK(s.first(2) != "f" );
1226  CHECK(s.first(2) == "fo" );
1227  CHECK(s.first(3) == "foo");
1228  // last(): <- length
1229  CHECK(s.last(0) == "");
1230  CHECK(s.last(1) == "R");
1231  CHECK(s.last(2) == "AR");
1232  CHECK(s.last(3) == "BAR");
1233  // range(): <- first, last
1234  CHECK(s.range(0, 12) == "fooFOObarBAR");
1235  CHECK(s.range(1, 12) == "ooFOObarBAR");
1236  CHECK(s.range(1, 11) == "ooFOObarBA" );
1237  CHECK(s.range(2, 10) == "oFOObarB" );
1238  CHECK(s.range(3, 9) == "FOObar" );
1239  // offs(): offset from beginning, end
1240  CHECK(s.offs(0, 0) == "fooFOObarBAR");
1241  CHECK(s.offs(1, 0) == "ooFOObarBAR");
1242  CHECK(s.offs(1, 1) == "ooFOObarBA" );
1243  CHECK(s.offs(2, 1) == "oFOObarBA" );
1244  CHECK(s.offs(2, 2) == "oFOObarB" );
1245  CHECK(s.offs(3, 3) == "FOObar" );
1246  // right_of(): <- pos, include_pos
1247  CHECK(s.right_of(0, true) == "fooFOObarBAR");
1248  CHECK(s.right_of(0, false) == "ooFOObarBAR");
1249  CHECK(s.right_of(1, true) == "ooFOObarBAR");
1250  CHECK(s.right_of(1, false) == "oFOObarBAR");
1251  CHECK(s.right_of(2, true) == "oFOObarBAR");
1252  CHECK(s.right_of(2, false) == "FOObarBAR");
1253  CHECK(s.right_of(3, true) == "FOObarBAR");
1254  CHECK(s.right_of(3, false) == "OObarBAR");
1255  // left_of() <- pos, include_pos
1256  CHECK(s.left_of(12, false) == "fooFOObarBAR");
1257  CHECK(s.left_of(11, true) == "fooFOObarBAR");
1258  CHECK(s.left_of(11, false) == "fooFOObarBA" );
1259  CHECK(s.left_of(10, true) == "fooFOObarBA" );
1260  CHECK(s.left_of(10, false) == "fooFOObarB" );
1261  CHECK(s.left_of( 9, true) == "fooFOObarB" );
1262  CHECK(s.left_of( 9, false) == "fooFOObar" );
1263  // left_of(),right_of() <- substr
1264  ryml::csubstr FOO = s.sub(3, 3);
1265  CHECK(s.is_super(FOO)); // required for the following
1266  CHECK(s.left_of(FOO) == "foo");
1267  CHECK(s.right_of(FOO) == "barBAR");
1268  }
1269 
1270  // printing a substr/csubstr using printf-like
1271  {
1272  ryml::csubstr s = "some substring";
1273  ryml::csubstr some = s.first(4);
1274  CHECK(some == "some");
1275  CHECK(s == "some substring");
1276  // To print a csubstr using printf(), use the %.*s format specifier:
1277  {
1278  char result[32] = {0};
1279  std::snprintf(result, sizeof(result), "%.*s", (int)some.len, some.str);
1280  printf("~~~%s~~~\n", result);
1281  CHECK(ryml::to_csubstr((const char*)result) == "some");
1282  CHECK(ryml::to_csubstr((const char*)result) == some);
1283  }
1284  // But NOTE: because this is a string view type, in general
1285  // the C-string is NOT zero terminated. So NEVER print it
1286  // directly, or it will overflow past the end of the given
1287  // substr, with a potential unbounded access. For example,
1288  // this is bad:
1289  {
1290  char result[32] = {0};
1291  std::snprintf(result, sizeof(result), "%s", some.str); // ERROR! do not print the c-string directly
1292  CHECK(ryml::to_csubstr((const char*)result) == "some substring");
1293  CHECK(ryml::to_csubstr((const char*)result) == s);
1294  }
1295  }
1296 
1297  // printing a substr/csubstr using ostreams
1298  {
1299  ryml::csubstr s = "some substring";
1300  ryml::csubstr some = s.first(4);
1301  CHECK(some == "some");
1302  CHECK(s == "some substring");
1303  // simple! just use plain operator<<
1304  {
1305  std::stringstream ss;
1306  ss << s;
1307  CHECK(ss.str() == "some substring"); // as expected
1308  CHECK(ss.str() == s); // as expected
1309  }
1310  // But NOTE: because this is a string view type, in general
1311  // the C-string is NOT zero terminated. So NEVER print it
1312  // directly, or it will overflow past the end of the given
1313  // substr, with a potential unbounded access. For example,
1314  // this is bad:
1315  {
1316  std::stringstream ss;
1317  ss << some.str; // ERROR! do not print the c-string directly
1318  CHECK(ss.str() == "some substring"); // NOT "some"
1319  CHECK(ss.str() == s); // NOT some
1320  }
1321  // this is also bad (the same)
1322  {
1323  std::stringstream ss;
1324  ss << some.data(); // ERROR! do not print the c-string directly
1325  CHECK(ss.str() == "some substring"); // NOT "some"
1326  CHECK(ss.str() == s); // NOT some
1327  }
1328  // this is ok:
1329  {
1330  std::stringstream ss;
1331  ss << some;
1332  CHECK(ss.str() == "some"); // ok
1333  CHECK(ss.str() == some); // ok
1334  }
1335  }
1336 
1337  // is_sub(),is_super()
1338  {
1339  ryml::csubstr foobar = "foobar";
1340  ryml::csubstr foo = foobar.first(3);
1341  CHECK(foo.is_sub(foobar));
1342  CHECK(foo.is_sub(foo));
1343  CHECK(!foo.is_super(foobar));
1344  CHECK(!foobar.is_sub(foo));
1345  // identity comparison is true:
1346  CHECK(foo.is_super(foo));
1347  CHECK(foo.is_sub(foo));
1348  CHECK(foobar.is_sub(foobar));
1349  CHECK(foobar.is_super(foobar));
1350  }
1351 
1352  // overlaps()
1353  {
1354  ryml::csubstr foobar = "foobar";
1355  ryml::csubstr foo = foobar.first(3);
1356  ryml::csubstr oba = foobar.offs(2, 1);
1357  ryml::csubstr abc = "abc";
1358  CHECK(foobar.overlaps(foo));
1359  CHECK(foobar.overlaps(oba));
1360  CHECK(foo.overlaps(foobar));
1361  CHECK(foo.overlaps(oba));
1362  CHECK(!foo.overlaps(abc));
1363  CHECK(!abc.overlaps(foo));
1364  }
1365 
1366  // triml(): trim characters from the left
1367  // trimr(): trim characters from the right
1368  // trim(): trim characters from left AND right
1369  {
1370  CHECK(ryml::csubstr(" \t\n\rcontents without whitespace\t \n\r").trim("\t \n\r") == "contents without whitespace");
1371  ryml::csubstr aaabbb = "aaabbb";
1372  ryml::csubstr aaa___bbb = "aaa___bbb";
1373  // trim a character:
1374  CHECK(aaabbb.triml('a') == aaabbb.last(3)); // bbb
1375  CHECK(aaabbb.trimr('a') == aaabbb);
1376  CHECK(aaabbb.trim ('a') == aaabbb.last(3)); // bbb
1377  CHECK(aaabbb.triml('b') == aaabbb);
1378  CHECK(aaabbb.trimr('b') == aaabbb.first(3)); // aaa
1379  CHECK(aaabbb.trim ('b') == aaabbb.first(3)); // aaa
1380  CHECK(aaabbb.triml('c') == aaabbb);
1381  CHECK(aaabbb.trimr('c') == aaabbb);
1382  CHECK(aaabbb.trim ('c') == aaabbb);
1383  CHECK(aaa___bbb.triml('a') == aaa___bbb.last(6)); // ___bbb
1384  CHECK(aaa___bbb.trimr('a') == aaa___bbb);
1385  CHECK(aaa___bbb.trim ('a') == aaa___bbb.last(6)); // ___bbb
1386  CHECK(aaa___bbb.triml('b') == aaa___bbb);
1387  CHECK(aaa___bbb.trimr('b') == aaa___bbb.first(6)); // aaa___
1388  CHECK(aaa___bbb.trim ('b') == aaa___bbb.first(6)); // aaa___
1389  CHECK(aaa___bbb.triml('c') == aaa___bbb);
1390  CHECK(aaa___bbb.trimr('c') == aaa___bbb);
1391  CHECK(aaa___bbb.trim ('c') == aaa___bbb);
1392  // trim ANY of the characters:
1393  CHECK(aaabbb.triml("ab") == "");
1394  CHECK(aaabbb.trimr("ab") == "");
1395  CHECK(aaabbb.trim ("ab") == "");
1396  CHECK(aaabbb.triml("ba") == "");
1397  CHECK(aaabbb.trimr("ba") == "");
1398  CHECK(aaabbb.trim ("ba") == "");
1399  CHECK(aaabbb.triml("cd") == aaabbb);
1400  CHECK(aaabbb.trimr("cd") == aaabbb);
1401  CHECK(aaabbb.trim ("cd") == aaabbb);
1402  CHECK(aaa___bbb.triml("ab") == aaa___bbb.last(6)); // ___bbb
1403  CHECK(aaa___bbb.triml("ba") == aaa___bbb.last(6)); // ___bbb
1404  CHECK(aaa___bbb.triml("cd") == aaa___bbb);
1405  CHECK(aaa___bbb.trimr("ab") == aaa___bbb.first(6)); // aaa___
1406  CHECK(aaa___bbb.trimr("ba") == aaa___bbb.first(6)); // aaa___
1407  CHECK(aaa___bbb.trimr("cd") == aaa___bbb);
1408  CHECK(aaa___bbb.trim ("ab") == aaa___bbb.range(3, 6)); // ___
1409  CHECK(aaa___bbb.trim ("ba") == aaa___bbb.range(3, 6)); // ___
1410  CHECK(aaa___bbb.trim ("cd") == aaa___bbb);
1411  }
1412 
1413  // unquoted():
1414  {
1415  CHECK(ryml::csubstr( "'this is is single quoted'" ).unquoted() == "this is is single quoted");
1416  CHECK(ryml::csubstr("\"this is is double quoted\"").unquoted() == "this is is double quoted");
1417  }
1418 
1419  // stripl(): remove pattern from the left
1420  // stripr(): remove pattern from the right
1421  {
1422  ryml::csubstr abc___cba = "abc___cba";
1423  ryml::csubstr abc___abc = "abc___abc";
1424  CHECK(abc___cba.stripl("abc") == abc___cba.last(6)); // ___cba
1425  CHECK(abc___cba.stripr("abc") == abc___cba);
1426  CHECK(abc___cba.stripl("ab") == abc___cba.last(7)); // c___cba
1427  CHECK(abc___cba.stripr("ab") == abc___cba);
1428  CHECK(abc___cba.stripl("a") == abc___cba.last(8)); // bc___cba, same as triml('a')
1429  CHECK(abc___cba.stripr("a") == abc___cba.first(8));
1430  CHECK(abc___abc.stripl("abc") == abc___abc.last(6)); // ___abc
1431  CHECK(abc___abc.stripr("abc") == abc___abc.first(6)); // abc___
1432  CHECK(abc___abc.stripl("ab") == abc___abc.last(7)); // c___cba
1433  CHECK(abc___abc.stripr("ab") == abc___abc);
1434  CHECK(abc___abc.stripl("a") == abc___abc.last(8)); // bc___cba, same as triml('a')
1435  CHECK(abc___abc.stripr("a") == abc___abc);
1436  }
1437 
1438  // begins_with()/ends_with()
1439  // begins_with_any()/ends_with_any()
1440  {
1441  ryml::csubstr s = "foobar123";
1442  // char overloads
1443  CHECK(s.begins_with('f'));
1444  CHECK(s.ends_with('3'));
1445  CHECK(!s.ends_with('2'));
1446  CHECK(!s.ends_with('o'));
1447  // char[] overloads
1448  CHECK(s.begins_with("foobar"));
1449  CHECK(s.begins_with("foo"));
1450  CHECK(s.begins_with_any("foo"));
1451  CHECK(!s.begins_with("oof"));
1452  CHECK(s.begins_with_any("oof"));
1453  CHECK(s.ends_with("23"));
1454  CHECK(s.ends_with("123"));
1455  CHECK(s.ends_with_any("123"));
1456  CHECK(!s.ends_with("321"));
1457  CHECK(s.ends_with_any("231"));
1458  }
1459 
1460  // select()
1461  {
1462  ryml::csubstr s = "0123456789";
1463  CHECK(s.select('0') == s.sub(0, 1));
1464  CHECK(s.select('1') == s.sub(1, 1));
1465  CHECK(s.select('2') == s.sub(2, 1));
1466  CHECK(s.select('8') == s.sub(8, 1));
1467  CHECK(s.select('9') == s.sub(9, 1));
1468  CHECK(s.select("0123") == s.range(0, 4));
1469  CHECK(s.select("012" ) == s.range(0, 3));
1470  CHECK(s.select("01" ) == s.range(0, 2));
1471  CHECK(s.select("0" ) == s.range(0, 1));
1472  CHECK(s.select( "123") == s.range(1, 4));
1473  CHECK(s.select( "23") == s.range(2, 4));
1474  CHECK(s.select( "3") == s.range(3, 4));
1475  }
1476 
1477  // find()
1478  {
1479  ryml::csubstr s012345 = "012345";
1480  // find single characters:
1481  CHECK(s012345.find('a') == ryml::npos);
1482  CHECK(s012345.find('0' ) == 0u);
1483  CHECK(s012345.find('0', 1u) == ryml::npos);
1484  CHECK(s012345.find('1' ) == 1u);
1485  CHECK(s012345.find('1', 2u) == ryml::npos);
1486  CHECK(s012345.find('2' ) == 2u);
1487  CHECK(s012345.find('2', 3u) == ryml::npos);
1488  CHECK(s012345.find('3' ) == 3u);
1489  CHECK(s012345.find('3', 4u) == ryml::npos);
1490  // find patterns
1491  CHECK(s012345.find("ab" ) == ryml::npos);
1492  CHECK(s012345.find("01" ) == 0u);
1493  CHECK(s012345.find("01", 1u) == ryml::npos);
1494  CHECK(s012345.find("12" ) == 1u);
1495  CHECK(s012345.find("12", 2u) == ryml::npos);
1496  CHECK(s012345.find("23" ) == 2u);
1497  CHECK(s012345.find("23", 3u) == ryml::npos);
1498  }
1499 
1500  // count(): count the number of occurrences of a character
1501  {
1502  ryml::csubstr buf = "00110022003300440055";
1503  CHECK(buf.count('1' ) == 2u);
1504  CHECK(buf.count('1', 0u) == 2u);
1505  CHECK(buf.count('1', 1u) == 2u);
1506  CHECK(buf.count('1', 2u) == 2u);
1507  CHECK(buf.count('1', 3u) == 1u);
1508  CHECK(buf.count('1', 4u) == 0u);
1509  CHECK(buf.count('1', 5u) == 0u);
1510  CHECK(buf.count('0' ) == 10u);
1511  CHECK(buf.count('0', 0u) == 10u);
1512  CHECK(buf.count('0', 1u) == 9u);
1513  CHECK(buf.count('0', 2u) == 8u);
1514  CHECK(buf.count('0', 3u) == 8u);
1515  CHECK(buf.count('0', 4u) == 8u);
1516  CHECK(buf.count('0', 5u) == 7u);
1517  CHECK(buf.count('0', 6u) == 6u);
1518  CHECK(buf.count('0', 7u) == 6u);
1519  CHECK(buf.count('0', 8u) == 6u);
1520  CHECK(buf.count('0', 9u) == 5u);
1521  CHECK(buf.count('0', 10u) == 4u);
1522  CHECK(buf.count('0', 11u) == 4u);
1523  CHECK(buf.count('0', 12u) == 4u);
1524  CHECK(buf.count('0', 13u) == 3u);
1525  CHECK(buf.count('0', 14u) == 2u);
1526  CHECK(buf.count('0', 15u) == 2u);
1527  CHECK(buf.count('0', 16u) == 2u);
1528  CHECK(buf.count('0', 17u) == 1u);
1529  CHECK(buf.count('0', 18u) == 0u);
1530  CHECK(buf.count('0', 19u) == 0u);
1531  CHECK(buf.count('0', 20u) == 0u);
1532  }
1533 
1534  // first_of(),last_of()
1535  {
1536  ryml::csubstr s012345 = "012345";
1537  CHECK(s012345.first_of('a') == ryml::npos);
1538  CHECK(s012345.first_of("ab") == ryml::npos);
1539  CHECK(s012345.first_of('0') == 0u);
1540  CHECK(s012345.first_of("0") == 0u);
1541  CHECK(s012345.first_of("01") == 0u);
1542  CHECK(s012345.first_of("10") == 0u);
1543  CHECK(s012345.first_of("012") == 0u);
1544  CHECK(s012345.first_of("210") == 0u);
1545  CHECK(s012345.first_of("0123") == 0u);
1546  CHECK(s012345.first_of("3210") == 0u);
1547  CHECK(s012345.first_of("01234") == 0u);
1548  CHECK(s012345.first_of("43210") == 0u);
1549  CHECK(s012345.first_of("012345") == 0u);
1550  CHECK(s012345.first_of("543210") == 0u);
1551  CHECK(s012345.first_of('5') == 5u);
1552  CHECK(s012345.first_of("5") == 5u);
1553  CHECK(s012345.first_of("45") == 4u);
1554  CHECK(s012345.first_of("54") == 4u);
1555  CHECK(s012345.first_of("345") == 3u);
1556  CHECK(s012345.first_of("543") == 3u);
1557  CHECK(s012345.first_of("2345") == 2u);
1558  CHECK(s012345.first_of("5432") == 2u);
1559  CHECK(s012345.first_of("12345") == 1u);
1560  CHECK(s012345.first_of("54321") == 1u);
1561  CHECK(s012345.first_of("012345") == 0u);
1562  CHECK(s012345.first_of("543210") == 0u);
1563  CHECK(s012345.first_of('0', 6u) == ryml::npos);
1564  CHECK(s012345.first_of('5', 6u) == ryml::npos);
1565  CHECK(s012345.first_of("012345", 6u) == ryml::npos);
1566  //
1567  CHECK(s012345.last_of('a') == ryml::npos);
1568  CHECK(s012345.last_of("ab") == ryml::npos);
1569  CHECK(s012345.last_of('0') == 0u);
1570  CHECK(s012345.last_of("0") == 0u);
1571  CHECK(s012345.last_of("01") == 1u);
1572  CHECK(s012345.last_of("10") == 1u);
1573  CHECK(s012345.last_of("012") == 2u);
1574  CHECK(s012345.last_of("210") == 2u);
1575  CHECK(s012345.last_of("0123") == 3u);
1576  CHECK(s012345.last_of("3210") == 3u);
1577  CHECK(s012345.last_of("01234") == 4u);
1578  CHECK(s012345.last_of("43210") == 4u);
1579  CHECK(s012345.last_of("012345") == 5u);
1580  CHECK(s012345.last_of("543210") == 5u);
1581  CHECK(s012345.last_of('5') == 5u);
1582  CHECK(s012345.last_of("5") == 5u);
1583  CHECK(s012345.last_of("45") == 5u);
1584  CHECK(s012345.last_of("54") == 5u);
1585  CHECK(s012345.last_of("345") == 5u);
1586  CHECK(s012345.last_of("543") == 5u);
1587  CHECK(s012345.last_of("2345") == 5u);
1588  CHECK(s012345.last_of("5432") == 5u);
1589  CHECK(s012345.last_of("12345") == 5u);
1590  CHECK(s012345.last_of("54321") == 5u);
1591  CHECK(s012345.last_of("012345") == 5u);
1592  CHECK(s012345.last_of("543210") == 5u);
1593  CHECK(s012345.last_of('0', 6u) == 0u);
1594  CHECK(s012345.last_of('5', 6u) == 5u);
1595  CHECK(s012345.last_of("012345", 6u) == 5u);
1596  }
1597 
1598  // first_not_of(), last_not_of()
1599  {
1600  ryml::csubstr s012345 = "012345";
1601  CHECK(s012345.first_not_of('a') == 0u);
1602  CHECK(s012345.first_not_of("ab") == 0u);
1603  CHECK(s012345.first_not_of('0') == 1u);
1604  CHECK(s012345.first_not_of("0") == 1u);
1605  CHECK(s012345.first_not_of("01") == 2u);
1606  CHECK(s012345.first_not_of("10") == 2u);
1607  CHECK(s012345.first_not_of("012") == 3u);
1608  CHECK(s012345.first_not_of("210") == 3u);
1609  CHECK(s012345.first_not_of("0123") == 4u);
1610  CHECK(s012345.first_not_of("3210") == 4u);
1611  CHECK(s012345.first_not_of("01234") == 5u);
1612  CHECK(s012345.first_not_of("43210") == 5u);
1613  CHECK(s012345.first_not_of("012345") == ryml::npos);
1614  CHECK(s012345.first_not_of("543210") == ryml::npos);
1615  CHECK(s012345.first_not_of('5') == 0u);
1616  CHECK(s012345.first_not_of("5") == 0u);
1617  CHECK(s012345.first_not_of("45") == 0u);
1618  CHECK(s012345.first_not_of("54") == 0u);
1619  CHECK(s012345.first_not_of("345") == 0u);
1620  CHECK(s012345.first_not_of("543") == 0u);
1621  CHECK(s012345.first_not_of("2345") == 0u);
1622  CHECK(s012345.first_not_of("5432") == 0u);
1623  CHECK(s012345.first_not_of("12345") == 0u);
1624  CHECK(s012345.first_not_of("54321") == 0u);
1625  CHECK(s012345.first_not_of("012345") == ryml::npos);
1626  CHECK(s012345.first_not_of("543210") == ryml::npos);
1627  CHECK(s012345.last_not_of('a') == 5u);
1628  CHECK(s012345.last_not_of("ab") == 5u);
1629  CHECK(s012345.last_not_of('5') == 4u);
1630  CHECK(s012345.last_not_of("5") == 4u);
1631  CHECK(s012345.last_not_of("45") == 3u);
1632  CHECK(s012345.last_not_of("54") == 3u);
1633  CHECK(s012345.last_not_of("345") == 2u);
1634  CHECK(s012345.last_not_of("543") == 2u);
1635  CHECK(s012345.last_not_of("2345") == 1u);
1636  CHECK(s012345.last_not_of("5432") == 1u);
1637  CHECK(s012345.last_not_of("12345") == 0u);
1638  CHECK(s012345.last_not_of("54321") == 0u);
1639  CHECK(s012345.last_not_of("012345") == ryml::npos);
1640  CHECK(s012345.last_not_of("543210") == ryml::npos);
1641  CHECK(s012345.last_not_of('0') == 5u);
1642  CHECK(s012345.last_not_of("0") == 5u);
1643  CHECK(s012345.last_not_of("01") == 5u);
1644  CHECK(s012345.last_not_of("10") == 5u);
1645  CHECK(s012345.last_not_of("012") == 5u);
1646  CHECK(s012345.last_not_of("210") == 5u);
1647  CHECK(s012345.last_not_of("0123") == 5u);
1648  CHECK(s012345.last_not_of("3210") == 5u);
1649  CHECK(s012345.last_not_of("01234") == 5u);
1650  CHECK(s012345.last_not_of("43210") == 5u);
1651  CHECK(s012345.last_not_of("012345") == ryml::npos);
1652  CHECK(s012345.last_not_of("543210") == ryml::npos);
1653  }
1654 
1655  // first_non_empty_span()
1656  {
1657  CHECK(ryml::csubstr("foo bar").first_non_empty_span() == "foo");
1658  CHECK(ryml::csubstr(" foo bar").first_non_empty_span() == "foo");
1659  CHECK(ryml::csubstr("\n \r \t foo bar").first_non_empty_span() == "foo");
1660  CHECK(ryml::csubstr("\n \r \t foo\n\r\t bar").first_non_empty_span() == "foo");
1661  CHECK(ryml::csubstr("\n \r \t foo\n\r\t bar").first_non_empty_span() == "foo");
1662  CHECK(ryml::csubstr(",\n \r \t foo\n\r\t bar").first_non_empty_span() == ",");
1663  }
1664  // first_uint_span()
1665  {
1666  CHECK(ryml::csubstr("1234 asdkjh").first_uint_span() == "1234");
1667  CHECK(ryml::csubstr("1234\rasdkjh").first_uint_span() == "1234");
1668  CHECK(ryml::csubstr("1234\tasdkjh").first_uint_span() == "1234");
1669  CHECK(ryml::csubstr("1234\nasdkjh").first_uint_span() == "1234");
1670  CHECK(ryml::csubstr("1234]asdkjh").first_uint_span() == "1234");
1671  CHECK(ryml::csubstr("1234)asdkjh").first_uint_span() == "1234");
1672  CHECK(ryml::csubstr("1234gasdkjh").first_uint_span() == "");
1673  }
1674  // first_int_span()
1675  {
1676  CHECK(ryml::csubstr("-1234 asdkjh").first_int_span() == "-1234");
1677  CHECK(ryml::csubstr("-1234\rasdkjh").first_int_span() == "-1234");
1678  CHECK(ryml::csubstr("-1234\tasdkjh").first_int_span() == "-1234");
1679  CHECK(ryml::csubstr("-1234\nasdkjh").first_int_span() == "-1234");
1680  CHECK(ryml::csubstr("-1234]asdkjh").first_int_span() == "-1234");
1681  CHECK(ryml::csubstr("-1234)asdkjh").first_int_span() == "-1234");
1682  CHECK(ryml::csubstr("-1234gasdkjh").first_int_span() == "");
1683  }
1684  // first_real_span()
1685  {
1686  CHECK(ryml::csubstr("-1234 asdkjh").first_real_span() == "-1234");
1687  CHECK(ryml::csubstr("-1234\rasdkjh").first_real_span() == "-1234");
1688  CHECK(ryml::csubstr("-1234\tasdkjh").first_real_span() == "-1234");
1689  CHECK(ryml::csubstr("-1234\nasdkjh").first_real_span() == "-1234");
1690  CHECK(ryml::csubstr("-1234]asdkjh").first_real_span() == "-1234");
1691  CHECK(ryml::csubstr("-1234)asdkjh").first_real_span() == "-1234");
1692  CHECK(ryml::csubstr("-1234gasdkjh").first_real_span() == "");
1693  CHECK(ryml::csubstr("1.234 asdkjh").first_real_span() == "1.234");
1694  CHECK(ryml::csubstr("1.234e+5 asdkjh").first_real_span() == "1.234e+5");
1695  CHECK(ryml::csubstr("1.234e-5 asdkjh").first_real_span() == "1.234e-5");
1696  CHECK(ryml::csubstr("1.234 asdkjh").first_real_span() == "1.234");
1697  CHECK(ryml::csubstr("1.234e+5 asdkjh").first_real_span() == "1.234e+5");
1698  CHECK(ryml::csubstr("1.234e-5 asdkjh").first_real_span() == "1.234e-5");
1699  CHECK(ryml::csubstr("-1.234 asdkjh").first_real_span() == "-1.234");
1700  CHECK(ryml::csubstr("-1.234e+5 asdkjh").first_real_span() == "-1.234e+5");
1701  CHECK(ryml::csubstr("-1.234e-5 asdkjh").first_real_span() == "-1.234e-5");
1702  // hexadecimal real numbers
1703  CHECK(ryml::csubstr("0x1.e8480p+19 asdkjh").first_real_span() == "0x1.e8480p+19");
1704  CHECK(ryml::csubstr("0x1.e8480p-19 asdkjh").first_real_span() == "0x1.e8480p-19");
1705  CHECK(ryml::csubstr("-0x1.e8480p+19 asdkjh").first_real_span() == "-0x1.e8480p+19");
1706  CHECK(ryml::csubstr("-0x1.e8480p-19 asdkjh").first_real_span() == "-0x1.e8480p-19");
1707  CHECK(ryml::csubstr("+0x1.e8480p+19 asdkjh").first_real_span() == "+0x1.e8480p+19");
1708  CHECK(ryml::csubstr("+0x1.e8480p-19 asdkjh").first_real_span() == "+0x1.e8480p-19");
1709  // binary real numbers
1710  CHECK(ryml::csubstr("0b101.011p+19 asdkjh").first_real_span() == "0b101.011p+19");
1711  CHECK(ryml::csubstr("0b101.011p-19 asdkjh").first_real_span() == "0b101.011p-19");
1712  CHECK(ryml::csubstr("-0b101.011p+19 asdkjh").first_real_span() == "-0b101.011p+19");
1713  CHECK(ryml::csubstr("-0b101.011p-19 asdkjh").first_real_span() == "-0b101.011p-19");
1714  CHECK(ryml::csubstr("+0b101.011p+19 asdkjh").first_real_span() == "+0b101.011p+19");
1715  CHECK(ryml::csubstr("+0b101.011p-19 asdkjh").first_real_span() == "+0b101.011p-19");
1716  // octal real numbers
1717  CHECK(ryml::csubstr("0o173.045p+19 asdkjh").first_real_span() == "0o173.045p+19");
1718  CHECK(ryml::csubstr("0o173.045p-19 asdkjh").first_real_span() == "0o173.045p-19");
1719  CHECK(ryml::csubstr("-0o173.045p+19 asdkjh").first_real_span() == "-0o173.045p+19");
1720  CHECK(ryml::csubstr("-0o173.045p-19 asdkjh").first_real_span() == "-0o173.045p-19");
1721  CHECK(ryml::csubstr("+0o173.045p+19 asdkjh").first_real_span() == "+0o173.045p+19");
1722  CHECK(ryml::csubstr("+0o173.045p-19 asdkjh").first_real_span() == "+0o173.045p-19");
1723  }
1724  // see also is_number()
1725 
1726  // basename(), dirname(), extshort(), extlong()
1727  {
1728  CHECK(ryml::csubstr("/path/to/file.tar.gz").basename() == "file.tar.gz");
1729  CHECK(ryml::csubstr("/path/to/file.tar.gz").dirname() == "/path/to/");
1730  CHECK(ryml::csubstr("C:\\path\\to\\file.tar.gz").basename('\\') == "file.tar.gz");
1731  CHECK(ryml::csubstr("C:\\path\\to\\file.tar.gz").dirname('\\') == "C:\\path\\to\\");
1732  CHECK(ryml::csubstr("/path/to/file.tar.gz").extshort() == "gz");
1733  CHECK(ryml::csubstr("/path/to/file.tar.gz").extlong() == "tar.gz");
1734  CHECK(ryml::csubstr("/path/to/file.tar.gz").name_wo_extshort() == "/path/to/file.tar");
1735  CHECK(ryml::csubstr("/path/to/file.tar.gz").name_wo_extlong() == "/path/to/file");
1736  }
1737 
1738  // split()
1739  {
1740  using namespace ryml;
1741  csubstr parts[] = {"aa", "bb", "cc", "dd", "ee", "ff"};
1742  {
1743  size_t count = 0;
1744  for(csubstr part : csubstr("aa/bb/cc/dd/ee/ff").split('/'))
1745  CHECK(part == parts[count++]);
1746  CHECK(count == 6u);
1747  }
1748  {
1749  size_t count = 0;
1750  for(csubstr part : csubstr("aa.bb.cc.dd.ee.ff").split('.'))
1751  CHECK(part == parts[count++]);
1752  CHECK(count == 6u);
1753  }
1754  {
1755  size_t count = 0;
1756  for(csubstr part : csubstr("aa-bb-cc-dd-ee-ff").split('-'))
1757  CHECK(part == parts[count++]);
1758  CHECK(count == 6u);
1759  }
1760  // see also next_split()
1761  }
1762 
1763  // pop_left(), pop_right() --- non-greedy version
1764  // gpop_left(), gpop_right() --- greedy version
1765  {
1766  const bool skip_empty = true;
1767  // pop_left(): pop the last element from the left
1768  CHECK(ryml::csubstr( "0/1/2" ). pop_left('/' ) == "0" );
1769  CHECK(ryml::csubstr( "/0/1/2" ). pop_left('/' ) == "" );
1770  CHECK(ryml::csubstr("//0/1/2" ). pop_left('/' ) == "" );
1771  CHECK(ryml::csubstr( "0/1/2" ). pop_left('/', skip_empty) == "0" );
1772  CHECK(ryml::csubstr( "/0/1/2" ). pop_left('/', skip_empty) == "/0" );
1773  CHECK(ryml::csubstr("//0/1/2" ). pop_left('/', skip_empty) == "//0" );
1774  // gpop_left(): pop all but the first element (greedy pop)
1775  CHECK(ryml::csubstr( "0/1/2" ).gpop_left('/' ) == "0/1" );
1776  CHECK(ryml::csubstr( "/0/1/2" ).gpop_left('/' ) == "/0/1" );
1777  CHECK(ryml::csubstr("//0/1/2" ).gpop_left('/' ) == "//0/1" );
1778  CHECK(ryml::csubstr( "0/1/2/" ).gpop_left('/' ) == "0/1/2");
1779  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_left('/' ) == "/0/1/2");
1780  CHECK(ryml::csubstr("//0/1/2/" ).gpop_left('/' ) == "//0/1/2");
1781  CHECK(ryml::csubstr( "0/1/2//" ).gpop_left('/' ) == "0/1/2/");
1782  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_left('/' ) == "/0/1/2/");
1783  CHECK(ryml::csubstr("//0/1/2//" ).gpop_left('/' ) == "//0/1/2/");
1784  CHECK(ryml::csubstr( "0/1/2" ).gpop_left('/', skip_empty) == "0/1" );
1785  CHECK(ryml::csubstr( "/0/1/2" ).gpop_left('/', skip_empty) == "/0/1" );
1786  CHECK(ryml::csubstr("//0/1/2" ).gpop_left('/', skip_empty) == "//0/1" );
1787  CHECK(ryml::csubstr( "0/1/2/" ).gpop_left('/', skip_empty) == "0/1" );
1788  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_left('/', skip_empty) == "/0/1" );
1789  CHECK(ryml::csubstr("//0/1/2/" ).gpop_left('/', skip_empty) == "//0/1" );
1790  CHECK(ryml::csubstr( "0/1/2//" ).gpop_left('/', skip_empty) == "0/1" );
1791  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_left('/', skip_empty) == "/0/1" );
1792  CHECK(ryml::csubstr("//0/1/2//" ).gpop_left('/', skip_empty) == "//0/1" );
1793  // pop_right(): pop the last element from the right
1794  CHECK(ryml::csubstr( "0/1/2" ). pop_right('/' ) == "2" );
1795  CHECK(ryml::csubstr( "0/1/2/" ). pop_right('/' ) == "" );
1796  CHECK(ryml::csubstr( "0/1/2//" ). pop_right('/' ) == "" );
1797  CHECK(ryml::csubstr( "0/1/2" ). pop_right('/', skip_empty) == "2" );
1798  CHECK(ryml::csubstr( "0/1/2/" ). pop_right('/', skip_empty) == "2/" );
1799  CHECK(ryml::csubstr( "0/1/2//" ). pop_right('/', skip_empty) == "2//" );
1800  // gpop_right(): pop all but the first element (greedy pop)
1801  CHECK(ryml::csubstr( "0/1/2" ).gpop_right('/' ) == "1/2" );
1802  CHECK(ryml::csubstr( "0/1/2/" ).gpop_right('/' ) == "1/2/" );
1803  CHECK(ryml::csubstr( "0/1/2//" ).gpop_right('/' ) == "1/2//");
1804  CHECK(ryml::csubstr( "/0/1/2" ).gpop_right('/' ) == "0/1/2" );
1805  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_right('/' ) == "0/1/2/" );
1806  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_right('/' ) == "0/1/2//");
1807  CHECK(ryml::csubstr("//0/1/2" ).gpop_right('/' ) == "/0/1/2" );
1808  CHECK(ryml::csubstr("//0/1/2/" ).gpop_right('/' ) == "/0/1/2/" );
1809  CHECK(ryml::csubstr("//0/1/2//" ).gpop_right('/' ) == "/0/1/2//");
1810  CHECK(ryml::csubstr( "0/1/2" ).gpop_right('/', skip_empty) == "1/2" );
1811  CHECK(ryml::csubstr( "0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1812  CHECK(ryml::csubstr( "0/1/2//" ).gpop_right('/', skip_empty) == "1/2//");
1813  CHECK(ryml::csubstr( "/0/1/2" ).gpop_right('/', skip_empty) == "1/2" );
1814  CHECK(ryml::csubstr( "/0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1815  CHECK(ryml::csubstr( "/0/1/2//" ).gpop_right('/', skip_empty) == "1/2//");
1816  CHECK(ryml::csubstr("//0/1/2" ).gpop_right('/', skip_empty) == "1/2" );
1817  CHECK(ryml::csubstr("//0/1/2/" ).gpop_right('/', skip_empty) == "1/2/" );
1818  CHECK(ryml::csubstr("//0/1/2//" ).gpop_right('/', skip_empty) == "1/2//");
1819  }
1820 }
1821 
1822 
1823 //-----------------------------------------------------------------------------
1824 
1825 
1826 /** demonstrate how to load a YAML file from disk to parse with ryml.
1827  *
1828  * ryml offers no overload to directly parse files from disk; it only
1829  * parses source buffers (which may be mutable or immutable). It is
1830  * up to the caller to load the file contents into a buffer before
1831  * parsing with ryml.
1832  *
1833  * But that does not mean that loading a file is unimportant. There
1834  * are many ways to achieve this in C++, but for convenience and to
1835  * enable you to quickly get up to speed, here is an example
1836  * implementation loading a file from disk and then parsing the
1837  * resulting buffer with ryml.
1838  * @see doc_parse */
1840 {
1841  const char filename[] = "ryml_example.yml";
1842  ryml::csubstr yaml = ""
1843  "foo: 1" "\n"
1844  "bar:" "\n"
1845  "- 2" "\n"
1846  "- 3" "\n";
1847  // because this is a minimal sample, it assumes nothing on the
1848  // environment/OS (other than that it can read/write files). So we
1849  // create the file on the fly:
1850  file_put_contents(filename, yaml);
1851 
1852  // now we can load it into a std::string (for example):
1853  {
1854  std::string contents = file_get_contents<std::string>(filename);
1855  ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(contents)); // immutable (csubstr) overload
1856  CHECK(tree["foo"].val() == "1");
1857  CHECK(tree["bar"][0].val() == "2");
1858  CHECK(tree["bar"][1].val() == "3");
1859  }
1860 
1861  // or we can use a vector<char> instead:
1862  {
1863  std::vector<char> contents = file_get_contents<std::vector<char>>(filename);
1864  ryml::Tree tree = ryml::parse_in_place(ryml::to_substr(contents)); // mutable (csubstr) overload
1865  CHECK(tree["foo"].val() == "1");
1866  CHECK(tree["bar"][0].val() == "2");
1867  CHECK(tree["bar"][1].val() == "3");
1868  }
1869 
1870  // generally, any contiguous char container can be used with ryml,
1871  // provided that the ryml::substr/ryml::csubstr view can be
1872  // created out of it.
1873  //
1874  // ryml provides the overloads above for these two containers, but
1875  // if you are using another container it should be very easy (only
1876  // requires pointer and length).
1877 }
1878 
1879 
1880 //-----------------------------------------------------------------------------
1881 
1882 /** demonstrate in-place parsing of a mutable YAML source buffer.
1883  * @see doc_parse */
1885 {
1886  // Like the name suggests, parse_in_place() directly mutates the
1887  // source buffer in place
1888  char src[] = "{foo: 1, bar: [2, 3]}"; // ryml can parse in situ
1889  ryml::substr srcview = src; // a mutable view to the source buffer
1890  ryml::Tree tree = ryml::parse_in_place(srcview); // you can also reuse the tree and/or parser
1891  ryml::ConstNodeRef root = tree.crootref(); // get a constant reference to the root
1892 
1893  CHECK(root.is_map());
1894  CHECK(root["foo"].is_keyval());
1895  CHECK(root["foo"].key() == "foo");
1896  CHECK(root["foo"].val() == "1");
1897  CHECK(root["bar"].is_seq());
1898  CHECK(root["bar"].has_key());
1899  CHECK(root["bar"].key() == "bar");
1900  CHECK(root["bar"][0].val() == "2");
1901  CHECK(root["bar"][1].val() == "3");
1902 
1903  // deserializing:
1904  int foo = 0, bar0 = 0, bar1 = 0;
1905  root["foo"] >> foo;
1906  root["bar"][0] >> bar0;
1907  root["bar"][1] >> bar1;
1908  CHECK(foo == 1);
1909  CHECK(bar0 == 2);
1910  CHECK(bar1 == 3);
1911 
1912  // after parsing, the tree holds views to the source buffer:
1913  CHECK(root["foo"].val().data() == src + strlen("{foo: "));
1914  CHECK(root["foo"].val().begin() == src + strlen("{foo: "));
1915  CHECK(root["foo"].val().end() == src + strlen("{foo: 1"));
1916  CHECK(root["foo"].val().is_sub(srcview)); // equivalent to the previous three assertions
1917  CHECK(root["bar"][0].val().data() == src + strlen("{foo: 1, bar: ["));
1918  CHECK(root["bar"][0].val().begin() == src + strlen("{foo: 1, bar: ["));
1919  CHECK(root["bar"][0].val().end() == src + strlen("{foo: 1, bar: [2"));
1920  CHECK(root["bar"][0].val().is_sub(srcview)); // equivalent to the previous three assertions
1921  CHECK(root["bar"][1].val().data() == src + strlen("{foo: 1, bar: [2, "));
1922  CHECK(root["bar"][1].val().begin() == src + strlen("{foo: 1, bar: [2, "));
1923  CHECK(root["bar"][1].val().end() == src + strlen("{foo: 1, bar: [2, 3"));
1924  CHECK(root["bar"][1].val().is_sub(srcview)); // equivalent to the previous three assertions
1925 
1926  // NOTE. parse_in_place() cannot accept ryml::csubstr
1927  // so this will cause a /compile/ error:
1928  ryml::csubstr csrcview = srcview; // ok, can assign from mutable to immutable
1929  //tree = ryml::parse_in_place(csrcview); // compile error, cannot mutate an immutable view
1930  (void)csrcview;
1931 }
1932 
1933 
1934 //-----------------------------------------------------------------------------
1935 
1936 /** demonstrate parsing of a read-only YAML source buffer
1937  * @see doc_parse */
1939 {
1940  // to parse read-only memory, ryml will copy first to the tree's
1941  // arena, and then parse the copied buffer:
1942  ryml::Tree tree = ryml::parse_in_arena("{foo: 1, bar: [2, 3]}");
1943  ryml::ConstNodeRef root = tree.crootref(); // get a const reference to the root
1944 
1945  CHECK(root.is_map());
1946  CHECK(root["foo"].is_keyval());
1947  CHECK(root["foo"].key() == "foo");
1948  CHECK(root["foo"].val() == "1");
1949  CHECK(root["bar"].is_seq());
1950  CHECK(root["bar"].has_key());
1951  CHECK(root["bar"].key() == "bar");
1952  CHECK(root["bar"][0].val() == "2");
1953  CHECK(root["bar"][1].val() == "3");
1954 
1955  // deserializing:
1956  int foo = 0, bar0 = 0, bar1 = 0;
1957  root["foo"] >> foo;
1958  root["bar"][0] >> bar0;
1959  root["bar"][1] >> bar1;
1960  CHECK(foo == 1);
1961  CHECK(bar0 == 2);
1962  CHECK(bar1 == 3);
1963 
1964  // NOTE. parse_in_arena() cannot accept ryml::substr. Overloads
1965  // receiving substr buffers are declared, but intentionally left
1966  // undefined, so this will cause a /linker/ error
1967  char src[] = "{foo: is it really true}";
1968  ryml::substr srcview = src;
1969  //tree = ryml::parse_in_place(srcview); // linker error, overload intentionally undefined
1970 
1971  // If you really intend to parse a mutable buffer in the arena,
1972  // then simply convert it to immutable prior to calling
1973  // parse_in_arena():
1974  ryml::csubstr csrcview = srcview; // assigning from src also works
1975  tree = ryml::parse_in_arena(csrcview); // OK! csrcview is immutable
1976  CHECK(tree["foo"].val() == "is it really true");
1977 }
1978 
1979 
1980 //-----------------------------------------------------------------------------
1981 
1982 /** demonstrate reuse/modification of tree when parsing
1983  * @see doc_parse */
1985 {
1986  ryml::Tree tree;
1987 
1988  // it will always be faster if the tree's size is conveniently reserved:
1989  tree.reserve(30); // reserve 30 nodes (good enough for this sample)
1990  // if you are using the tree's arena to serialize data,
1991  // then reserve also the arena's size:
1992  tree.reserve_arena(256); // reserve 256 characters (good enough for this sample)
1993 
1994  // now parse into the tree:
1995  ryml::csubstr yaml = ""
1996  "foo: 1" "\n"
1997  "bar: [2,3]" "\n"
1998  "";
1999  ryml::parse_in_arena(yaml, &tree);
2000 
2001  ryml::ConstNodeRef root = tree.crootref();
2002  CHECK(root.num_children() == 2);
2003  CHECK(root.is_map());
2004  CHECK(root["foo"].is_keyval());
2005  CHECK(root["foo"].key() == "foo");
2006  CHECK(root["foo"].val() == "1");
2007  CHECK(root["bar"].is_seq());
2008  CHECK(root["bar"].has_key());
2009  CHECK(root["bar"].key() == "bar");
2010  CHECK(root["bar"][0].val() == "2");
2011  CHECK(root["bar"][1].val() == "3");
2012  CHECK(ryml::emitrs_yaml<std::string>(tree) == yaml);
2013 
2014  // WATCHOUT: parsing into an existing tree will APPEND to it:
2015  ryml::parse_in_arena("{foo2: 12, bar2: [22, 32]}", &tree);
2016  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2017  "foo: 1" "\n"
2018  "bar: [2,3]" "\n"
2019  "foo2: 12" "\n"
2020  "bar2: [22,32]" "\n"
2021  "");
2022  CHECK(root.num_children() == 4);
2023  CHECK(root["foo2"].is_keyval());
2024  CHECK(root["foo2"].key() == "foo2");
2025  CHECK(root["foo2"].val() == "12");
2026  CHECK(root["bar2"].is_seq());
2027  CHECK(root["bar2"].has_key());
2028  CHECK(root["bar2"].key() == "bar2");
2029  CHECK(root["bar2"][0].val() == "22");
2030  CHECK(root["bar2"][1].val() == "32");
2031 
2032  // if you want to fully replace the tree, you need to clear first
2033  // before parsing into an existing tree:
2034  tree.clear();
2035  tree.clear_arena(); // you may or may not want to clear the arena
2036  ryml::parse_in_arena("- a\n- b\n- {x0: 1, x1: 2}", &tree);
2037  CHECK(ryml::emitrs_yaml<std::string>(tree) == "- a\n- b\n- {x0: 1,x1: 2}\n");
2038  CHECK(root.is_seq());
2039  CHECK(root[0].val() == "a");
2040  CHECK(root[1].val() == "b");
2041  CHECK(root[2].is_map());
2042  CHECK(root[2]["x0"].val() == "1");
2043  CHECK(root[2]["x1"].val() == "2");
2044 
2045  // we can parse directly into a node nested deep in an existing tree:
2046  ryml::NodeRef mroot = tree.rootref(); // modifiable root
2047  ryml::parse_in_arena("champagne: Dom Perignon\ncoffee: Arabica", mroot.append_child());
2048  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2049  "- a" "\n"
2050  "- b" "\n"
2051  "- {x0: 1,x1: 2}" "\n"
2052  "- champagne: Dom Perignon" "\n"
2053  " coffee: Arabica" "\n"
2054  "");
2055  CHECK(root.is_seq());
2056  CHECK(root[0].val() == "a");
2057  CHECK(root[1].val() == "b");
2058  CHECK(root[2].is_map());
2059  CHECK(root[2]["x0"].val() == "1");
2060  CHECK(root[2]["x1"].val() == "2");
2061  CHECK(root[3].is_map());
2062  CHECK(root[3]["champagne"].val() == "Dom Perignon");
2063  CHECK(root[3]["coffee"].val() == "Arabica");
2064 
2065  // watchout: to add to an existing node within a map, the node's
2066  // key must be separately set first:
2067  ryml::NodeRef more = mroot[3].append_child({ryml::KEYMAP, "more"});
2068  ryml::NodeRef beer = mroot[3].append_child({ryml::KEYSEQ, "beer"});
2069  ryml::NodeRef always = mroot[3].append_child({ryml::KEY, "always"});
2070  ryml::parse_in_arena("{vinho verde: Soalheiro, vinho tinto: Redoma 2017}", more);
2071  ryml::parse_in_arena("- Rochefort 10\n- Busch\n- Leffe Rituel", beer);
2072  ryml::parse_in_arena("lots\nof\nwater", always);
2073  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2074  "- a" "\n"
2075  "- b" "\n"
2076  "- {x0: 1,x1: 2}" "\n"
2077  "- champagne: Dom Perignon" "\n"
2078  " coffee: Arabica" "\n"
2079  // note these were added:
2080  " more:" "\n"
2081  " vinho verde: Soalheiro" "\n"
2082  " vinho tinto: Redoma 2017" "\n"
2083  " beer:" "\n"
2084  " - Rochefort 10" "\n"
2085  " - Busch" "\n"
2086  " - Leffe Rituel" "\n"
2087  " always: lots of water" "\n"
2088  "");
2089 
2090  // can append at the top:
2091  ryml::parse_in_arena("- foo\n- bar\n- baz\n- bat", mroot);
2092  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2093  "- a" "\n"
2094  "- b" "\n"
2095  "- {x0: 1,x1: 2}" "\n"
2096  "- champagne: Dom Perignon" "\n"
2097  " coffee: Arabica" "\n"
2098  " more:" "\n"
2099  " vinho verde: Soalheiro" "\n"
2100  " vinho tinto: Redoma 2017" "\n"
2101  " beer:" "\n"
2102  " - Rochefort 10" "\n"
2103  " - Busch" "\n"
2104  " - Leffe Rituel" "\n"
2105  " always: lots of water" "\n"
2106  // note these were added
2107  "- foo" "\n"
2108  "- bar" "\n"
2109  "- baz" "\n"
2110  "- bat" "\n"
2111  "");
2112 
2113  // or nested:
2114  ryml::parse_in_arena("[Kasteel Donker]", beer);
2115  CHECK(ryml::emitrs_yaml<std::string>(tree) ==
2116  "- a" "\n"
2117  "- b" "\n"
2118  "- {x0: 1,x1: 2}" "\n"
2119  "- champagne: Dom Perignon" "\n"
2120  " coffee: Arabica" "\n"
2121  " more:" "\n"
2122  " vinho verde: Soalheiro" "\n"
2123  " vinho tinto: Redoma 2017" "\n"
2124  " beer:" "\n"
2125  " - Rochefort 10" "\n"
2126  " - Busch" "\n"
2127  " - Leffe Rituel" "\n"
2128  " - Kasteel Donker" "\n" // added this
2129  " always: lots of water" "\n"
2130  "- foo" "\n"
2131  "- bar" "\n"
2132  "- baz" "\n"
2133  "- bat" "\n"
2134  "");
2135 }
2136 
2137 
2138 //-----------------------------------------------------------------------------
2139 
2140 /** Demonstrates reuse of an existing parser. Doing this is
2141  * recommended when multiple files are parsed.
2142  * @see doc_parse */
2144 {
2145  ryml::EventHandlerTree evt_handler = {};
2146  ryml::Parser parser(&evt_handler);
2147 
2148  // it is also advised to reserve the parser depth
2149  // to the expected depth of the data tree:
2150  parser.reserve_stack(10); // uses small storage optimization
2151  // defaulting to 16 depth, so this
2152  // instruction is a no-op, and the stack
2153  // will located in the parser object.
2154  parser.reserve_stack(20); // But this will cause an allocation
2155  // because it is above 16.
2156 
2157  ryml::csubstr yaml = "[Dom Perignon,Gosset Grande Reserve,Jacquesson 742]";
2158  ryml::Tree champagnes = parse_in_arena(&parser, "champagnes.yml", yaml);
2159  CHECK(ryml::emitrs_yaml<std::string>(champagnes) == yaml);
2160 
2161  yaml = "[Rochefort 10,Busch,Leffe Rituel,Kasteel Donker]";
2162  ryml::Tree beers = parse_in_arena(&parser, "beers.yml", yaml);
2163  CHECK(ryml::emitrs_yaml<std::string>(beers) == yaml);
2164 }
2165 
2166 
2167 //-----------------------------------------------------------------------------
2168 
2169 /** for ultimate speed when parsing multiple times, reuse both the
2170  * tree and parser
2171  * @see doc_parse */
2173 {
2174  ryml::Tree tree;
2175  // it will always be faster if the tree's size is conveniently reserved:
2176  tree.reserve(30); // reserve 30 nodes (good enough for this sample)
2177  // if you are using the tree's arena to serialize data,
2178  // then reserve also the arena's size:
2179  tree.reserve(256); // reserve 256 characters (good enough for this sample)
2180 
2181  ryml::EventHandlerTree evt_handler;
2182  ryml::Parser parser(&evt_handler);
2183  // it is also advised to reserve the parser depth
2184  // to the expected depth of the data tree:
2185  parser.reserve_stack(10); // the parser uses small storage
2186  // optimization defaulting to 16 depth,
2187  // so this instruction is a no-op, and
2188  // the stack will be located in the
2189  // parser object.
2190  parser.reserve_stack(20); // But this will cause an allocation
2191  // because it is above 16.
2192 
2193  ryml::csubstr champagnes = ""
2194  "- Dom Perignon\n"
2195  "- Gosset Grande Reserve\n"
2196  "- Jacquesson 742\n"
2197  "";
2198  ryml::csubstr beers = ""
2199  "- Rochefort 10\n"
2200  "- Busch\n"
2201  "- Leffe Rituel\n"
2202  "- Kasteel Donker\n"
2203  "";
2204  ryml::csubstr wines = ""
2205  "- Soalheiro\n"
2206  "- Niepoort Redoma 2017\n"
2207  "- Vina Esmeralda\n"
2208  "";
2209 
2210  parse_in_arena(&parser, "champagnes.yml", champagnes, &tree);
2211  CHECK(ryml::emitrs_yaml<std::string>(tree) == champagnes);
2212 
2213  // watchout: this will APPEND to the given tree:
2214  parse_in_arena(&parser, "beers.yml", beers, &tree);
2215  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2216  "- Dom Perignon\n"
2217  "- Gosset Grande Reserve\n"
2218  "- Jacquesson 742\n"
2219  "- Rochefort 10\n"
2220  "- Busch\n"
2221  "- Leffe Rituel\n"
2222  "- Kasteel Donker\n"
2223  "");
2224 
2225  // if you don't wish to append, clear the tree first:
2226  tree.clear();
2227  parse_in_arena(&parser, "wines.yml", wines, &tree);
2228  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2229  "- Soalheiro\n"
2230  "- Niepoort Redoma 2017\n"
2231  "- Vina Esmeralda\n"
2232  "");
2233 }
2234 
2235 
2236 //-----------------------------------------------------------------------------
2237 
2238 /** shows how to programatically iterate through trees
2239  * @see doc_tree
2240  * @see doc_node_classes
2241  */
2243 {
2244  const ryml::Tree tree = ryml::parse_in_arena(
2245  "doe: a deer, a female deer" "\n"
2246  "ray: a drop of golden sun" "\n"
2247  "pi: 3.14159" "\n"
2248  "xmas: true" "\n"
2249  "french-hens: 3" "\n"
2250  "calling-birds:" "\n"
2251  " - huey" "\n"
2252  " - dewey" "\n"
2253  " - louie" "\n"
2254  " - fred" "\n"
2255  "xmas-fifth-day:" "\n"
2256  " calling-birds: four" "\n"
2257  " french-hens: 3" "\n"
2258  " golden-rings: 5" "\n"
2259  " partridges:" "\n"
2260  " count: 1" "\n"
2261  " location: a pear tree" "\n"
2262  " turtle-doves: two" "\n"
2263  "cars: GTO" "\n"
2264  "");
2265  ryml::ConstNodeRef root = tree.crootref();
2266 
2267  // iterate children
2268  {
2269  std::vector<ryml::csubstr> keys, vals; // to store all the root-level keys, vals
2270  for(ryml::ConstNodeRef n : root.children())
2271  {
2272  keys.emplace_back(n.key());
2273  vals.emplace_back(n.has_val() ? n.val() : ryml::csubstr{});
2274  }
2275  CHECK(keys.size() >= 6);
2276  CHECK(vals.size() >= 6);
2277  if(keys.size() >= 6 && vals.size() >= 6)
2278  {
2279  CHECK(keys[0] == "doe");
2280  CHECK(vals[0] == "a deer, a female deer");
2281  CHECK(keys[1] == "ray");
2282  CHECK(vals[1] == "a drop of golden sun");
2283  CHECK(keys[2] == "pi");
2284  CHECK(vals[2] == "3.14159");
2285  CHECK(keys[3] == "xmas");
2286  CHECK(vals[3] == "true");
2287  CHECK(root[5].has_key());
2288  CHECK(root[5].is_seq());
2289  CHECK(root[5].key() == "calling-birds");
2290  CHECK(!root[5].has_val()); // it is a map, so not a val
2291  //CHECK(root[5].val() == ""); // ERROR! node does not have a val.
2292  CHECK(keys[5] == "calling-birds");
2293  CHECK(vals[5] == "");
2294  }
2295  }
2296 
2297  // iterate siblings
2298  {
2299  size_t count = 0;
2300  ryml::csubstr calling_birds[] = {"huey", "dewey", "louie", "fred"};
2301  for(ryml::ConstNodeRef n : root["calling-birds"][2].siblings())
2302  CHECK(n.val() == calling_birds[count++]);
2303  CHECK(count == 4u);
2304  }
2305 }
2306 
2307 
2308 //-----------------------------------------------------------------------------
2309 
2310 /** shows how to programatically create trees
2311  * @see doc_tree
2312  * @see doc_node_classes
2313  * */
2315 {
2316  ryml::NodeRef doe;
2317  CHECK(doe.invalid()); // it's pointing at nowhere
2318 
2319  ryml::Tree tree;
2320  ryml::NodeRef root = tree.rootref();
2321  root |= ryml::MAP; // mark root as a map
2322  doe = root["doe"];
2323  CHECK(!doe.invalid()); // it's now pointing at the tree
2324  CHECK(doe.is_seed()); // but the tree has nothing there, so this is only a seed
2325 
2326  // set the value of the node
2327  const char a_deer[] = "a deer, a female deer";
2328  doe = a_deer;
2329  // now the node really exists in the tree, and this ref is no
2330  // longer a seed:
2331  CHECK(!doe.is_seed());
2332  // WATCHOUT for lifetimes:
2333  CHECK(doe.val().str == a_deer); // it is pointing at the initial string
2334  // If you need to avoid lifetime dependency, serialize the data:
2335  {
2336  std::string a_drop = "a drop of golden sun";
2337  // this will copy the string to the tree's arena:
2338  // (see the serialization samples below)
2339  root["ray"] << a_drop;
2340  // and now you can modify the original string without changing
2341  // the tree:
2342  a_drop[0] = 'Z';
2343  a_drop[1] = 'Z';
2344  }
2345  CHECK(root["ray"].val() == "a drop of golden sun");
2346 
2347  // etc.
2348  root["pi"] << ryml::fmt::real(3.141592654, 5);
2349  root["xmas"] << ryml::fmt::boolalpha(true);
2350  root["french-hens"] << 3;
2351  ryml::NodeRef calling_birds = root["calling-birds"];
2352  calling_birds |= ryml::SEQ;
2353  calling_birds.append_child() = "huey";
2354  calling_birds.append_child() = "dewey";
2355  calling_birds.append_child() = "louie";
2356  calling_birds.append_child() = "fred";
2357  ryml::NodeRef xmas5 = root["xmas-fifth-day"];
2358  xmas5 |= ryml::MAP;
2359  xmas5["calling-birds"] = "four";
2360  xmas5["french-hens"] << 3;
2361  xmas5["golden-rings"] << 5;
2362  xmas5["partridges"] |= ryml::MAP;
2363  xmas5["partridges"]["count"] << 1;
2364  xmas5["partridges"]["location"] = "a pear tree";
2365  xmas5["turtle-doves"] = "two";
2366  root["cars"] = "GTO";
2367 
2368  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2369  "doe: a deer, a female deer" "\n"
2370  "ray: a drop of golden sun" "\n"
2371  "pi: 3.14159" "\n"
2372  "xmas: true" "\n"
2373  "french-hens: 3" "\n"
2374  "calling-birds:" "\n"
2375  " - huey" "\n"
2376  " - dewey" "\n"
2377  " - louie" "\n"
2378  " - fred" "\n"
2379  "xmas-fifth-day:" "\n"
2380  " calling-birds: four" "\n"
2381  " french-hens: 3" "\n"
2382  " golden-rings: 5" "\n"
2383  " partridges:" "\n"
2384  " count: 1" "\n"
2385  " location: a pear tree" "\n"
2386  " turtle-doves: two" "\n"
2387  "cars: GTO" "\n"
2388  "");
2389 }
2390 
2391 
2392 //-----------------------------------------------------------------------------
2393 
2394 /** demonstrates explicit and implicit interaction with the tree's string arena.
2395  * Notice that ryml only holds strings in the tree's nodes. */
2397 {
2398  // mutable buffers are parsed in situ:
2399  {
2400  char buf[] = "[a, b, c, d]";
2401  ryml::substr yml = buf;
2402  ryml::Tree tree = ryml::parse_in_place(yml);
2403  // notice the arena is empty:
2404  CHECK(tree.arena().empty());
2405  // and the tree is pointing at the original buffer:
2406  ryml::NodeRef root = tree.rootref();
2407  CHECK(root[0].val().is_sub(yml));
2408  CHECK(root[1].val().is_sub(yml));
2409  CHECK(root[2].val().is_sub(yml));
2410  CHECK(root[3].val().is_sub(yml));
2411  CHECK(yml.is_super(root[0].val()));
2412  CHECK(yml.is_super(root[1].val()));
2413  CHECK(yml.is_super(root[2].val()));
2414  CHECK(yml.is_super(root[3].val()));
2415  }
2416 
2417  // when parsing immutable buffers, the buffer is first copied to the
2418  // tree's arena; the copy in the arena is then the buffer which is
2419  // actually parsed
2420  {
2421  ryml::csubstr yml = "[a, b, c, d]";
2422  ryml::Tree tree = ryml::parse_in_arena(yml);
2423  // notice the buffer was copied to the arena:
2424  CHECK(tree.arena().data() != yml.data());
2425  CHECK(tree.arena() == yml);
2426  // and the tree is pointing at the arena instead of to the
2427  // original buffer:
2428  ryml::NodeRef root = tree.rootref();
2429  ryml::csubstr arena = tree.arena();
2430  CHECK(root[0].val().is_sub(arena));
2431  CHECK(root[1].val().is_sub(arena));
2432  CHECK(root[2].val().is_sub(arena));
2433  CHECK(root[3].val().is_sub(arena));
2434  CHECK(arena.is_super(root[0].val()));
2435  CHECK(arena.is_super(root[1].val()));
2436  CHECK(arena.is_super(root[2].val()));
2437  CHECK(arena.is_super(root[3].val()));
2438  }
2439 
2440  // the arena is also used when the data is serialized to string
2441  // with NodeRef::operator<<(): mutable buffer
2442  {
2443  char buf[] = "[a, b, c, d]"; // mutable
2444  ryml::substr yml = buf;
2445  ryml::Tree tree = ryml::parse_in_place(yml);
2446  // notice the arena is empty:
2447  CHECK(tree.arena().empty());
2448  ryml::NodeRef root = tree.rootref();
2449 
2450  // serialize an integer, and mutate the tree
2451  CHECK(root[2].val() == "c");
2452  CHECK(root[2].val().is_sub(yml)); // val is first pointing at the buffer
2453  root[2] << 12345;
2454  CHECK(root[2].val() == "12345");
2455  CHECK(root[2].val().is_sub(tree.arena())); // now val is pointing at the arena
2456  // notice the serialized string was appended to the tree's arena:
2457  CHECK(tree.arena() == "12345");
2458 
2459  // serialize an integer, and mutate the tree
2460  CHECK(root[3].val() == "d");
2461  CHECK(root[3].val().is_sub(yml)); // val is first pointing at the buffer
2462  root[3] << 67890;
2463  CHECK(root[3].val() == "67890");
2464  CHECK(root[3].val().is_sub(tree.arena())); // now val is pointing at the arena
2465  // notice the serialized string was appended to the tree's arena:
2466  CHECK(tree.arena() == "1234567890");
2467  }
2468  // the arena is also used when the data is serialized to string
2469  // with NodeRef::operator<<(): immutable buffer
2470  {
2471  ryml::csubstr yml = "[a, b, c, d]"; // immutable
2472  ryml::Tree tree = ryml::parse_in_arena(yml);
2473  // notice the buffer was copied to the arena:
2474  CHECK(tree.arena().data() != yml.data());
2475  CHECK(tree.arena() == yml);
2476  ryml::NodeRef root = tree.rootref();
2477 
2478  // serialize an integer, and mutate the tree
2479  CHECK(root[2].val() == "c");
2480  root[2] << 12345; // serialize an integer
2481  CHECK(root[2].val() == "12345");
2482  // notice the serialized string was appended to the tree's arena:
2483  // notice also the previous values remain there.
2484  // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2485  CHECK(tree.arena() == "[a, b, c, d]12345");
2486  // old values: --------------^
2487 
2488  // serialize an integer, and mutate the tree
2489  root[3] << 67890;
2490  CHECK(root[3].val() == "67890");
2491  // notice the serialized string was appended to the tree's arena:
2492  // notice also the previous values remain there.
2493  // RYML DOES NOT KEEP TRACK OF REFERENCES TO THE ARENA.
2494  CHECK(tree.arena() == "[a, b, c, d]1234567890");
2495  // old values: --------------^ ---^^^^^
2496  }
2497 
2498  // to_arena(): directly serialize values to the arena:
2499  {
2500  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2501  ryml::csubstr c10 = tree.to_arena(10101010);
2502  CHECK(c10 == "10101010");
2503  CHECK(c10.is_sub(tree.arena()));
2504  CHECK(tree.arena() == "{a: b}10101010");
2505  CHECK(tree.key(1) == "a");
2506  CHECK(tree.val(1) == "b");
2507  tree.set_val(1, c10);
2508  CHECK(tree.val(1) == c10);
2509  // and you can also do it through a node:
2510  ryml::NodeRef root = tree.rootref();
2511  root["a"].set_val_serialized(2222);
2512  CHECK(root["a"].val() == "2222");
2513  CHECK(tree.arena() == "{a: b}101010102222");
2514  }
2515 
2516  // copy_to_arena(): manually copy a string to the arena:
2517  {
2518  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2519  ryml::csubstr mystr = "Gosset Grande Reserve";
2520  ryml::csubstr copied = tree.copy_to_arena(mystr);
2521  CHECK(!copied.overlaps(mystr));
2522  CHECK(copied == mystr);
2523  CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2524  }
2525 
2526  // alloc_arena(): allocate a buffer from the arena:
2527  {
2528  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2529  ryml::csubstr mystr = "Gosset Grande Reserve";
2530  ryml::substr copied = tree.alloc_arena(mystr.size());
2531  CHECK(!copied.overlaps(mystr));
2532  memcpy(copied.str, mystr.str, mystr.len);
2533  CHECK(copied == mystr);
2534  CHECK(tree.arena() == "{a: b}Gosset Grande Reserve");
2535  }
2536 
2537  // reserve_arena(): ensure the arena has a certain size to avoid reallocations
2538  {
2539  ryml::Tree tree = ryml::parse_in_arena("{a: b}");
2540  CHECK(tree.arena().size() == strlen("{a: b}"));
2541  tree.reserve_arena(100);
2542  CHECK(tree.arena_capacity() >= 100);
2543  CHECK(tree.arena().size() == strlen("{a: b}"));
2544  tree.to_arena(123456);
2545  CHECK(tree.arena().first(12) == "{a: b}123456");
2546  }
2547 }
2548 
2549 
2550 //-----------------------------------------------------------------------------
2551 
2552 /** ryml provides facilities for serializing and deserializing the C++
2553  fundamental types, including boolean and null values; this is
2554  provided by the several overloads in @ref doc_to_chars and @ref
2555  doc_from_chars. To add serialization for user scalar types (ie,
2556  those types that should be serialized as strings in leaf nodes),
2557  you just need to define the appropriate overloads of to_chars and
2558  from_chars for those types; see @ref sample_user_scalar_types for
2559  an example on how to achieve this, and see @ref doc_serialization
2560  for more information on serialization. */
2562 {
2563  ryml::Tree tree;
2564  CHECK(tree.arena().empty());
2565  CHECK(tree.to_arena('a') == "a"); CHECK(tree.arena() == "a");
2566  CHECK(tree.to_arena("bcde") == "bcde"); CHECK(tree.arena() == "abcde");
2567  CHECK(tree.to_arena(unsigned(0)) == "0"); CHECK(tree.arena() == "abcde0");
2568  CHECK(tree.to_arena(int(1)) == "1"); CHECK(tree.arena() == "abcde01");
2569  CHECK(tree.to_arena(uint8_t(0)) == "0"); CHECK(tree.arena() == "abcde010");
2570  CHECK(tree.to_arena(uint16_t(1)) == "1"); CHECK(tree.arena() == "abcde0101");
2571  CHECK(tree.to_arena(uint32_t(2)) == "2"); CHECK(tree.arena() == "abcde01012");
2572  CHECK(tree.to_arena(uint64_t(3)) == "3"); CHECK(tree.arena() == "abcde010123");
2573  CHECK(tree.to_arena(int8_t( 4)) == "4"); CHECK(tree.arena() == "abcde0101234");
2574  CHECK(tree.to_arena(int8_t(-4)) == "-4"); CHECK(tree.arena() == "abcde0101234-4");
2575  CHECK(tree.to_arena(int16_t( 5)) == "5"); CHECK(tree.arena() == "abcde0101234-45");
2576  CHECK(tree.to_arena(int16_t(-5)) == "-5"); CHECK(tree.arena() == "abcde0101234-45-5");
2577  CHECK(tree.to_arena(int32_t( 6)) == "6"); CHECK(tree.arena() == "abcde0101234-45-56");
2578  CHECK(tree.to_arena(int32_t(-6)) == "-6"); CHECK(tree.arena() == "abcde0101234-45-56-6");
2579  CHECK(tree.to_arena(int64_t( 7)) == "7"); CHECK(tree.arena() == "abcde0101234-45-56-67");
2580  CHECK(tree.to_arena(int64_t(-7)) == "-7"); CHECK(tree.arena() == "abcde0101234-45-56-67-7");
2581  CHECK(tree.to_arena((void*)1) == "0x1"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x1");
2582  CHECK(tree.to_arena(float(0.124)) == "0.124"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.124");
2583  CHECK(tree.to_arena(double(0.234)) == "0.234"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.234");
2584 
2585  // write boolean values - see also sample_formatting()
2586  CHECK(tree.to_arena(bool(true)) == "1"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.2341");
2587  CHECK(tree.to_arena(bool(false)) == "0"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410");
2588  CHECK(tree.to_arena(c4::fmt::boolalpha(true)) == "true"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410true");
2589  CHECK(tree.to_arena(c4::fmt::boolalpha(false)) == "false"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse");
2590 
2591  // write special float values
2592  // see also sample_float_precision()
2593  const float fnan = std::numeric_limits<float >::quiet_NaN();
2594  const double dnan = std::numeric_limits<double>::quiet_NaN();
2595  const float finf = std::numeric_limits<float >::infinity();
2596  const double dinf = std::numeric_limits<double>::infinity();
2597  CHECK(tree.to_arena( finf) == ".inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf");
2598  CHECK(tree.to_arena( dinf) == ".inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf");
2599  CHECK(tree.to_arena(-finf) == "-.inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf");
2600  CHECK(tree.to_arena(-dinf) == "-.inf"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf");
2601  CHECK(tree.to_arena( fnan) == ".nan"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf.nan");
2602  CHECK(tree.to_arena( dnan) == ".nan"); CHECK(tree.arena() == "abcde0101234-45-56-67-70x10.1240.23410truefalse.inf.inf-.inf-.inf.nan.nan");
2603 
2604  // read special float values
2605  // see also sample_float_precision()
2606  C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wfloat-equal");
2607  tree = ryml::parse_in_arena(R"({ninf: -.inf, pinf: .inf, nan: .nan})");
2608  float f = 0.f;
2609  double d = 0.;
2610  CHECK(f == 0.f);
2611  CHECK(d == 0.);
2612  tree["ninf"] >> f; CHECK(f == -finf);
2613  tree["ninf"] >> d; CHECK(d == -dinf);
2614  tree["pinf"] >> f; CHECK(f == finf);
2615  tree["pinf"] >> d; CHECK(d == dinf);
2616  tree["nan" ] >> f; CHECK(std::isnan(f));
2617  tree["nan" ] >> d; CHECK(std::isnan(d));
2618  C4_SUPPRESS_WARNING_GCC_CLANG_POP
2619 
2620  // value overflow detection:
2621  // (for integral types only)
2622  {
2623  // we will be detecting errors below, so we use this sample helper
2624  ScopedErrorHandlerExample err = {};
2625  ryml::Tree t(err.callbacks()); // instantiate with the error-detecting callbacks
2626  // create a simple tree with an int value
2627  ryml::parse_in_arena(R"({val: 258})", &t);
2628  // by default, overflow is not detected:
2629  uint8_t valu8 = 0;
2630  int8_t vali8 = 0;
2631  t["val"] >> valu8; CHECK(valu8 == 2); // not 257; it wrapped around
2632  t["val"] >> vali8; CHECK(vali8 == 2); // not 257; it wrapped around
2633  // ...but there are facilities to detect overflow
2634  CHECK(ryml::overflows<uint8_t>(t["val"].val()));
2635  CHECK(ryml::overflows<int8_t>(t["val"].val()));
2636  CHECK( ! ryml::overflows<int16_t>(t["val"].val()));
2637  // and there is a format helper
2638  CHECK(err.check_error_occurs([&]{
2639  auto checku8 = ryml::fmt::overflow_checked(valu8); // need to declare the wrapper type before using it with >>
2640  t["val"] >> checku8; // this will cause an error
2641  }));
2642  CHECK(err.check_error_occurs([&]{
2643  auto checki8 = ryml::fmt::overflow_checked(vali8); // need to declare the wrapper type before using it with >>
2644  t["val"] >> checki8; // this will cause an error
2645  }));
2646  }
2647 }
2648 
2649 
2650 //-----------------------------------------------------------------------------
2651 
2652 /** Shows how to deal with empty/null values. See also @ref
2653  * c4::yml::Tree::val_is_null */
2655 {
2656  // reading empty/null values - see also sample_formatting()
2658  "plain:" "\n"
2659  "squoted: ''" "\n"
2660  "dquoted: \"\"" "\n"
2661  "literal: |" "\n"
2662  "folded: >" "\n"
2663  "all_null: [~, null, Null, NULL]" "\n"
2664  "non_null: [nULL, non_null, non null, null it is not]" "\n"
2665  "");
2666  // first, remember that .has_val() is a structural predicate
2667  // indicating the node is a leaf, and not a container.
2668  CHECK(tree["plain"].has_val()); // has a val, even if it's empty!
2669  CHECK(tree["squoted"].has_val());
2670  CHECK(tree["dquoted"].has_val());
2671  CHECK(tree["literal"].has_val());
2672  CHECK(tree["folded"].has_val());
2673  CHECK( ! tree["all_null"].has_val());
2674  CHECK( ! tree["non_null"].has_val());
2675  // In essence, has_val() is the logical opposite of is_container()
2676  CHECK( ! tree["plain"].is_container());
2677  CHECK( ! tree["squoted"].is_container());
2678  CHECK( ! tree["dquoted"].is_container());
2679  CHECK( ! tree["literal"].is_container());
2680  CHECK( ! tree["folded"].is_container());
2681  CHECK(tree["all_null"].is_container());
2682  CHECK(tree["non_null"].is_container());
2683  //
2684  // Right. How about the contents of each val?
2685  //
2686  // all of these scalars have zero-length:
2687  CHECK(tree["plain"].val().len == 0);
2688  CHECK(tree["squoted"].val().len == 0);
2689  CHECK(tree["dquoted"].val().len == 0);
2690  CHECK(tree["literal"].val().len == 0);
2691  CHECK(tree["folded"].val().len == 0);
2692  // but only the empty scalar has null string:
2693  CHECK(tree["plain"].val().str == nullptr);
2694  CHECK(tree["squoted"].val().str != nullptr);
2695  CHECK(tree["dquoted"].val().str != nullptr);
2696  CHECK(tree["literal"].val().str != nullptr);
2697  CHECK(tree["folded"].val().str != nullptr);
2698  // likewise, scalar comparison to nullptr has the same results:
2699  // (remember that .val() gives you the scalar value, node must
2700  // have a val, ie must be a leaf node, not a container)
2701  CHECK(tree["plain"].val() == nullptr);
2702  CHECK(tree["squoted"].val() != nullptr);
2703  CHECK(tree["dquoted"].val() != nullptr);
2704  CHECK(tree["literal"].val() != nullptr);
2705  CHECK(tree["folded"].val() != nullptr);
2706  // the tree and node classes provide the corresponding predicate
2707  // functions .key_is_null() and .val_is_null().
2708  // (note that these functions have the same preconditions as .val(),
2709  // because they need get the val to look into its contents)
2710  CHECK(tree["plain"].val_is_null());
2711  CHECK( ! tree["squoted"].val_is_null());
2712  CHECK( ! tree["dquoted"].val_is_null());
2713  CHECK( ! tree["literal"].val_is_null());
2714  CHECK( ! tree["folded"].val_is_null());
2715  // matching to null is case-sensitive. only the cases shown here
2716  // match to null:
2717  for(ryml::ConstNodeRef child : tree["all_null"].children())
2718  {
2719  CHECK(child.val() != nullptr); // it is pointing at a string, so it is not nullptr!
2720  CHECK(child.val_is_null());
2721  }
2722  for(ryml::ConstNodeRef child : tree["non_null"].children())
2723  {
2724  CHECK(child.val() != nullptr);
2725  CHECK( ! child.val_is_null());
2726  }
2727  //
2728  //
2729  // Because the meaning of null/~/empty will vary from application
2730  // to application, ryml makes no assumption on what should be
2731  // serialized as null. It leaves this decision to the user. But
2732  // it also provides the proper toolbox for the user to implement
2733  // its intended solution.
2734  //
2735  // writing/disambiguating null values:
2736  ryml::csubstr null = {};
2737  ryml::csubstr nonnull = "";
2738  ryml::csubstr strnull = "null";
2739  ryml::csubstr tilde = "~";
2740  CHECK(null .len == 0); CHECK(null .str == nullptr); CHECK(null == nullptr);
2741  CHECK(nonnull.len == 0); CHECK(nonnull.str != nullptr); CHECK(nonnull != nullptr);
2742  CHECK(strnull.len != 0); CHECK(strnull.str != nullptr); CHECK(strnull != nullptr);
2743  CHECK(tilde .len != 0); CHECK(tilde .str != nullptr); CHECK(tilde != nullptr);
2744  tree.clear();
2745  tree.clear_arena();
2746  tree.rootref() |= ryml::MAP;
2747  // serializes as an empty plain scalar:
2748  tree["empty_null"] << null; CHECK(tree.arena() == "");
2749  // serializes as an empty quoted scalar:
2750  tree["empty_nonnull"] << nonnull; CHECK(tree.arena() == "");
2751  // serializes as the normal 'null' string:
2752  tree["str_null"] << strnull; CHECK(tree.arena() == "null");
2753  // serializes as the normal '~' string:
2754  tree["str_tilde"] << tilde; CHECK(tree.arena() == "null~");
2755  // this is the resulting yaml:
2756  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2757  "empty_null: " "\n"
2758  "empty_nonnull: ''" "\n"
2759  "str_null: null" "\n"
2760  "str_tilde: ~" "\n"
2761  "");
2762  // To enforce a particular concept of what is a null string, you
2763  // can use the appropriate condition based on pointer nulity or
2764  // other appropriate criteria.
2765  //
2766  // As an example, proper comparison to nullptr:
2767  auto null_if_nullptr = [](ryml::csubstr s) {
2768  return s.str == nullptr ? "null" : s;
2769  };
2770  tree["empty_null"] << null_if_nullptr(null);
2771  tree["empty_nonnull"] << null_if_nullptr(nonnull);
2772  tree["str_null"] << null_if_nullptr(strnull);
2773  tree["str_tilde"] << null_if_nullptr(tilde);
2774  // this is the resulting yaml:
2775  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2776  "empty_null: null" "\n"
2777  "empty_nonnull: ''" "\n"
2778  "str_null: null" "\n"
2779  "str_tilde: ~" "\n"
2780  "");
2781  //
2782  // As another example, nulity check based on the YAML nulity
2783  // predicate:
2784  auto null_if_predicate = [](ryml::csubstr s) {
2785  return ryml::scalar_is_null(s) ? "null" : s;
2786  };
2787  tree["empty_null"] << null_if_predicate(null);
2788  tree["empty_nonnull"] << null_if_predicate(nonnull);
2789  tree["str_null"] << null_if_predicate(strnull);
2790  tree["str_tilde"] << null_if_predicate(tilde);
2791  // this is the resulting yaml:
2792  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2793  "empty_null: null" "\n"
2794  "empty_nonnull: ''" "\n"
2795  "str_null: null" "\n"
2796  "str_tilde: null" "\n"
2797  "");
2798  //
2799  // As another example, nulity check based on the YAML nulity
2800  // predicate, but returning "~" to simbolize nulity:
2801  auto tilde_if_predicate = [](ryml::csubstr s) {
2802  return ryml::scalar_is_null(s) ? "~" : s;
2803  };
2804  tree["empty_null"] << tilde_if_predicate(null);
2805  tree["empty_nonnull"] << tilde_if_predicate(nonnull);
2806  tree["str_null"] << tilde_if_predicate(strnull);
2807  tree["str_tilde"] << tilde_if_predicate(tilde);
2808  // this is the resulting yaml:
2809  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
2810  "empty_null: ~" "\n"
2811  "empty_nonnull: ''" "\n"
2812  "str_null: ~" "\n"
2813  "str_tilde: ~" "\n"
2814  "");
2815 }
2816 
2817 
2818 //-----------------------------------------------------------------------------
2819 
2820 /** ryml provides facilities for formatting/deformatting (imported
2821  * from c4core into the ryml namespace). See @ref doc_format_utils
2822  * . These functions are very useful to serialize and deserialize
2823  * scalar types; see @ref doc_serialization .
2824  */
2826 {
2827  // format(), format_sub(), formatrs(): format arguments
2828  {
2829  char buf_[256] = {};
2830  ryml::substr buf = buf_;
2831  size_t size = ryml::format(buf, "a={} foo {} {} bar {}", 0.1, 10, 11, 12);
2832  CHECK(size == strlen("a=0.1 foo 10 11 bar 12"));
2833  CHECK(buf.first(size) == "a=0.1 foo 10 11 bar 12");
2834  // it is safe to call on an empty buffer:
2835  // returns the size needed for the result, and no overflow occurs:
2836  size = ryml::format({} , "a={} foo {} {} bar {}", "this_is_a", 10, 11, 12);
2837  CHECK(size == ryml::format(buf, "a={} foo {} {} bar {}", "this_is_a", 10, 11, 12));
2838  CHECK(size == strlen("a=this_is_a foo 10 11 bar 12"));
2839  // it is also safe to call on an insufficient buffer:
2840  char smallbuf[8] = {};
2841  size = ryml::format(smallbuf, "{} is too large {}", "this", "for the buffer");
2842  CHECK(size == strlen("this is too large for the buffer"));
2843  // ... and the result is truncated at the buffer size:
2844  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "this is\0");
2845 
2846  // format_sub() directly returns the written string:
2847  ryml::csubstr result = ryml::format_sub(buf, "b={}, damn it.", 1);
2848  CHECK(result == "b=1, damn it.");
2849  CHECK(result.is_sub(buf));
2850 
2851  // formatrs() means FORMAT & ReSize:
2852  //
2853  // Instead of a substr, it receives any owning linear char container
2854  // for which to_substr() is defined (using ADL).
2855  // <ryml_std.hpp> has to_substr() definitions for std::string and
2856  // std::vector<char>.
2857  //
2858  // formatrs() starts by calling format(), and if needed, resizes the container
2859  // and calls format() again.
2860  //
2861  // Note that unless the container is previously sized, this
2862  // may cause an allocation, which will make your code slower.
2863  // Make sure to call .reserve() on the container for real
2864  // production code.
2865  std::string sbuf;
2866  ryml::formatrs(&sbuf, "and c={} seems about right", 2);
2867  CHECK(sbuf == "and c=2 seems about right");
2868  std::vector<char> vbuf; // works with any linear char container
2869  ryml::formatrs(&vbuf, "and c={} seems about right", 2);
2870  CHECK(sbuf == "and c=2 seems about right");
2871  // with formatrs() it is also possible to append:
2872  ryml::formatrs_append(&sbuf, ", and finally d={} - done", 3);
2873  CHECK(sbuf == "and c=2 seems about right, and finally d=3 - done");
2874  }
2875 
2876  // unformat(): read arguments - opposite of format()
2877  {
2878  char buf_[256];
2879 
2880  int a = 0, b = 1, c = 2;
2881  ryml::csubstr result = ryml::format_sub(buf_, "{} and {} and {}", a, b, c);
2882  CHECK(result == "0 and 1 and 2");
2883  int aa = -1, bb = -2, cc = -3;
2884  size_t num_characters = ryml::unformat(result, "{} and {} and {}", aa, bb, cc);
2885  CHECK(num_characters != ryml::csubstr::npos); // if a conversion fails, returns ryml::csubstr::npos
2886  CHECK(num_characters == result.size());
2887  CHECK(aa == a);
2888  CHECK(bb == b);
2889  CHECK(cc == c);
2890 
2891  result = ryml::format_sub(buf_, "{} and {} and {}", 10, 20, 30);
2892  CHECK(result == "10 and 20 and 30");
2893  num_characters = ryml::unformat(result, "{} and {} and {}", aa, bb, cc);
2894  CHECK(num_characters != ryml::csubstr::npos); // if a conversion fails, returns ryml::csubstr::npos
2895  CHECK(num_characters == result.size());
2896  CHECK(aa == 10);
2897  CHECK(bb == 20);
2898  CHECK(cc == 30);
2899  }
2900 
2901  // cat(), cat_sub(), catrs(): concatenate arguments
2902  {
2903  char buf_[256] = {};
2904  ryml::substr buf = buf_;
2905  size_t size = ryml::cat(buf, "a=", 0.1, "foo", 10, 11, "bar", 12);
2906  CHECK(size == strlen("a=0.1foo1011bar12"));
2907  CHECK(buf.first(size) == "a=0.1foo1011bar12");
2908  // it is safe to call on an empty buffer:
2909  // returns the size needed for the result, and no overflow occurs:
2910  CHECK(ryml::cat({}, "a=", 0) == 3);
2911  // it is also safe to call on an insufficient buffer:
2912  char smallbuf[8] = {};
2913  size = ryml::cat(smallbuf, "this", " is too large ", "for the buffer");
2914  CHECK(size == strlen("this is too large for the buffer"));
2915  // ... and the result is truncated at the buffer size:
2916  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "this is\0");
2917 
2918  // cat_sub() directly returns the written string:
2919  ryml::csubstr result = ryml::cat_sub(buf, "b=", 1, ", damn it.");
2920  CHECK(result == "b=1, damn it.");
2921  CHECK(result.is_sub(buf));
2922 
2923  // catrs() means CAT & ReSize:
2924  //
2925  // Instead of a substr, it receives any owning linear char container
2926  // for which to_substr() is defined (using ADL).
2927  // <ryml_std.hpp> has to_substr() definitions for std::string and
2928  // std::vector<char>.
2929  //
2930  // catrs() starts by calling cat(), and if needed, resizes the container
2931  // and calls cat() again.
2932  //
2933  // Note that unless the container is previously sized, this
2934  // may cause an allocation, which will make your code slower.
2935  // Make sure to call .reserve() on the container for real
2936  // production code.
2937  std::string sbuf;
2938  ryml::catrs(&sbuf, "and c=", 2, " seems about right");
2939  CHECK(sbuf == "and c=2 seems about right");
2940  std::vector<char> vbuf; // works with any linear char container
2941  ryml::catrs(&vbuf, "and c=", 2, " seems about right");
2942  CHECK(sbuf == "and c=2 seems about right");
2943  // with catrs() it is also possible to append:
2944  ryml::catrs_append(&sbuf, ", and finally d=", 3, " - done");
2945  CHECK(sbuf == "and c=2 seems about right, and finally d=3 - done");
2946  }
2947 
2948  // uncat(): read arguments - opposite of cat()
2949  {
2950  char buf_[256];
2951 
2952  int a = 0, b = 1, c = 2;
2953  ryml::csubstr result = ryml::cat_sub(buf_, a, ' ', b, ' ', c);
2954  CHECK(result == "0 1 2");
2955  int aa = -1, bb = -2, cc = -3;
2956  char sep1 = 'a', sep2 = 'b';
2957  size_t num_characters = ryml::uncat(result, aa, sep1, bb, sep2, cc);
2958  CHECK(num_characters == result.size());
2959  CHECK(aa == a);
2960  CHECK(bb == b);
2961  CHECK(cc == c);
2962  CHECK(sep1 == ' ');
2963  CHECK(sep2 == ' ');
2964 
2965  result = ryml::cat_sub(buf_, 10, ' ', 20, ' ', 30);
2966  CHECK(result == "10 20 30");
2967  num_characters = ryml::uncat(result, aa, sep1, bb, sep2, cc);
2968  CHECK(num_characters == result.size());
2969  CHECK(aa == 10);
2970  CHECK(bb == 20);
2971  CHECK(cc == 30);
2972  CHECK(sep1 == ' ');
2973  CHECK(sep2 == ' ');
2974  }
2975 
2976  // catsep(), catsep_sub(), catseprs(): concatenate arguments, with a separator
2977  {
2978  char buf_[256] = {};
2979  ryml::substr buf = buf_;
2980  // use ' ' as a separator
2981  size_t size = ryml::catsep(buf, ' ', "a=", 0, "b=", 1, "c=", 2, 45, 67);
2982  CHECK(buf.first(size) == "a= 0 b= 1 c= 2 45 67");
2983  // any separator may be used
2984  // use " and " as a separator
2985  size = ryml::catsep(buf, " and ", "a=0", "b=1", "c=2", 45, 67);
2986  CHECK(buf.first(size) == "a=0 and b=1 and c=2 and 45 and 67");
2987  // use " ... " as a separator
2988  size = ryml::catsep(buf, " ... ", "a=0", "b=1", "c=2", 45, 67);
2989  CHECK(buf.first(size) == "a=0 ... b=1 ... c=2 ... 45 ... 67");
2990  // use '/' as a separator
2991  size = ryml::catsep(buf, '/', "a=", 0, "b=", 1, "c=", 2, 45, 67);
2992  CHECK(buf.first(size) == "a=/0/b=/1/c=/2/45/67");
2993  // use 888 as a separator
2994  size = ryml::catsep(buf, 888, "a=0", "b=1", "c=2", 45, 67);
2995  CHECK(buf.first(size) == "a=0888b=1888c=28884588867");
2996 
2997  // it is safe to call on an empty buffer:
2998  // returns the size needed for the result, and no overflow occurs:
2999  CHECK(size == ryml::catsep({}, 888, "a=0", "b=1", "c=2", 45, 67));
3000  // it is also safe to call on an insufficient buffer:
3001  char smallbuf[8] = {};
3002  CHECK(size == ryml::catsep(smallbuf, 888, "a=0", "b=1", "c=2", 45, 67));
3003  CHECK(size == strlen("a=0888b=1888c=28884588867"));
3004  // ... and the result is truncated:
3005  CHECK(ryml::substr(smallbuf, sizeof(smallbuf)) == "a=0888b\0");
3006 
3007  // catsep_sub() directly returns the written substr:
3008  ryml::csubstr result = ryml::catsep_sub(buf, " and ", "a=0", "b=1", "c=2", 45, 67);
3009  CHECK(result == "a=0 and b=1 and c=2 and 45 and 67");
3010  CHECK(result.is_sub(buf));
3011 
3012  // catseprs() means CATSEP & ReSize:
3013  //
3014  // Instead of a substr, it receives any owning linear char container
3015  // for which to_substr() is defined (using ADL).
3016  // <ryml_std.hpp> has to_substr() definitions for std::string and
3017  // std::vector<char>.
3018  //
3019  // catseprs() starts by calling catsep(), and if needed, resizes the container
3020  // and calls catsep() again.
3021  //
3022  // Note that unless the container is previously sized, this
3023  // may cause an allocation, which will make your code slower.
3024  // Make sure to call .reserve() on the container for real
3025  // production code.
3026  std::string sbuf;
3027  ryml::catseprs(&sbuf, " and ", "a=0", "b=1", "c=2", 45, 67);
3028  CHECK(sbuf == "a=0 and b=1 and c=2 and 45 and 67");
3029  std::vector<char> vbuf; // works with any linear char container
3030  ryml::catseprs(&vbuf, " and ", "a=0", "b=1", "c=2", 45, 67);
3031  CHECK(ryml::to_csubstr(vbuf) == "a=0 and b=1 and c=2 and 45 and 67");
3032 
3033  // with catseprs() it is also possible to append:
3034  ryml::catseprs_append(&sbuf, " well ", " --- a=0", "b=11", "c=12", 145, 167);
3035  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");
3036  }
3037 
3038  // uncatsep(): read arguments with a separator - opposite of catsep()
3039  {
3040  char buf_[256] = {};
3041 
3042  int a = 0, b = 1, c = 2;
3043  ryml::csubstr result = ryml::catsep_sub(buf_, ' ', a, b, c);
3044  CHECK(result == "0 1 2");
3045  int aa = -1, bb = -2, cc = -3;
3046  size_t num_characters = ryml::uncatsep(result, " ", aa, bb, cc);
3047  CHECK(num_characters == result.size());
3048  CHECK(aa == a);
3049  CHECK(bb == b);
3050  CHECK(cc == c);
3051 
3052  result = ryml::catsep_sub(buf_, "--", 10, 20, 30);
3053  CHECK(result == "10--20--30");
3054  num_characters = ryml::uncatsep(result, "--", aa, bb, cc);
3055  CHECK(num_characters == result.size());
3056  CHECK(aa == 10);
3057  CHECK(bb == 20);
3058  CHECK(cc == 30);
3059  }
3060 
3061  // formatting individual arguments
3062  {
3063  using namespace ryml; // all the symbols below are in the ryml namespace.
3064  char buf_[256] = {}; // all the results below are written in this buffer
3065  substr buf = buf_;
3066  // --------------------------------------
3067  // fmt::boolalpha(): format as true/false
3068  // --------------------------------------
3069  // just as with std streams, printing a bool will output the integer value:
3070  CHECK("0" == cat_sub(buf, false));
3071  CHECK("1" == cat_sub(buf, true));
3072  // to force a "true"/"false", use fmt::boolalpha:
3073  CHECK("false" == cat_sub(buf, fmt::boolalpha(false)));
3074  CHECK("true" == cat_sub(buf, fmt::boolalpha(true)));
3075 
3076  // ---------------------------------
3077  // fmt::hex(): format as hexadecimal
3078  // ---------------------------------
3079  CHECK("0xff" == cat_sub(buf, fmt::hex(255)));
3080  CHECK("0x100" == cat_sub(buf, fmt::hex(256)));
3081  CHECK("-0xff" == cat_sub(buf, fmt::hex(-255)));
3082  CHECK("-0x100" == cat_sub(buf, fmt::hex(-256)));
3083  CHECK("3735928559" == cat_sub(buf, UINT32_C(0xdeadbeef)));
3084  CHECK("0xdeadbeef" == cat_sub(buf, fmt::hex(UINT32_C(0xdeadbeef))));
3085  // ----------------------------
3086  // fmt::bin(): format as binary
3087  // ----------------------------
3088  CHECK("0b1000" == cat_sub(buf, fmt::bin(8)));
3089  CHECK("0b1001" == cat_sub(buf, fmt::bin(9)));
3090  CHECK("0b10001" == cat_sub(buf, fmt::bin(17)));
3091  CHECK("0b11001" == cat_sub(buf, fmt::bin(25)));
3092  CHECK("-0b1000" == cat_sub(buf, fmt::bin(-8)));
3093  CHECK("-0b1001" == cat_sub(buf, fmt::bin(-9)));
3094  CHECK("-0b10001" == cat_sub(buf, fmt::bin(-17)));
3095  CHECK("-0b11001" == cat_sub(buf, fmt::bin(-25)));
3096  // ---------------------------
3097  // fmt::bin(): format as octal
3098  // ---------------------------
3099  CHECK("0o77" == cat_sub(buf, fmt::oct(63)));
3100  CHECK("0o100" == cat_sub(buf, fmt::oct(64)));
3101  CHECK("0o377" == cat_sub(buf, fmt::oct(255)));
3102  CHECK("0o400" == cat_sub(buf, fmt::oct(256)));
3103  CHECK("0o1000" == cat_sub(buf, fmt::oct(512)));
3104  CHECK("-0o77" == cat_sub(buf, fmt::oct(-63)));
3105  CHECK("-0o100" == cat_sub(buf, fmt::oct(-64)));
3106  CHECK("-0o377" == cat_sub(buf, fmt::oct(-255)));
3107  CHECK("-0o400" == cat_sub(buf, fmt::oct(-256)));
3108  CHECK("-0o1000" == cat_sub(buf, fmt::oct(-512)));
3109  // ---------------------------
3110  // fmt::zpad(): pad with zeros
3111  // ---------------------------
3112  CHECK("000063" == cat_sub(buf, fmt::zpad(63, 6)));
3113  CHECK( "00063" == cat_sub(buf, fmt::zpad(63, 5)));
3114  CHECK( "0063" == cat_sub(buf, fmt::zpad(63, 4)));
3115  CHECK( "063" == cat_sub(buf, fmt::zpad(63, 3)));
3116  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 2)));
3117  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 1))); // will never trim the result
3118  CHECK( "63" == cat_sub(buf, fmt::zpad(63, 0))); // will never trim the result
3119  CHECK("0x00003f" == cat_sub(buf, fmt::zpad(fmt::hex(63), 6)));
3120  CHECK("0o000077" == cat_sub(buf, fmt::zpad(fmt::oct(63), 6)));
3121  CHECK("0b00011001" == cat_sub(buf, fmt::zpad(fmt::bin(25), 8)));
3122  // ------------------------------------------------
3123  // fmt::left(): align left with a given field width
3124  // ------------------------------------------------
3125  CHECK("63 " == cat_sub(buf, fmt::left(63, 6)));
3126  CHECK("63 " == cat_sub(buf, fmt::left(63, 5)));
3127  CHECK("63 " == cat_sub(buf, fmt::left(63, 4)));
3128  CHECK("63 " == cat_sub(buf, fmt::left(63, 3)));
3129  CHECK("63" == cat_sub(buf, fmt::left(63, 2)));
3130  CHECK("63" == cat_sub(buf, fmt::left(63, 1))); // will never trim the result
3131  CHECK("63" == cat_sub(buf, fmt::left(63, 0))); // will never trim the result
3132  // the fill character can be specified (defaults to ' '):
3133  CHECK("63----" == cat_sub(buf, fmt::left(63, 6, '-')));
3134  CHECK("63++++" == cat_sub(buf, fmt::left(63, 6, '+')));
3135  CHECK("63////" == cat_sub(buf, fmt::left(63, 6, '/')));
3136  CHECK("630000" == cat_sub(buf, fmt::left(63, 6, '0')));
3137  CHECK("63@@@@" == cat_sub(buf, fmt::left(63, 6, '@')));
3138  CHECK("0x003f " == cat_sub(buf, fmt::left(fmt::zpad(fmt::hex(63), 4), 10)));
3139  // --------------------------------------------------
3140  // fmt::right(): align right with a given field width
3141  // --------------------------------------------------
3142  CHECK(" 63" == cat_sub(buf, fmt::right(63, 6)));
3143  CHECK(" 63" == cat_sub(buf, fmt::right(63, 5)));
3144  CHECK(" 63" == cat_sub(buf, fmt::right(63, 4)));
3145  CHECK(" 63" == cat_sub(buf, fmt::right(63, 3)));
3146  CHECK("63" == cat_sub(buf, fmt::right(63, 2)));
3147  CHECK("63" == cat_sub(buf, fmt::right(63, 1))); // will never trim the result
3148  CHECK("63" == cat_sub(buf, fmt::right(63, 0))); // will never trim the result
3149  // the fill character can be specified (defaults to ' '):
3150  CHECK("----63" == cat_sub(buf, fmt::right(63, 6, '-')));
3151  CHECK("++++63" == cat_sub(buf, fmt::right(63, 6, '+')));
3152  CHECK("////63" == cat_sub(buf, fmt::right(63, 6, '/')));
3153  CHECK("000063" == cat_sub(buf, fmt::right(63, 6, '0')));
3154  CHECK("@@@@63" == cat_sub(buf, fmt::right(63, 6, '@')));
3155  CHECK(" 0x003f" == cat_sub(buf, fmt::right(fmt::zpad(fmt::hex(63), 4), 10)));
3156 
3157  // ------------------------------------------
3158  // fmt::real(): format floating point numbers
3159  // ------------------------------------------
3160  // see also sample_float_precision()
3161  CHECK("0" == cat_sub(buf, fmt::real(0.01f, 0)));
3162  CHECK("0.0" == cat_sub(buf, fmt::real(0.01f, 1)));
3163  CHECK("0.01" == cat_sub(buf, fmt::real(0.01f, 2)));
3164  CHECK("0.010" == cat_sub(buf, fmt::real(0.01f, 3)));
3165  CHECK("0.0100" == cat_sub(buf, fmt::real(0.01f, 4)));
3166  CHECK("0.01000" == cat_sub(buf, fmt::real(0.01f, 5)));
3167  CHECK("1" == cat_sub(buf, fmt::real(1.01f, 0)));
3168  CHECK("1.0" == cat_sub(buf, fmt::real(1.01f, 1)));
3169  CHECK("1.01" == cat_sub(buf, fmt::real(1.01f, 2)));
3170  CHECK("1.010" == cat_sub(buf, fmt::real(1.01f, 3)));
3171  CHECK("1.0100" == cat_sub(buf, fmt::real(1.01f, 4)));
3172  CHECK("1.01000" == cat_sub(buf, fmt::real(1.01f, 5)));
3173  CHECK("1" == cat_sub(buf, fmt::real(1.234234234, 0)));
3174  CHECK("1.2" == cat_sub(buf, fmt::real(1.234234234, 1)));
3175  CHECK("1.23" == cat_sub(buf, fmt::real(1.234234234, 2)));
3176  CHECK("1.234" == cat_sub(buf, fmt::real(1.234234234, 3)));
3177  CHECK("1.2342" == cat_sub(buf, fmt::real(1.234234234, 4)));
3178  CHECK("1.23423" == cat_sub(buf, fmt::real(1.234234234, 5)));
3179  CHECK("1000000.00000" == cat_sub(buf, fmt::real(1000000.000000000, 5)));
3180  CHECK("1234234.23423" == cat_sub(buf, fmt::real(1234234.234234234, 5)));
3181  // AKA %f
3182  CHECK("1000000.00000" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_FLOAT))); // AKA %f, same as above
3183  CHECK("1234234.23423" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_FLOAT))); // AKA %f
3184  CHECK("1234234.2342" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_FLOAT))); // AKA %f
3185  CHECK("1234234.234" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_FLOAT))); // AKA %f
3186  CHECK("1234234.23" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_FLOAT))); // AKA %f
3187  // AKA %e
3188  CHECK("1.00000e+06" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_SCIENT))); // AKA %e
3189  CHECK("1.23423e+06" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_SCIENT))); // AKA %e
3190  CHECK("1.2342e+06" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_SCIENT))); // AKA %e
3191  CHECK("1.234e+06" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_SCIENT))); // AKA %e
3192  CHECK("1.23e+06" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_SCIENT))); // AKA %e
3193  // AKA %g
3194  CHECK("1e+06" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_FLEX))); // AKA %g
3195  CHECK("1.2342e+06" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_FLEX))); // AKA %g
3196  CHECK("1.234e+06" == cat_sub(buf, fmt::real(1234234.234234234, 4, FTOA_FLEX))); // AKA %g
3197  CHECK("1.23e+06" == cat_sub(buf, fmt::real(1234234.234234234, 3, FTOA_FLEX))); // AKA %g
3198  CHECK("1.2e+06" == cat_sub(buf, fmt::real(1234234.234234234, 2, FTOA_FLEX))); // AKA %g
3199  // FTOA_HEXA: AKA %a (hexadecimal formatting of floats)
3200  CHECK("0x1.e8480p+19" == cat_sub(buf, fmt::real(1000000.000000000, 5, FTOA_HEXA))); // AKA %a
3201  CHECK("0x1.2d53ap+20" == cat_sub(buf, fmt::real(1234234.234234234, 5, FTOA_HEXA))); // AKA %a
3202 
3203  // --------------------------------------------------------------
3204  // fmt::raw(): dump data in raw (binary) machine format (respecting alignment)
3205  // --------------------------------------------------------------
3206  {
3207  C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wcast-align") // we're casting the values directly, so alignment is strictly respected.
3208  const uint32_t payload[] = {10, 20, 30, 40, UINT32_C(0xdeadbeef)};
3209  // (package payload as a substr, for comparison only)
3210  csubstr expected = csubstr((const char *)payload, sizeof(payload));
3211  csubstr actual = cat_sub(buf, fmt::raw(payload));
3212  CHECK(!actual.overlaps(expected));
3213  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3214  // also possible with variables:
3215  for(const uint32_t value : payload)
3216  {
3217  // (package payload as a substr, for comparison only)
3218  expected = csubstr((const char *)&value, sizeof(value));
3219  actual = cat_sub(buf, fmt::raw(value));
3220  CHECK(actual.size() == sizeof(uint32_t));
3221  CHECK(!actual.overlaps(expected));
3222  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3223  // with non-const data, fmt::craw() may be needed for disambiguation:
3224  actual = cat_sub(buf, fmt::craw(value));
3225  CHECK(actual.size() == sizeof(uint32_t));
3226  CHECK(!actual.overlaps(expected));
3227  CHECK(0 == memcmp(expected.str, actual.str, expected.len));
3228  //
3229  // read back:
3230  uint32_t result = 0;
3231  auto reader = fmt::raw(result);
3232  CHECK(uncat(actual, reader));
3233  // and compare:
3234  // (vs2017/release/32bit does not reload result from cache, so force it)
3235  CHECK(result == value); // roundtrip completed successfully
3236  }
3237  C4_SUPPRESS_WARNING_GCC_CLANG_POP
3238  }
3239 
3240  // -------------------------
3241  // fmt::base64(): see below!
3242  // -------------------------
3243  }
3244 }
3245 
3246 
3247 //-----------------------------------------------------------------------------
3248 
3249 /** demonstrates how to read and write base64-encoded blobs.
3250  * @see @ref doc_base64 */
3252 {
3253  ryml::Tree tree;
3254  tree.rootref() |= ryml::MAP;
3255  struct text_and_base64 { ryml::csubstr text, base64; };
3256  text_and_base64 cases[] = {
3257  {{"Hello, World!"}, {"SGVsbG8sIFdvcmxkIQ=="}},
3258  {{"Brevity is the soul of wit."}, {"QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu"}},
3259  {{"All that glitters is not gold."}, {"QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu"}},
3260  };
3261  // to encode base64 and write the result to val:
3262  for(text_and_base64 c : cases)
3263  {
3264  tree[c.text] << ryml::fmt::base64(c.text);
3265  CHECK(tree[c.text].val() == c.base64);
3266  }
3267  // to encode base64 and write the result to key:
3268  for(text_and_base64 c : cases)
3269  {
3270  tree.rootref().append_child() << ryml::key(ryml::fmt::base64(c.text)) << c.text;
3271  CHECK(tree[c.base64].val() == c.text);
3272  }
3273  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
3274  "Hello, World!: SGVsbG8sIFdvcmxkIQ==" "\n"
3275  "Brevity is the soul of wit.: QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu" "\n"
3276  "All that glitters is not gold.: QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu" "\n"
3277  "SGVsbG8sIFdvcmxkIQ==: Hello, World!" "\n"
3278  "QnJldml0eSBpcyB0aGUgc291bCBvZiB3aXQu: Brevity is the soul of wit." "\n"
3279  "QWxsIHRoYXQgZ2xpdHRlcnMgaXMgbm90IGdvbGQu: All that glitters is not gold." "\n"
3280  "");
3281  char buf1_[128], buf2_[128];
3282  ryml::substr buf1 = buf1_; // this is where we will write the result (using >>)
3283  ryml::substr buf2 = buf2_; // this is where we will write the result (using deserialize_val()/deserialize_key())
3284  std::string result = {}; // show also how to decode to a std::string
3285  // to decode the val base64 and write the result to buf:
3286  for(const text_and_base64 c : cases)
3287  {
3288  // write the decoded result into the given buffer
3289  tree[c.text] >> ryml::fmt::base64(buf1); // cannot know the needed size
3290  size_t len = tree[c.text].deserialize_val(ryml::fmt::base64(buf2)); // returns the needed size
3291  CHECK(len <= buf1.len);
3292  CHECK(len <= buf2.len);
3293  CHECK(c.text.len == len);
3294  CHECK(buf1.first(len) == c.text);
3295  CHECK(buf2.first(len) == c.text);
3296  //
3297  // interop with std::string: using substr
3298  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3299  len = tree[c.text].deserialize_val(ryml::fmt::base64(ryml::to_substr(result))); // returns the needed size
3300  if(len > result.size()) // the size was not enough; resize and call again
3301  {
3302  result.resize(len);
3303  len = tree[c.text].deserialize_val(ryml::fmt::base64(ryml::to_substr(result))); // returns the needed size
3304  }
3305  result.resize(len); // trim to the length of the decoded buffer
3306  CHECK(result == c.text);
3307  //
3308  // interop with std::string: using blob
3309  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3310  ryml::blob strblob(&result[0], result.size());
3311  CHECK(strblob.buf == result.data());
3312  CHECK(strblob.len == result.size());
3313  len = tree[c.text].deserialize_val(ryml::fmt::base64(strblob)); // returns the needed size
3314  if(len > result.size()) // the size was not enough; resize and call again
3315  {
3316  result.resize(len);
3317  strblob = {&result[0], result.size()};
3318  CHECK(strblob.buf == result.data());
3319  CHECK(strblob.len == result.size());
3320  len = tree[c.text].deserialize_val(ryml::fmt::base64(strblob)); // returns the needed size
3321  }
3322  result.resize(len); // trim to the length of the decoded buffer
3323  CHECK(result == c.text);
3324  //
3325  // Note also these are just syntatic wrappers to simplify client code.
3326  // You can call into the lower level functions without much effort:
3327  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3328  ryml::csubstr encoded = tree[c.text].val();
3329  CHECK(encoded == c.base64);
3330  len = base64_decode(encoded, ryml::blob{&result[0], result.size()});
3331  if(len > result.size()) // the size was not enough; resize and call again
3332  {
3333  result.resize(len);
3334  len = base64_decode(encoded, ryml::blob{&result[0], result.size()});
3335  }
3336  result.resize(len); // trim to the length of the decoded buffer
3337  CHECK(result == c.text);
3338  }
3339  // to decode the key base64 and write the result to buf:
3340  for(const text_and_base64 c : cases)
3341  {
3342  // write the decoded result into the given buffer
3343  tree[c.base64] >> ryml::key(ryml::fmt::base64(buf1)); // cannot know the needed size
3344  size_t len = tree[c.base64].deserialize_key(ryml::fmt::base64(buf2)); // returns the needed size
3345  CHECK(len <= buf1.len);
3346  CHECK(len <= buf2.len);
3347  CHECK(c.text.len == len);
3348  CHECK(buf1.first(len) == c.text);
3349  CHECK(buf2.first(len) == c.text);
3350  // interop with std::string: using substr
3351  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3352  len = tree[c.base64].deserialize_key(ryml::fmt::base64(ryml::to_substr(result))); // returns the needed size
3353  if(len > result.size()) // the size was not enough; resize and call again
3354  {
3355  result.resize(len);
3356  len = tree[c.base64].deserialize_key(ryml::fmt::base64(ryml::to_substr(result))); // returns the needed size
3357  }
3358  result.resize(len); // trim to the length of the decoded buffer
3359  CHECK(result == c.text);
3360  //
3361  // interop with std::string: using blob
3362  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3363  ryml::blob strblob = {&result[0], result.size()};
3364  CHECK(strblob.buf == result.data());
3365  CHECK(strblob.len == result.size());
3366  len = tree[c.base64].deserialize_key(ryml::fmt::base64(strblob)); // returns the needed size
3367  if(len > result.size()) // the size was not enough; resize and call again
3368  {
3369  result.resize(len);
3370  strblob = {&result[0], result.size()};
3371  CHECK(strblob.buf == result.data());
3372  CHECK(strblob.len == result.size());
3373  len = tree[c.base64].deserialize_key(ryml::fmt::base64(strblob)); // returns the needed size
3374  }
3375  result.resize(len); // trim to the length of the decoded buffer
3376  CHECK(result == c.text);
3377  //
3378  // Note also these are just syntactic wrappers to simplify client code.
3379  // You can call into the lower level functions without much effort:
3380  result.clear(); // this is not needed. We do it just to show that the first call can fail.
3381  ryml::csubstr encoded = tree[c.base64].key();
3382  CHECK(encoded == c.base64);
3383  len = base64_decode(encoded, ryml::blob{&result[0], result.size()});
3384  if(len > result.size()) // the size was not enough; resize and call again
3385  {
3386  result.resize(len);
3387  len = base64_decode(encoded, ryml::blob{&result[0], result.size()});
3388  }
3389  result.resize(len); // trim to the length of the decoded buffer
3390  CHECK(result == c.text);
3391  }
3392  // directly encode variables
3393  {
3394  const uint64_t valin = UINT64_C(0xdeadbeef);
3395  uint64_t valout = 0;
3396  tree["deadbeef"] << c4::fmt::base64(valin); // sometimes cbase64() is needed to avoid ambiguity
3397  size_t len = tree["deadbeef"].deserialize_val(ryml::fmt::base64(valout));
3398  CHECK(len <= sizeof(valout));
3399  CHECK(valout == UINT64_C(0xdeadbeef)); // base64 roundtrip is bit-accurate
3400  }
3401  // directly encode memory ranges
3402  {
3403  const uint32_t data_in[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xdeadbeef};
3404  uint32_t data_out[11] = {};
3405  CHECK(memcmp(data_in, data_out, sizeof(data_in)) != 0); // before the roundtrip
3406  tree["int_data"] << c4::fmt::base64(data_in);
3407  size_t len = tree["int_data"].deserialize_val(ryml::fmt::base64(data_out));
3408  CHECK(len <= sizeof(data_out));
3409  CHECK(memcmp(data_in, data_out, sizeof(data_in)) == 0); // after the roundtrip
3410  }
3411 }
3412 
3413 
3414 //-----------------------------------------------------------------------------
3415 // Serialization info
3416 
3417 /** @} */ // doc_quickstart
3418 /** @addtogroup doc_serialization
3419  *
3420  * @{
3421  *
3422  * ## Fundamental types
3423  *
3424  * ryml provides serialization/deserialization utilities for all
3425  * fundamental data types in @ref doc_charconv .
3426  *
3427  * - See @ref sample_fundamental_types() for basic examples
3428  * of serialization of fundamental types.
3429  * - See @ref sample_empty_null_values() for different ways
3430  * to serialize and deserialize empty and null values/
3431  * - When serializing floating point values in C++ earlier than
3432  * 17, be aware that there may be a truncation of the precision
3433  * with the default float/double implementations of @ref
3434  * doc_to_chars. To enforce a particular precision, use for
3435  * example @ref c4::fmt::real, or call directly @ref c4::ftoa() or
3436  * @ref c4::dtoa(), or any other method (remember that ryml only
3437  * stores the final string in the tree, so nothing prevents you from
3438  * creating it in whatever way is most suitable). See the relevant
3439  * sample: @ref sample_float_precision().
3440  * - You can also serialize and deserialize base64: see @ref
3441  * doc_base64 and @ref sample_base64
3442  *
3443  * To serialize/deserialize any non-fundamental type will require
3444  * that you instruct ryml on how to achieve this. That will differ
3445  * based on whether the type is scalar or container.
3446  *
3447  *
3448  * ## User scalar types
3449  *
3450  * See @ref doc_sample_scalar_types for serializing user scalar types
3451  * (ie leaf nodes in the YAML tree, containing a string
3452  * representation):
3453  *
3454  * - See examples on how to @ref doc_sample_to_chars_scalar
3455  * - See examples on how to @ref doc_sample_from_chars_scalar
3456  * - See the sample @ref sample_user_scalar_types
3457  * - See the sample @ref sample_formatting for examples
3458  * of functions from @ref doc_format_utils that will be very
3459  * helpful in implementing custom `to_chars()`/`from_chars()`
3460  * functions.
3461  * - See @ref doc_charconv for the implementations of
3462  * `to_chars()`/`from_chars()` for the fundamental types.
3463  * - See @ref doc_substr and @ref sample_substr() for the
3464  * many useful utilities in the substring class.
3465  *
3466  *
3467  * ## User container types
3468  *
3469  * - See @ref doc_sample_container_types for when the type is a
3470  * container (ie, a node which has children, which may themselves be
3471  * containers).
3472  *
3473  * - See the sample @ref sample_user_container_types
3474  *
3475  * - See the sample @ref sample_std_types, and also...
3476  *
3477  *
3478  * ## STL types
3479  *
3480  * ryml does not use any STL containers internally, but it can be
3481  * used to serialize and deserialize these containers. See @ref
3482  * sample_std_types() for an example. See the header @ref
3483  * ryml_std.hpp and also the headers it includes:
3484  *
3485  * - scalar types:
3486  * - for `std::string`: @ref ext/c4core/src/c4/std/string.hpp
3487  * - for `std::string_view`: @ref ext/c4core/src/c4/std/string_view.hpp
3488  * - for `std::vector<char>`: @ref ext/c4core/src/c4/std/vector.hpp
3489  * - container types:
3490  * - for `std::vector<T>`: @ref src/c4/yml/std/vector.hpp
3491  * - for `std::map<K,V>`: @ref src/c4/yml/std/map.hpp
3492  *
3493  * @}
3494  *
3495  * @addtogroup doc_quickstart
3496  * @{ */
3497 
3498 
3499 //-----------------------------------------------------------------------------
3500 // user scalar types: implemented in ryml through to_chars() + from_chars()
3501 
3502 /** @addtogroup doc_sample_helpers
3503  * @{ */
3504 
3505 /** @defgroup doc_sample_scalar_types Serialize/deserialize scalar types
3506  * @{ */
3507 
3508 template<class T> struct vec2 { T x, y; }; ///< example scalar type, serialized and deserialized
3509 template<class T> struct vec3 { T x, y, z; }; ///< example scalar type, serialized and deserialized
3510 template<class T> struct vec4 { T x, y, z, w; }; ///< example scalar type, serialized and deserialized
3511 
3512 template<class T> struct parse_only_vec2 { T x, y; }; ///< example scalar type, deserialized only
3513 template<class T> struct parse_only_vec3 { T x, y, z; }; ///< example scalar type, deserialized only
3514 template<class T> struct parse_only_vec4 { T x, y, z, w; }; ///< example scalar type, deserialized only
3515 
3516 template<class T> struct emit_only_vec2 { T x, y; }; ///< example scalar type, serialized only
3517 template<class T> struct emit_only_vec3 { T x, y, z; }; ///< example scalar type, serialized only
3518 template<class T> struct emit_only_vec4 { T x, y, z, w; }; ///< example scalar type, serialized only
3519 
3520 /** @defgroup doc_sample_to_chars_scalar Define to_chars to write scalar types
3521  *
3522  * @brief To serialize user scalar types, implement the appropriate
3523  * function to_chars (see also @ref doc_to_chars):
3524  *
3525  * ```cpp
3526  * // any of these can be used:
3527  * size_t to_chars(substr buf, T const& v);
3528  * size_t to_chars(substr buf, T v); // this also works, and is good when the type is small
3529  * ```
3530  *
3531  * See the sample @ref sample_user_scalar_types() for an example usage.
3532  *
3533  * Your implementation of to_chars must format v to the given string
3534  * view + return the number of characters written into it. The view
3535  * size (buf.len) must be strictly respected. Return the number of
3536  * characters that need to be written for the value to be completely
3537  * serialized in the string. So if the return value is larger than
3538  * buf.len, ryml will know that the buffer resize the buffer and call
3539  * this again with a larger buffer of the correct size.
3540  *
3541  * In your implementation, you may be interested in using the
3542  * formatting facilities in @ref doc_format_utils and @ref doc_charconv;
3543  * refer to their documentation for further details. But this is not
3544  * mandatory, and anything can be used, provided that the implemented
3545  * `to_chars()` fulfills its contract, described above.
3546  *
3547  * @warning Because of [C++'s ADL
3548  * rules](http://en.cppreference.com/w/cpp/language/adl), **it is
3549  * required to overload these functions in the namespace of the type**
3550  * you're serializing (or in the c4 namespace, or in the c4::yml
3551  * namespace). [Here's an example of an issue where failing to do this
3552  * was causing problems in some
3553  * platforms](https://github.com/biojppm/rapidyaml/issues/424)
3554  *
3555  * @note Please take note of the following pitfall when using
3556  * serialization functions: you may have to include the header with
3557  * your `to_chars()` implementation before any other headers that use
3558  * functions from it. See the include order at the top of this source
3559  * file. This constraint also applies to the conversion functions for
3560  * your types; just like with the STL's headers, they should be
3561  * included prior to ryml's headers. Lately, some effort was directed
3562  * to provide forward declarations to alleviate this problem, but it
3563  * may still occur.
3564  *
3565  * @see string.hpp
3566  * @see string_view.hpp
3567  * @{
3568  */
3569 template<class T> size_t to_chars(ryml::substr buf, vec2<T> v) { return ryml::format(buf, "({},{})", v.x, v.y); }
3570 template<class T> size_t to_chars(ryml::substr buf, vec3<T> v) { return ryml::format(buf, "({},{},{})", v.x, v.y, v.z); }
3571 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); }
3572 
3573 template<class T> size_t to_chars(ryml::substr buf, emit_only_vec2<T> v) { return ryml::format(buf, "({},{})", v.x, v.y); }
3574 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); }
3575 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); }
3576 /** @} */
3577 
3578 
3579 /** @defgroup doc_sample_from_chars_scalar Define from_chars to read scalar types
3580  *
3581  * @brief To deserialize user scalar types, implement the
3582  * function `bool from_chars(csubstr buf, T *val)`; see @ref
3583  * doc_from_chars.
3584  *
3585  * The implementation of from_chars must never read beyond the limit
3586  * of the given buffer, and must return true/false to indicate
3587  * success/failure in the deserialization. On failure, it is up to you
3588  * whether the value is left unchanged; ryml itself does not care
3589  * about the value when the deserialization failed.
3590  *
3591  * In your implementation, you may be interested in using the
3592  * reading facilities in @ref doc_format_utils and @ref doc_charconv;
3593  * refer to their documentation for further details. But this is not
3594  * mandatory, and anything can be used, provided that the implemented
3595  * from_chars fulfills its contract, described above.
3596  *
3597  * @warning Because of [C++'s ADL
3598  * rules](http://en.cppreference.com/w/cpp/language/adl), **it is
3599  * required to overload these functions in the namespace of the type**
3600  * you're serializing (or in the c4 namespace, or in the c4::yml
3601  * namespace). [Here's an example of an issue where failing to do this
3602  * was causing problems in some
3603  * platforms](https://github.com/biojppm/rapidyaml/issues/424)
3604  *
3605  * @note Please take note of the following pitfall when using
3606  * serialization functions: you may have to include the header with
3607  * your `from_chars()` implementation before any other headers that use
3608  * functions from it. See the include order at the top of this source
3609  * file. This constraint also applies to the conversion functions for
3610  * your types; just like with the STL's headers, they should be
3611  * included prior to ryml's headers. Lately, some effort was directed
3612  * to provide forward declarations to alleviate this problem, but it
3613  * may still occur.
3614  *
3615  * @{
3616  */
3617 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; }
3618 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; }
3619 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; }
3620 
3621 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; }
3622 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; }
3623 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; }
3624 /** @} */ // doc_sample_from_chars_scalar
3625 
3626 /** @} */ // doc_sample_scalar_types
3627 /** @} */ // doc_sample_helpers
3628 
3629 
3630 /** to add scalar types (ie leaf types converting to/from string),
3631  * define the functions above for those types. See @ref
3632  * doc_sample_scalar_types. */
3634 {
3635  ryml::Tree t;
3636 
3637  auto r = t.rootref();
3638  r |= ryml::MAP;
3639 
3640  vec2<int> v2in{10, 11};
3641  vec2<int> v2out{1, 2};
3642  r["v2"] << v2in; // serializes to the tree's arena, and then sets the keyval
3643  r["v2"] >> v2out;
3644  CHECK(v2in.x == v2out.x);
3645  CHECK(v2in.y == v2out.y);
3646  vec3<int> v3in{100, 101, 102};
3647  vec3<int> v3out{1, 2, 3};
3648  r["v3"] << v3in; // serializes to the tree's arena, and then sets the keyval
3649  r["v3"] >> v3out;
3650  CHECK(v3in.x == v3out.x);
3651  CHECK(v3in.y == v3out.y);
3652  CHECK(v3in.z == v3out.z);
3653  vec4<int> v4in{1000, 1001, 1002, 1003};
3654  vec4<int> v4out{1, 2, 3, 4};
3655  r["v4"] << v4in; // serializes to the tree's arena, and then sets the keyval
3656  r["v4"] >> v4out;
3657  CHECK(v4in.x == v4out.x);
3658  CHECK(v4in.y == v4out.y);
3659  CHECK(v4in.z == v4out.z);
3660  CHECK(v4in.w == v4out.w);
3661  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
3662  "v2: (10,11)" "\n"
3663  "v3: (100,101,102)" "\n"
3664  "v4: (1000,1001,1002,1003)" "\n"
3665  "");
3666 
3667  // note that only the used functions are needed:
3668  // - if a type is only parsed, then only from_chars() is needed
3669  // - if a type is only emitted, then only to_chars() is needed
3670  emit_only_vec2<int> eov2in{20, 21}; // only has to_chars()
3671  parse_only_vec2<int> pov2out{1, 2}; // only has from_chars()
3672  r["v2"] << eov2in; // serializes to the tree's arena, and then sets the keyval
3673  r["v2"] >> pov2out;
3674  CHECK(eov2in.x == pov2out.x);
3675  CHECK(eov2in.y == pov2out.y);
3676  emit_only_vec3<int> eov3in{30, 31, 32}; // only has to_chars()
3677  parse_only_vec3<int> pov3out{1, 2, 3}; // only has from_chars()
3678  r["v3"] << eov3in; // serializes to the tree's arena, and then sets the keyval
3679  r["v3"] >> pov3out;
3680  CHECK(eov3in.x == pov3out.x);
3681  CHECK(eov3in.y == pov3out.y);
3682  CHECK(eov3in.z == pov3out.z);
3683  emit_only_vec4<int> eov4in{40, 41, 42, 43}; // only has to_chars()
3684  parse_only_vec4<int> pov4out{1, 2, 3, 4}; // only has from_chars()
3685  r["v4"] << eov4in; // serializes to the tree's arena, and then sets the keyval
3686  r["v4"] >> pov4out;
3687  CHECK(eov4in.x == pov4out.x);
3688  CHECK(eov4in.y == pov4out.y);
3689  CHECK(eov4in.z == pov4out.z);
3690  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
3691  "v2: (20,21)" "\n"
3692  "v3: (30,31,32)" "\n"
3693  "v4: (40,41,42,43)" "\n"
3694  "");
3695 }
3696 
3697 
3698 //-----------------------------------------------------------------------------
3699 // user container types: implemented in ryml through write() + read()
3700 
3701 /** @addtogroup doc_sample_helpers
3702  * @{ */
3703 
3704 /** @defgroup doc_sample_container_types Serialize/deserialize container types
3705  *
3706  * To serialize/deserialize container types to a tree, implement the
3707  * appropriate functions:
3708  *
3709  * ```cpp
3710  * void write(ryml::NodeRef *n, T const& seq);
3711  * bool read(ryml::ConstNodeRef const& n, T *seq);
3712  * ```
3713  *
3714  * @warning Because of [C++'s ADL
3715  * rules](http://en.cppreference.com/w/cpp/language/adl), **it is
3716  * required to overload these functions in the namespace of the type**
3717  * you're serializing (or in the c4 namespace, or in the c4::yml
3718  * namespace). [Here's an example of an issue where failing to do this
3719  * was causing problems in some
3720  * platforms](https://github.com/biojppm/rapidyaml/issues/424)
3721  *
3722  * @note Please take note of the following pitfall when using
3723  * serialization functions: you may have to include the header with
3724  * your `write()` or `read()` implementation before any other headers
3725  * that use functions from it. See the include order at the top of
3726  * this source file. This constraint also applies to the conversion
3727  * functions for your types; just like with the STL's headers, they
3728  * should be included prior to ryml's headers. Lately, some effort was
3729  * directed to provide forward declarations to alleviate this problem,
3730  * but it may still occur.
3731  *
3732  * @see sample_container_types
3733  * @see sample_std_types
3734  *
3735  * @{ */
3736 
3737 
3738 /** example user container type: seq-like */
3739 template<class T>
3741 {
3742  std::vector<T> seq_member;
3743 };
3744 /** example user container type: map-like */
3745 template<class K, class V>
3747 {
3748  std::map<K, V> map_member;
3749 };
3750 /** example user container type with nested container members.
3751  * notice all the members have user-defined serialization methods. */
3752 struct my_type
3753 {
3754  // these are leaf nodes:
3758  // these are container nodes:
3761 };
3762 
3763 template<class T>
3764 void write(ryml::NodeRef *n, my_seq_type<T> const& seq)
3765 {
3766  *n |= ryml::SEQ;
3767  for(auto const& v : seq.seq_member)
3768  n->append_child() << v;
3769 }
3770 template<class K, class V>
3772 {
3773  *n |= ryml::MAP;
3774  for(auto const& v : map.map_member)
3775  n->append_child() << ryml::key(v.first) << v.second;
3776 }
3777 void write(ryml::NodeRef *n, my_type const& val)
3778 {
3779  *n |= ryml::MAP;
3780  // these are leaf nodes:
3781  n->append_child() << ryml::key("v2") << val.v2;
3782  n->append_child() << ryml::key("v3") << val.v3;
3783  n->append_child() << ryml::key("v4") << val.v4;
3784  // these are container nodes:
3785  n->append_child() << ryml::key("seq") << val.seq;
3786  n->append_child() << ryml::key("map") << val.map;
3787 }
3788 
3789 template<class T>
3791 {
3792  seq->seq_member.resize(static_cast<size_t>(n.num_children())); // num_children() is O(N)
3793  size_t pos = 0;
3794  for(auto const ch : n.children())
3795  ch >> seq->seq_member[pos++];
3796  return true;
3797 }
3798 template<class K, class V>
3800 {
3801  K k{};
3802  V v{};
3803  for(auto const ch : n)
3804  {
3805  ch >> c4::yml::key(k) >> v;
3806  map->map_member.emplace(std::make_pair(std::move(k), std::move(v)));
3807  }
3808  return true;
3809 }
3810 bool read(ryml::ConstNodeRef const& n, my_type *val)
3811 {
3812  // these are leaf nodes:
3813  n["v2"] >> val->v2;
3814  n["v3"] >> val->v3;
3815  n["v4"] >> val->v4;
3816  // these are container nodes:
3817  n["seq"] >> val->seq;
3818  n["map"] >> val->map;
3819  return true;
3820 }
3821 
3822 /** @} */ // doc_sample_container_types
3823 
3824 /** @} */ // sample_helpers
3825 
3826 
3827 /** shows how to serialize/deserialize container types.
3828  * @see doc_sample_container_types
3829  * @see sample_std_types
3830  * */
3832 {
3833  my_type mt_in{
3834  {20, 21},
3835  {30, 31, 32},
3836  {40, 41, 42, 43},
3837  {{101, 102, 103, 104, 105, 106, 107}},
3838  {{{1001, 2001}, {1002, 2002}, {1003, 2003}}},
3839  };
3840  my_type mt_out;
3841 
3842  ryml::Tree t;
3843  t.rootref() << mt_in; // read from this
3844  t.crootref() >> mt_out; // assign here
3845  CHECK(mt_out.v2.x == mt_in.v2.x);
3846  CHECK(mt_out.v2.y == mt_in.v2.y);
3847  CHECK(mt_out.v3.x == mt_in.v3.x);
3848  CHECK(mt_out.v3.y == mt_in.v3.y);
3849  CHECK(mt_out.v3.z == mt_in.v3.z);
3850  CHECK(mt_out.v4.x == mt_in.v4.x);
3851  CHECK(mt_out.v4.y == mt_in.v4.y);
3852  CHECK(mt_out.v4.z == mt_in.v4.z);
3853  CHECK(mt_out.v4.w == mt_in.v4.w);
3854  CHECK(mt_in.seq.seq_member.size() > 0);
3855  CHECK(mt_out.seq.seq_member.size() == mt_in.seq.seq_member.size());
3856  for(size_t i = 0; i < mt_in.seq.seq_member.size(); ++i)
3857  {
3858  CHECK(mt_out.seq.seq_member[i] == mt_in.seq.seq_member[i]);
3859  }
3860  CHECK(mt_in.map.map_member.size() > 0);
3861  CHECK(mt_out.map.map_member.size() == mt_in.map.map_member.size());
3862  for(auto const& kv : mt_in.map.map_member)
3863  {
3864  CHECK(mt_out.map.map_member.find(kv.first) != mt_out.map.map_member.end());
3865  CHECK(mt_out.map.map_member[kv.first] == kv.second);
3866  }
3867  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
3868  "v2: (20,21)" "\n"
3869  "v3: (30,31,32)" "\n"
3870  "v4: (40,41,42,43)" "\n"
3871  "seq:" "\n"
3872  " - 101" "\n"
3873  " - 102" "\n"
3874  " - 103" "\n"
3875  " - 104" "\n"
3876  " - 105" "\n"
3877  " - 106" "\n"
3878  " - 107" "\n"
3879  "map:" "\n"
3880  " 1001: 2001" "\n"
3881  " 1002: 2002" "\n"
3882  " 1003: 2003" "\n"
3883  "");
3884 }
3885 
3886 
3887 //-----------------------------------------------------------------------------
3888 
3889 /** demonstrates usage with the std implementations provided by ryml
3890  in the ryml_std.hpp header
3891  @see @ref doc_sample_container_types
3892  @see also the STL section in @ref doc_serialization */
3894 {
3895  std::string yml_std_string = ""
3896  "- v2: (20,21)" "\n"
3897  " v3: (30,31,32)" "\n"
3898  " v4: (40,41,42,43)" "\n"
3899  " seq:" "\n"
3900  " - 101" "\n"
3901  " - 102" "\n"
3902  " - 103" "\n"
3903  " - 104" "\n"
3904  " - 105" "\n"
3905  " - 106" "\n"
3906  " - 107" "\n"
3907  " map:" "\n"
3908  " 1001: 2001" "\n"
3909  " 1002: 2002" "\n"
3910  " 1003: 2003" "\n"
3911  "- v2: (120,121)" "\n"
3912  " v3: (130,131,132)" "\n"
3913  " v4: (140,141,142,143)" "\n"
3914  " seq:" "\n"
3915  " - 1101" "\n"
3916  " - 1102" "\n"
3917  " - 1103" "\n"
3918  " - 1104" "\n"
3919  " - 1105" "\n"
3920  " - 1106" "\n"
3921  " - 1107" "\n"
3922  " map:" "\n"
3923  " 11001: 12001" "\n"
3924  " 11002: 12002" "\n"
3925  " 11003: 12003" "\n"
3926  "- v2: (220,221)" "\n"
3927  " v3: (230,231,232)" "\n"
3928  " v4: (240,241,242,243)" "\n"
3929  " seq:" "\n"
3930  " - 2101" "\n"
3931  " - 2102" "\n"
3932  " - 2103" "\n"
3933  " - 2104" "\n"
3934  " - 2105" "\n"
3935  " - 2106" "\n"
3936  " - 2107" "\n"
3937  " map:" "\n"
3938  " 21001: 22001" "\n"
3939  " 21002: 22002" "\n"
3940  " 21003: 22003" "\n"
3941  "";
3942  // parse in-place using the std::string above
3943  ryml::Tree tree = ryml::parse_in_place(ryml::to_substr(yml_std_string));
3944  // my_type is a container-of-containers type. see above its
3945  // definition implementation for ryml.
3946  std::vector<my_type> vmt;
3947  tree.rootref() >> vmt;
3948  CHECK(vmt.size() == 3);
3949  ryml::Tree tree_out;
3950  tree_out.rootref() << vmt;
3951  CHECK(ryml::emitrs_yaml<std::string>(tree_out) == yml_std_string);
3952 }
3953 
3954 
3955 //-----------------------------------------------------------------------------
3956 
3957 /** control precision of serialized floats */
3959 {
3960  std::vector<double> reference{1.23234412342131234, 2.12323123143434237, 3.67847983572591234};
3961  // A safe precision for comparing doubles. May vary depending on
3962  // compiler flags. Double goes to about 15 digits, so 14 should be
3963  // safe enough for this test to succeed.
3964  const double precision_safe = 1.e-14;
3965  const size_t num_digits_safe = 14;
3966  const size_t num_digits_original = 17;
3967  auto get_num_digits = [](ryml::csubstr number){ return number.len - 2u; };
3968  //
3969  // no significant precision is lost when reading
3970  // floating point numbers:
3971  {
3972  ryml::Tree tree = ryml::parse_in_arena("[1.23234412342131234, 2.12323123143434237, 3.67847983572591234]");
3973  std::vector<double> output;
3974  tree.rootref() >> output;
3975  CHECK(output.size() == reference.size());
3976  for(size_t i = 0; i < reference.size(); ++i)
3977  {
3978  CHECK(get_num_digits(tree[(ryml::id_type)i].val()) == num_digits_original);
3979  CHECK(fabs(output[i] - reference[i]) < precision_safe);
3980  }
3981  }
3982  //
3983  // However, depending on the compilation settings, there may be a
3984  // significant precision loss when serializing with the default
3985  // approach, operator<<(double):
3986  {
3987  ryml::Tree serialized;
3988  serialized.rootref() << reference;
3989  std::cout << serialized;
3990  // Without std::to_chars() there is a loss of precision:
3991  #if (!C4CORE_HAVE_STD_TOCHARS) // check if std::to_chars() is available.
3992  CHECK((ryml::emitrs_yaml<std::string>(serialized) == ""
3993  "- 1.23234" "\n"
3994  "- 2.12323" "\n"
3995  "- 3.67848" "\n"
3996  "") || (bool)"this is indicative; the exact results will vary from platform to platform.");
3997  C4_UNUSED(num_digits_safe);
3998  // ... but when using C++17 and above, the results are eminently equal:
3999  #else
4000  CHECK((ryml::emitrs_yaml<std::string>(serialized) == ""
4001  "- 1.2323441234213124" "\n"
4002  "- 2.1232312314343424" "\n"
4003  "- 3.6784798357259123" "\n"
4004  "") || (bool)"this is indicative; the exact results will vary from platform to platform.");
4005  size_t pos = 0;
4006  for(ryml::ConstNodeRef child : serialized.rootref().children())
4007  {
4008  CHECK(get_num_digits(child.val()) >= num_digits_safe);
4009  double out = {};
4010  child >> out;
4011  CHECK(fabs(out - reference[pos++]) < precision_safe);
4012  }
4013  #endif
4014  }
4015  //
4016  // The difference is explained by the availability of
4017  // fastfloat::from_chars(), std::from_chars() and std::to_chars().
4018  //
4019  // ryml prefers the fastfloat::from_chars() version. Unfortunately
4020  // fastfloat does not have to_chars() (see
4021  // https://github.com/fastfloat/fast_float/issues/23).
4022  //
4023  // When C++17 is used, ryml uses std::to_chars(), which produces
4024  // good defaults.
4025  //
4026  // However, with earlier standards, or in some library
4027  // implementations, there's only snprintf() available. Every other
4028  // std library function will either disrespect the string limits,
4029  // or more precisely, accept no string size limits. So the
4030  // implementation of c4core (which ryml uses) falls back to
4031  // snprintf("%g"), and that picks by default a (low) number of
4032  // digits.
4033  //
4034  // But all is not lost for C++11/C++14 users!
4035  //
4036  // To force a particular precision when serializing, you can use
4037  // c4::fmt::real() (brought into the ryml:: namespace). Or you can
4038  // serialize the number yourself! The small downside is that you
4039  // have to build the container.
4040  //
4041  // First a function to check the result:
4042  auto check_precision = [&](ryml::Tree const& serialized){
4043  std::cout << serialized;
4044  // now it works!
4045  CHECK((ryml::emitrs_yaml<std::string>(serialized) == ""
4046  "- 1.23234412342131239" "\n"
4047  "- 2.12323123143434245" "\n"
4048  "- 3.67847983572591231" "\n"
4049  "") || (bool)"this is indicative; the exact results will vary from platform to platform.");
4050  size_t pos = 0;
4051  for(ryml::ConstNodeRef child : serialized.rootref().children())
4052  {
4053  CHECK(get_num_digits(child.val()) == num_digits_original);
4054  double out = {};
4055  child >> out;
4056  CHECK(fabs(out - reference[pos++]) < precision_safe);
4057  }
4058  };
4059  //
4060  // Serialization example using fmt::real()
4061  {
4062  ryml::Tree serialized;
4063  ryml::NodeRef root = serialized.rootref();
4064  root |= ryml::SEQ;
4065  for(const double v : reference)
4066  root.append_child() << ryml::fmt::real(v, num_digits_original, ryml::FTOA_FLOAT);
4067  check_precision(serialized); // OK - now within bounds!
4068  }
4069  //
4070  // Serialization example using snprintf
4071  {
4072  ryml::Tree serialized;
4073  ryml::NodeRef root = serialized.rootref();
4074  root |= ryml::SEQ;
4075  char tmp[64];
4076  for(const double v : reference)
4077  {
4078  // reuse a buffer to serialize.
4079  // add 1 to the significant digits because the %g
4080  // specifier counts the integral digits.
4081  (void)snprintf(tmp, sizeof(tmp), "%.18g", v);
4082  // copy the serialized string to the tree (operator<<
4083  // copies to the arena, operator= just assigns the string
4084  // pointer and would be wrong in this case):
4085  root.append_child() << ryml::to_csubstr((const char*)tmp);
4086  }
4087  check_precision(serialized); // OK - now within bounds!
4088  }
4089 }
4090 
4091 
4092 //-----------------------------------------------------------------------------
4093 
4094 /** demonstrates how to emit to a linear container of char */
4096 {
4097 
4098  ryml::csubstr ymla =
4099  "- 1\n"
4100  "- 2\n"
4101  "";
4102  ryml::csubstr ymlb =
4103  "- a" "\n"
4104  "- b" "\n"
4105  "- x0: 1" "\n"
4106  " x1: 2" "\n"
4107  "- champagne: Dom Perignon" "\n"
4108  " coffee: Arabica" "\n"
4109  " more:" "\n"
4110  " vinho verde: Soalheiro" "\n"
4111  " vinho tinto: Redoma 2017" "\n"
4112  " beer:" "\n"
4113  " - Rochefort 10" "\n"
4114  " - Busch" "\n"
4115  " - Leffe Rituel" "\n"
4116  "- foo" "\n"
4117  "- bar" "\n"
4118  "- baz" "\n"
4119  "- bat" "\n"
4120  "";
4121  const ryml::Tree treea = ryml::parse_in_arena(ymla);
4122  const ryml::Tree treeb = ryml::parse_in_arena(ymlb);
4123 
4124  // it is possible to emit to any linear container of char.
4125  //
4126  // eg, std::vector<char>
4127  {
4128  // do a blank call on an empty buffer to find the required size.
4129  // no overflow will occur, and returns a substr with the size
4130  // required to output
4131  ryml::csubstr output = ryml::emit_yaml(treea, treea.root_id(), ryml::substr{}, /*error_on_excess*/false);
4132  CHECK(output.str == nullptr);
4133  CHECK(output.len > 0);
4134  size_t num_needed_chars = output.len;
4135  std::vector<char> buf(num_needed_chars);
4136  // now try again with the proper buffer
4137  output = ryml::emit_yaml(treea, treea.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4138  CHECK(output == ymla);
4139 
4140  // it is possible to reuse the buffer and grow it as needed.
4141  // first do a blank run to find the size:
4142  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::substr{}, /*error_on_excess*/false);
4143  CHECK(output.str == nullptr);
4144  CHECK(output.len > 0);
4145  CHECK(output.len == ymlb.len);
4146  num_needed_chars = output.len;
4147  buf.resize(num_needed_chars);
4148  // now try again with the proper buffer
4149  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4150  CHECK(output == ymlb);
4151 
4152  // there is a convenience wrapper performing the same as above:
4153  // provided to_substr() is defined for that container.
4154  output = ryml::emitrs_yaml(treeb, &buf);
4155  CHECK(output == ymlb);
4156 
4157  // or you can just output a new container:
4158  // provided to_substr() is defined for that container.
4159  std::vector<char> another = ryml::emitrs_yaml<std::vector<char>>(treeb);
4160  CHECK(ryml::to_csubstr(another) == ymlb);
4161 
4162  // you can also emit nested nodes:
4163  another = ryml::emitrs_yaml<std::vector<char>>(treeb[3][2]);
4164  CHECK(ryml::to_csubstr(another) == ""
4165  "more:" "\n"
4166  " vinho verde: Soalheiro" "\n"
4167  " vinho tinto: Redoma 2017" "\n"
4168  "");
4169  }
4170 
4171 
4172  // eg, std::string. notice this is the same code as above
4173  {
4174  // do a blank call on an empty buffer to find the required size.
4175  // no overflow will occur, and returns a substr with the size
4176  // required to output
4177  ryml::csubstr output = ryml::emit_yaml(treea, treea.root_id(), ryml::substr{}, /*error_on_excess*/false);
4178  CHECK(output.str == nullptr);
4179  CHECK(output.len > 0);
4180  size_t num_needed_chars = output.len;
4181  std::string buf;
4182  buf.resize(num_needed_chars);
4183  // now try again with the proper buffer
4184  output = ryml::emit_yaml(treea, treea.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4185  CHECK(output == ymla);
4186 
4187  // it is possible to reuse the buffer and grow it as needed.
4188  // first do a blank run to find the size:
4189  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::substr{}, /*error_on_excess*/false);
4190  CHECK(output.str == nullptr);
4191  CHECK(output.len > 0);
4192  CHECK(output.len == ymlb.len);
4193  num_needed_chars = output.len;
4194  buf.resize(num_needed_chars);
4195  // now try again with the proper buffer
4196  output = ryml::emit_yaml(treeb, treeb.root_id(), ryml::to_substr(buf), /*error_on_excess*/true);
4197  CHECK(output == ymlb);
4198 
4199  // there is a convenience wrapper performing the above instructions:
4200  // provided to_substr() is defined for that container
4201  output = ryml::emitrs_yaml(treeb, &buf);
4202  CHECK(output == ymlb);
4203 
4204  // or you can just output a new container:
4205  // provided to_substr() is defined for that container.
4206  std::string another = ryml::emitrs_yaml<std::string>(treeb);
4207  CHECK(ryml::to_csubstr(another) == ymlb);
4208 
4209  // you can also emit nested nodes:
4210  another = ryml::emitrs_yaml<std::string>(treeb[3][2]);
4211  CHECK(ryml::to_csubstr(another) == ""
4212  "more:" "\n"
4213  " vinho verde: Soalheiro" "\n"
4214  " vinho tinto: Redoma 2017" "\n"
4215  "");
4216  }
4217 }
4218 
4219 
4220 //-----------------------------------------------------------------------------
4221 
4222 /** demonstrates how to emit to a stream-like structure */
4224 {
4225  ryml::csubstr ymlb =
4226  "- a" "\n"
4227  "- b" "\n"
4228  "- x0: 1" "\n"
4229  " x1: 2" "\n"
4230  "- champagne: Dom Perignon" "\n"
4231  " coffee: Arabica" "\n"
4232  " more:" "\n"
4233  " vinho verde: Soalheiro" "\n"
4234  " vinho tinto: Redoma 2017" "\n"
4235  " beer:" "\n"
4236  " - Rochefort 10" "\n"
4237  " - Busch" "\n"
4238  " - Leffe Rituel" "\n"
4239  "- foo" "\n"
4240  "- bar" "\n"
4241  "- baz" "\n"
4242  "- bat" "\n"
4243  "";
4244  const ryml::Tree tree = ryml::parse_in_arena(ymlb);
4245 
4246  std::string s;
4247 
4248  // emit a full tree
4249  {
4250  std::stringstream ss;
4251  ss << tree; // works with any stream having .operator<<() and .write()
4252  s = ss.str();
4253  CHECK(ryml::to_csubstr(s) == ymlb);
4254  }
4255 
4256  // emit a full tree as json
4257  {
4258  std::stringstream ss;
4259  ss << ryml::as_json(tree); // works with any stream having .operator<<() and .write()
4260  s = ss.str();
4261  CHECK(ryml::to_csubstr(s) ==
4262  "[" "\n"
4263  " \"a\"," "\n"
4264  " \"b\"," "\n"
4265  " {" "\n"
4266  " \"x0\": 1," "\n"
4267  " \"x1\": 2" "\n"
4268  " }," "\n"
4269  " {" "\n"
4270  " \"champagne\": \"Dom Perignon\"," "\n"
4271  " \"coffee\": \"Arabica\"," "\n"
4272  " \"more\": {" "\n"
4273  " \"vinho verde\": \"Soalheiro\"," "\n"
4274  " \"vinho tinto\": \"Redoma 2017\"" "\n"
4275  " }," "\n"
4276  " \"beer\": [" "\n"
4277  " \"Rochefort 10\"," "\n"
4278  " \"Busch\"," "\n"
4279  " \"Leffe Rituel\"" "\n"
4280  " ]" "\n"
4281  " }," "\n"
4282  " \"foo\"," "\n"
4283  " \"bar\"," "\n"
4284  " \"baz\"," "\n"
4285  " \"bat\"" "\n"
4286  "]" "\n"
4287  "");
4288  }
4289 
4290  // emit a nested node
4291  {
4292  std::stringstream ss;
4293  ss << tree[3][2]; // works with any stream having .operator<<() and .write()
4294  s = ss.str();
4295  CHECK(ryml::to_csubstr(s) == ""
4296  "more:" "\n"
4297  " vinho verde: Soalheiro" "\n"
4298  " vinho tinto: Redoma 2017" "\n"
4299  "");
4300  }
4301 
4302  // emit a nested node as json
4303  {
4304  std::stringstream ss;
4305  ss << ryml::as_json(tree[3][2]); // works with any stream having .operator<<() and .write()
4306  s = ss.str();
4307  CHECK(ryml::to_csubstr(s) ==
4308  "\"more\": {" "\n"
4309  " \"vinho verde\": \"Soalheiro\"," "\n"
4310  " \"vinho tinto\": \"Redoma 2017\"" "\n"
4311  "}" "\n"
4312  "");
4313  }
4314 }
4315 
4316 
4317 //-----------------------------------------------------------------------------
4318 
4319 /** demonstrates how to emit to a FILE* */
4321 {
4322  ryml::csubstr yml = ""
4323  "- a" "\n"
4324  "- b" "\n"
4325  "- x0: 1" "\n"
4326  " x1: 2" "\n"
4327  "- champagne: Dom Perignon" "\n"
4328  " coffee: Arabica" "\n"
4329  " more:" "\n"
4330  " vinho verde: Soalheiro" "\n"
4331  " vinho tinto: Redoma 2017" "\n"
4332  " beer:" "\n"
4333  " - Rochefort 10" "\n"
4334  " - Busch" "\n"
4335  " - Leffe Rituel" "\n"
4336  "- foo" "\n"
4337  "- bar" "\n"
4338  "- baz" "\n"
4339  "- bat" "\n"
4340  "";
4341  const ryml::Tree tree = ryml::parse_in_arena(yml);
4342  // this is emitting to stdout, but of course you can pass in any
4343  // FILE* obtained from fopen()
4344  size_t len = ryml::emit_yaml(tree, tree.root_id(), stdout);
4345  // the return value is the number of characters that were written
4346  // to the file
4347  CHECK(len == yml.len);
4348 }
4349 
4350 
4351 //-----------------------------------------------------------------------------
4352 
4353 /** just like parsing into a nested node, you can also emit from a nested node. */
4355 {
4356  const ryml::Tree tree = ryml::parse_in_arena(""
4357  "- a" "\n"
4358  "- b" "\n"
4359  "- x0: 1" "\n"
4360  " x1: 2" "\n"
4361  "- champagne: Dom Perignon" "\n"
4362  " coffee: Arabica" "\n"
4363  " more:" "\n"
4364  " vinho verde: Soalheiro" "\n"
4365  " vinho tinto: Redoma 2017" "\n"
4366  " beer:" "\n"
4367  " - Rochefort 10" "\n"
4368  " - Busch" "\n"
4369  " - Leffe Rituel" "\n"
4370  " - - and so" "\n"
4371  " - many other" "\n"
4372  " - wonderful beers" "\n"
4373  "- more" "\n"
4374  "- seq" "\n"
4375  "- members" "\n"
4376  "- here" "\n"
4377  "");
4378  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"]) == ""
4379  "beer:" "\n"
4380  " - Rochefort 10" "\n"
4381  " - Busch" "\n"
4382  " - Leffe Rituel" "\n"
4383  " - - and so" "\n"
4384  " - many other" "\n"
4385  " - wonderful beers" "\n"
4386  "");
4387  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"][0]) == "Rochefort 10");
4388  CHECK(ryml::emitrs_yaml<std::string>(tree[3]["beer"][3]) == ""
4389  "- and so" "\n"
4390  "- many other" "\n"
4391  "- wonderful beers" "\n"
4392  "");
4393 }
4394 
4395 
4396 //-----------------------------------------------------------------------------
4397 
4398 /** [experimental] query/set/modify node style to control
4399  * formatting of emitted YAML code. */
4401 {
4402  // we will be using this helper throughout this function
4403  auto tostr = [](ryml::ConstNodeRef n) { return ryml::emitrs_yaml<std::string>(n); };
4404  // let's parse this yaml:
4405  ryml::csubstr yaml = ""
4406  "block map:" "\n"
4407  " block key: block val" "\n"
4408  "block seq:" "\n"
4409  " - block val 1" "\n"
4410  " - block val 2" "\n"
4411  " - 'quoted'" "\n"
4412  "flow map, singleline: {flow key: flow val}" "\n"
4413  "flow seq, singleline: [flow val,flow val]" "\n"
4414  "flow map, multiline: {" "\n"
4415  " flow key: flow val" "\n"
4416  " }" "\n"
4417  "flow seq, multiline: [" "\n"
4418  " flow val," "\n"
4419  " flow val" "\n"
4420  " ]" "\n"
4421  "";
4422  ryml::Tree tree = ryml::parse_in_arena(yaml);
4423  // while parsing, ryml marks parsed nodes with their original style:
4424  CHECK(tree.rootref().is_block());
4425  CHECK(tree["block map"].is_key_plain());
4426  CHECK(tree["block seq"].is_key_plain());
4427  CHECK(tree["flow map, singleline"].is_key_plain());
4428  CHECK(tree["flow seq, singleline"].is_key_plain());
4429  CHECK(tree["flow map, multiline"].is_key_plain());
4430  CHECK(tree["flow seq, multiline"].is_key_plain());
4431  CHECK(tree["block map"].is_block());
4432  CHECK(tree["block seq"].is_block());
4433  // flow is either singleline (FLOW_SL) or multiline (FLOW_ML)
4434  CHECK(tree["flow map, singleline"].is_flow_sl());
4435  CHECK(tree["flow seq, singleline"].is_flow_sl());
4436  CHECK(tree["flow map, multiline"].is_flow_ml());
4437  CHECK(tree["flow seq, multiline"].is_flow_ml());
4438  // is_flow() is equivalent to (is_flow_sl() || is_flow_ml())
4439  CHECK(tree["flow map, singleline"].is_flow());
4440  CHECK(tree["flow seq, singleline"].is_flow());
4441  CHECK(tree["flow map, multiline"].is_flow());
4442  CHECK(tree["flow seq, multiline"].is_flow());
4443  //
4444  // since the tree nodes are marked with their original parsed
4445  // style, emitting the parsed tree will preserve the original
4446  // style (minus whitespace):
4447  //
4448  CHECK(tostr(tree) == yaml); // same as before!
4449  //
4450  // you can set/modify the style programatically!
4451  //
4452  // here are more examples.
4453  //
4454  {
4455  ryml::NodeRef n = tree["block map"]; // Let's look at one node
4456  // It looks like this originally:
4457  CHECK(tostr(n) ==
4458  "block map:\n"
4459  " block key: block val\n"
4460  "");
4461  // let's modify its style:
4462  n.set_key_style(ryml::KEY_SQUO); // scalar style: to single-quoted scalar
4463  n.set_container_style(ryml::FLOW_SL); // container style: to flow singleline
4464  // now it looks like this:
4465  CHECK(tostr(n) ==
4466  "'block map': {block key: block val}\n"
4467  "");
4468  }
4469  // next example
4470  {
4471  ryml::NodeRef n = tree["block seq"];
4472  CHECK(tostr(n) == ""
4473  "block seq:\n"
4474  " - block val 1\n"
4475  " - block val 2\n"
4476  " - 'quoted'\n"
4477  "");
4478  n.set_key_style(ryml::KEY_DQUO); // scalar style: to double-quoted scalar
4479  n.set_container_style(ryml::FLOW_ML); // container style: to flow multiline
4480  n[2].set_val_style(ryml::VAL_PLAIN); // scalar style: to plain
4481  CHECK(tostr(n) == ""
4482  "\"block seq\": [\n"
4483  " block val 1,\n"
4484  " block val 2,\n"
4485  " quoted\n"
4486  " ]\n");
4487  }
4488  // next example
4489  {
4490  ryml::NodeRef n = tree["flow map, singleline"];
4491  CHECK(tostr(n) == "flow map, singleline: {flow key: flow val}\n");
4493  n["flow key"].set_val_style(ryml::VAL_LITERAL);
4494  CHECK(tostr(n) == ""
4495  "flow map, singleline:\n"
4496  " flow key: |-\n"
4497  " flow val\n"
4498  "");
4499  }
4500  // next example
4501  {
4502  ryml::NodeRef n = tree["flow map, multiline"];
4503  CHECK(tostr(n) == ""
4504  "flow map, multiline: {\n"
4505  " flow key: flow val\n"
4506  " }\n"
4507  "");
4509  CHECK(tostr(n) == ""
4510  "flow map, multiline:\n"
4511  " flow key: flow val\n"
4512  "");
4513  }
4514  // next example
4515  {
4516  ryml::NodeRef n = tree["flow seq, singleline"];
4517  CHECK(tostr(n) == "flow seq, singleline: [flow val,flow val]\n");
4522  CHECK(tostr(n) == ""
4523  "? >-\n"
4524  " flow seq, singleline\n"
4525  ":\n"
4526  " - 'flow val'\n"
4527  " - \"flow val\"\n"
4528  "");
4529  }
4530  // next example
4531  {
4532  ryml::NodeRef n = tree["flow seq, multiline"];
4533  CHECK(tostr(n) == ""
4534  "flow seq, multiline: [\n"
4535  " flow val,\n"
4536  " flow val\n"
4537  " ]\n"
4538  "");
4540  CHECK(tostr(n) == "flow seq, multiline: [flow val,flow val]\n");
4541  }
4542  // note the full tree now:
4543  CHECK(tostr(tree) != yaml);
4544  CHECK(tostr(tree) ==
4545  "'block map': {block key: block val}" "\n"
4546  "\"block seq\": [" "\n"
4547  " block val 1," "\n"
4548  " block val 2," "\n"
4549  " quoted" "\n"
4550  " ]" "\n"
4551  "flow map, singleline:" "\n"
4552  " flow key: |-" "\n"
4553  " flow val" "\n"
4554  "? >-" "\n"
4555  " flow seq, singleline" "\n"
4556  ":" "\n"
4557  " - 'flow val'" "\n"
4558  " - \"flow val\"" "\n"
4559  "flow map, multiline:" "\n"
4560  " flow key: flow val" "\n"
4561  "flow seq, multiline: [flow val,flow val]" "\n"
4562  "");
4563  // you can clear the style of single nodes:
4564  tree["block map"].clear_style();
4565  tree["block seq"].clear_style();
4566  CHECK(tostr(tree) ==
4567  "block map:" "\n"
4568  " block key: block val" "\n"
4569  "block seq:" "\n"
4570  " - block val 1" "\n"
4571  " - block val 2" "\n"
4572  " - quoted" "\n"
4573  "flow map, singleline:" "\n"
4574  " flow key: |-" "\n"
4575  " flow val" "\n"
4576  "? >-" "\n"
4577  " flow seq, singleline" "\n"
4578  ":" "\n"
4579  " - 'flow val'" "\n"
4580  " - \"flow val\"" "\n"
4581  "flow map, multiline:" "\n"
4582  " flow key: flow val" "\n"
4583  "flow seq, multiline: [flow val,flow val]" "\n"
4584  "");
4585  // you can clear the style recursively:
4586  tree.rootref().clear_style(/*recurse*/true);
4587  // when emitting nodes which have no style set, ryml will default
4588  // to block format for containers, and call
4589  // ryml::scalar_style_choose() to pick the style for each scalar
4590  // (at the cost of a scan over each scalar). Note that ryml picks
4591  // single-quoted for scalars containing commas:
4592  CHECK(tostr(tree) ==
4593  "block map:" "\n"
4594  " block key: block val" "\n"
4595  "block seq:" "\n"
4596  " - block val 1" "\n"
4597  " - block val 2" "\n"
4598  " - quoted" "\n"
4599  "flow map, singleline:" "\n"
4600  " flow key: flow val" "\n"
4601  "flow seq, singleline:" "\n"
4602  " - flow val" "\n"
4603  " - flow val" "\n"
4604  "flow map, multiline:" "\n"
4605  " flow key: flow val" "\n"
4606  "flow seq, multiline:" "\n"
4607  " - flow val" "\n"
4608  " - flow val" "\n"
4609  "");
4610  // you can set the style based on type conditions:
4611  //
4612  // eg, set a single key to single-quoted
4613  tree["block map"].set_style_conditionally(/*type_mask*/ryml::KEY,
4614  /*remflags*/ryml::KEY_STYLE,
4615  /*addflags*/ryml::KEY_SQUO,
4616  /*recurse*/false);
4617  CHECK(tostr(tree) ==
4618  "'block map':" "\n"
4619  " block key: block val" "\n"
4620  "block seq:" "\n"
4621  " - block val 1" "\n"
4622  " - block val 2" "\n"
4623  " - quoted" "\n"
4624  "flow map, singleline:" "\n"
4625  " flow key: flow val" "\n"
4626  "flow seq, singleline:" "\n"
4627  " - flow val" "\n"
4628  " - flow val" "\n"
4629  "flow map, multiline:" "\n"
4630  " flow key: flow val" "\n"
4631  "flow seq, multiline:" "\n"
4632  " - flow val" "\n"
4633  " - flow val" "\n"
4634  "");
4635  // change all keys to single-quoted:
4636  tree.rootref().set_style_conditionally(/*type_mask*/ryml::KEY,
4637  /*remflags*/ryml::KEY_STYLE,
4638  /*addflags*/ryml::KEY_SQUO,
4639  /*recurse*/true);
4640  // change all vals to double-quoted
4641  tree.rootref().set_style_conditionally(/*type_mask*/ryml::VAL,
4642  /*remflags*/ryml::VAL_STYLE,
4643  /*addflags*/ryml::VAL_DQUO,
4644  /*recurse*/true);
4645  // change all seqs to flow
4646  tree.rootref().set_style_conditionally(/*type_mask*/ryml::SEQ,
4647  /*remflags*/ryml::CONTAINER_STYLE,
4648  /*addflags*/ryml::FLOW_SL,
4649  /*recurse*/true);
4650  // change all maps to flow
4651  tree.rootref().set_style_conditionally(/*type_mask*/ryml::MAP,
4652  /*remflags*/ryml::CONTAINER_STYLE,
4653  /*addflags*/ryml::BLOCK,
4654  /*recurse*/true);
4655  // done!
4656  CHECK(tostr(tree) == ""
4657  "'block map':" "\n"
4658  " 'block key': \"block val\"" "\n"
4659  "'block seq': [\"block val 1\",\"block val 2\",\"quoted\"]" "\n"
4660  "'flow map, singleline':" "\n"
4661  " 'flow key': \"flow val\"" "\n"
4662  "'flow seq, singleline': [\"flow val\",\"flow val\"]" "\n"
4663  "'flow map, multiline':" "\n"
4664  " 'flow key': \"flow val\"" "\n"
4665  "'flow seq, multiline': [\"flow val\",\"flow val\"]" "\n"
4666  "");
4667  // you can also set a conditional style in a single node (or its branch if recurse is true):
4668  tree["flow seq, singleline"].set_style_conditionally(/*type_mask*/ryml::SEQ,
4669  /*remflags*/ryml::CONTAINER_STYLE,
4670  /*addflags*/ryml::BLOCK,
4671  /*recurse*/false);
4672  CHECK(tostr(tree) == ""
4673  "'block map':" "\n"
4674  " 'block key': \"block val\"" "\n"
4675  "'block seq': [\"block val 1\",\"block val 2\",\"quoted\"]" "\n"
4676  "'flow map, singleline':" "\n"
4677  " 'flow key': \"flow val\"" "\n"
4678  "'flow seq, singleline':" "\n"
4679  " - \"flow val\"" "\n"
4680  " - \"flow val\"" "\n"
4681  "'flow map, multiline':" "\n"
4682  " 'flow key': \"flow val\"" "\n"
4683  "'flow seq, multiline': [\"flow val\",\"flow val\"]" "\n"
4684  "");
4685  // see also:
4686  // - ryml::scalar_style_choose()
4687  // - ryml::scalar_style_json_choose()
4688  // - ryml::scalar_style_query_squo()
4689  // - ryml::scalar_style_query_plain()
4690 }
4691 
4692 
4693 //-----------------------------------------------------------------------------
4694 
4695 /** [experimental] control the indentation of emitted FLOW_ML containers */
4697 {
4698  // we will be using this helper throughout this function
4699  auto tostr = [](ryml::ConstNodeRef n, ryml::EmitOptions opts) {
4700  return ryml::emitrs_yaml<std::string>(n, opts);
4701  };
4702  ryml::csubstr yaml = "{map: {seq: [0, 1, 2, 3, [40, 41]]}}";
4703  ryml::Tree tree = ryml::parse_in_arena(yaml);
4704  ryml::EmitOptions defaults = {};
4706  CHECK(tostr(tree, defaults) == "{map: {seq: [0,1,2,3,[40,41]]}}");
4707  // let's now set the style to FLOW_ML (it was FLOW_SL)
4709  tree["map"].set_container_style(ryml::FLOW_ML);
4710  tree["map"]["seq"].set_container_style(ryml::FLOW_ML);
4711  tree["map"]["seq"][4].set_container_style(ryml::FLOW_ML);
4712  // by default FLOW_ML prints one value per line, indented:
4713  CHECK(tostr(tree, defaults) ==
4714  "{" "\n"
4715  " map: {" "\n"
4716  " seq: [" "\n"
4717  " 0," "\n"
4718  " 1," "\n"
4719  " 2," "\n"
4720  " 3," "\n"
4721  " [" "\n"
4722  " 40," "\n"
4723  " 41" "\n"
4724  " ]" "\n"
4725  " ]" "\n"
4726  " }" "\n"
4727  "}" "\n"
4728  "");
4729  // if we use the noindent options, then each value is put at the
4730  // beginning of the line
4731  CHECK(tostr(tree, noindent) ==
4732  "{" "\n"
4733  "map: {" "\n"
4734  "seq: [" "\n"
4735  "0," "\n"
4736  "1," "\n"
4737  "2," "\n"
4738  "3," "\n"
4739  "[" "\n"
4740  "40," "\n"
4741  "41" "\n"
4742  "]" "\n"
4743  "]" "\n"
4744  "}" "\n"
4745  "}" "\n"
4746  "");
4747  // Note that the noindent option will safely respect any prior
4748  // indent level from enclosing block containers! For example:
4750  CHECK(tostr(tree, noindent) == ""// notice it is indented at the map level
4751  "map: {" "\n"
4752  " seq: [" "\n"
4753  " 0," "\n"
4754  " 1," "\n"
4755  " 2," "\n"
4756  " 3," "\n"
4757  " [" "\n"
4758  " 40," "\n"
4759  " 41" "\n"
4760  " ]" "\n"
4761  " ]" "\n"
4762  " }" "\n"
4763  "");
4764  // Let's set it one BLOCK level further:
4765  tree["map"].set_container_style(ryml::BLOCK);
4766  CHECK(tostr(tree, noindent) == // notice it is indented one more level
4767  "map:" "\n"
4768  " seq: [" "\n"
4769  " 0," "\n"
4770  " 1," "\n"
4771  " 2," "\n"
4772  " 3," "\n"
4773  " [" "\n"
4774  " 40," "\n"
4775  " 41" "\n"
4776  " ]" "\n"
4777  " ]" "\n"
4778  "");
4779 }
4780 
4781 
4782 //-----------------------------------------------------------------------------
4783 
4784 /** [experimental] set the parser to pick FLOW_SL even if the
4785  * container being parsed is FLOW_ML */
4787 {
4788  ryml::csubstr yaml = ""
4789  "{" "\n"
4790  " map: {" "\n"
4791  " seq: [" "\n"
4792  " 0," "\n"
4793  " 1," "\n"
4794  " 2," "\n"
4795  " 3," "\n"
4796  " [" "\n"
4797  " 40," "\n"
4798  " 41" "\n"
4799  " ]" "\n"
4800  " ]" "\n"
4801  " }" "\n"
4802  "}" "\n"
4803  "";
4804  ryml::csubstr yaml_not_indented = ""
4805  "{" "\n"
4806  "map: {" "\n"
4807  "seq: [" "\n"
4808  "0," "\n"
4809  "1," "\n"
4810  "2," "\n"
4811  "3," "\n"
4812  "[" "\n"
4813  "40," "\n"
4814  "41" "\n"
4815  "]" "\n"
4816  "]" "\n"
4817  "}" "\n"
4818  "}" "\n"
4819  "";
4820  // note that the parser defaults to detect multiline flow
4821  // (FLOW_ML) containers:
4822  {
4823  const ryml::Tree tree = ryml::parse_in_arena(yaml);
4824  CHECK(tree["map"].is_flow_ml()); // etc
4825  // emitted yaml is exactly equal to parsed yaml:
4826  CHECK(ryml::emitrs_yaml<std::string>(tree) == yaml);
4827  }
4828  // if you prefer to shorten the emitted yaml, you can set the
4829  // parser to set singleline flow (FLOW_SL) on all flow containers:
4830  {
4832  const ryml::Tree tree = ryml::parse_in_arena(yaml, opts);
4833  CHECK(tree["map"].is_flow_sl()); // etc
4834  // notice how this is smaller now:
4835  CHECK(ryml::emitrs_yaml<std::string>(tree) ==
4836  R"({map: {seq: [0,1,2,3,[40,41]]}})");
4837  }
4838  // you can also keep FLOW_ML, but control its indentation:
4839  // (see more details in @ref sample_style_flow_ml_indent())
4840  {
4841  const ryml::EmitOptions noindent = ryml::EmitOptions{}.indent_flow_ml(false);
4842  const ryml::Tree tree = ryml::parse_in_arena(yaml);
4843  CHECK(tree["map"].is_flow_ml()); // etc
4844  CHECK(ryml::emitrs_yaml<std::string>(tree, noindent) == yaml_not_indented);
4845  }
4846 }
4847 
4848 
4849 //-----------------------------------------------------------------------------
4850 
4851 /** shows how to parse and emit JSON.
4852  *
4853  * To emit YAML parsed from JSON, see also @ref sample_style() for
4854  * info on clearing the style flags (example below). */
4856 {
4857  ryml::csubstr json = ""
4858  "{" "\n"
4859  " \"doe\": \"a deer, a female deer\"," "\n"
4860  " \"ray\": \"a drop of golden sun\"," "\n"
4861  " \"me\": \"a name, I call myself\"," "\n"
4862  " \"far\": \"a long long way to go\"" "\n"
4863  "}" "\n"
4864  "";
4865  // Since JSON is a subset of YAML, parsing JSON is just the
4866  // same as YAML:
4867  ryml::Tree tree = ryml::parse_in_arena(json);
4868  // If you are sure the source is valid json, you can use the
4869  // appropriate parse_json overload, which is faster because json
4870  // has a smaller grammar:
4871  ryml::Tree json_tree = ryml::parse_json_in_arena(json);
4872  // to emit JSON:
4873  CHECK(ryml::emitrs_json<std::string>(tree) == json);
4874  CHECK(ryml::emitrs_json<std::string>(json_tree) == json);
4875  // to emit JSON to a stream:
4876  std::stringstream ss;
4877  ss << ryml::as_json(tree); // <- mark it like this
4878  CHECK(ss.str() == json);
4879  // Note the following limitations:
4880  //
4881  // - YAML streams cannot be emitted as json, and are not
4882  // allowed. But you can work around this by emitting the
4883  // individual documents separately; see the sample_docs()
4884  // below for such an example.
4885  //
4886  // - tags cannot be emitted as json, and are not allowed.
4887  //
4888  // - anchors and references cannot be emitted as json and
4889  // are not allowed.
4890  //
4891 
4892  // Note that when parsing JSON, ryml will the style of each node
4893  // in the JSON. This means that if you emit as YAML it will look
4894  // mostly the same as the JSON:
4895  std::cout << ryml::emitrs_yaml<std::string>(json_tree);
4896  CHECK(ryml::emitrs_yaml<std::string>(json_tree) == json);
4897  // If you want to avoid this, you will need to clear the style.
4898  json_tree.rootref().clear_style(); // clear the style of the map, but do not recurse
4899  // note that this is now block mode. That is because when no
4900  // style is set, ryml defaults to emitting in block mode.
4901  CHECK(ryml::emitrs_yaml<std::string>(json_tree) == ""
4902  "\"doe\": \"a deer, a female deer\"" "\n"
4903  "\"ray\": \"a drop of golden sun\"" "\n"
4904  "\"me\": \"a name, I call myself\"" "\n"
4905  "\"far\": \"a long long way to go\"" "\n"
4906  "");
4907  // if you don't want the double quotes in the scalar, you can
4908  // recurse:
4909  json_tree.rootref().clear_style(/*recurse*/true);
4910  // so now when emitting you will get this:
4911  // (the scalars with a comma are single-quote)
4912  CHECK(ryml::emitrs_yaml<std::string>(json_tree) == ""
4913  "doe: a deer, a female deer" "\n"
4914  "ray: a drop of golden sun" "\n"
4915  "me: a name, I call myself" "\n"
4916  "far: a long long way to go" "\n"
4917  "");
4918  // you can do custom style changes based on a type mask. this
4919  // will change set the style of all scalar values to single-quoted
4921  /*remflags*/ryml::VAL_STYLE,
4922  /*addflags*/ryml::VAL_SQUO,
4923  /*recurse*/true);
4924  CHECK(ryml::emitrs_yaml<std::string>(json_tree) ==
4925  "doe: 'a deer, a female deer'" "\n"
4926  "ray: 'a drop of golden sun'" "\n"
4927  "me: 'a name, I call myself'" "\n"
4928  "far: 'a long long way to go'" "\n"
4929  "");
4930  // see in particular sample_style() for more examples
4931 }
4932 
4933 
4934 //-----------------------------------------------------------------------------
4935 
4936 /** demonstrates usage with anchors and alias references.
4937 
4938 Note that dereferencing is opt-in; after parsing, you have to call
4939 @ref c4::yml::Tree::resolve() explicitly if you want resolved
4940 references in the tree. This method will resolve all references and
4941 substitute the anchored values in place of the reference.
4942 
4943 The @ref c4::yml::Tree::resolve() method first does a full traversal
4944 of the tree to gather all anchors and references in a separate
4945 collection, then it goes through that collection to locate the names,
4946 which it does by obeying the YAML standard diktat that
4947 
4948  > an alias node refers to the most recent node in
4949  > the serialization having the specified anchor
4950 
4951 So, depending on the number of anchor/alias nodes, this is a
4952 potentially expensive operation, with a best-case linear complexity
4953 (from the initial traversal) and a worst-case quadratic complexity (if
4954 every node has an alias/anchor). This potential cost is the reason for
4955 requiring an explicit call to @ref c4::yml::Tree::resolve() */
4957 {
4958  std::string unresolved = ""
4959  "base: &base" "\n"
4960  " name: Everyone has same name" "\n"
4961  "foo: &foo" "\n"
4962  " <<: *base" "\n"
4963  " age: 10" "\n"
4964  "bar: &bar" "\n"
4965  " <<: *base" "\n"
4966  " age: 20" "\n"
4967  "bill_to: &id001" "\n"
4968  " street: |-" "\n"
4969  " 123 Tornado Alley" "\n"
4970  " Suite 16" "\n"
4971  " city: East Centerville" "\n"
4972  " state: KS" "\n"
4973  "ship_to: *id001" "\n"
4974  "&keyref key: &valref val" "\n"
4975  "*valref : *keyref" "\n"
4976  "";
4977  std::string resolved = ""
4978  "base:" "\n"
4979  " name: Everyone has same name" "\n"
4980  "foo:" "\n"
4981  " name: Everyone has same name" "\n"
4982  " age: 10" "\n"
4983  "bar:" "\n"
4984  " name: Everyone has same name" "\n"
4985  " age: 20" "\n"
4986  "bill_to:" "\n"
4987  " street: |-" "\n"
4988  " 123 Tornado Alley" "\n"
4989  " Suite 16" "\n"
4990  " city: East Centerville" "\n"
4991  " state: KS" "\n"
4992  "ship_to:" "\n"
4993  " street: |-" "\n"
4994  " 123 Tornado Alley" "\n"
4995  " Suite 16" "\n"
4996  " city: East Centerville" "\n"
4997  " state: KS" "\n"
4998  "key: val" "\n"
4999  "val: key" "\n"
5000  "";
5001 
5002  ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(unresolved));
5003  // by default, references are not resolved when parsing:
5004  CHECK( ! tree["base"].has_key_anchor());
5005  CHECK( tree["base"].has_val_anchor());
5006  CHECK( tree["base"].val_anchor() == "base");
5007  CHECK( tree["key"].key_anchor() == "keyref");
5008  CHECK( tree["key"].val_anchor() == "valref");
5009  CHECK( tree["*valref"].is_key_ref());
5010  CHECK( tree["*valref"].is_val_ref());
5011  CHECK( tree["*valref"].key_ref() == "valref");
5012  CHECK( tree["*valref"].val_ref() == "keyref");
5013 
5014  // to resolve references, simply call tree.resolve(),
5015  // which will perform the reference instantiations:
5016  tree.resolve();
5017 
5018  // all the anchors and references are substistuted and then removed:
5019  CHECK( ! tree["base"].has_key_anchor());
5020  CHECK( ! tree["base"].has_val_anchor());
5021  CHECK( ! tree["base"].has_val_anchor());
5022  CHECK( ! tree["key"].has_key_anchor());
5023  CHECK( ! tree["key"].has_val_anchor());
5024  CHECK( ! tree["val"].is_key_ref()); // notice *valref is now turned to val
5025  CHECK( ! tree["val"].is_val_ref()); // notice *valref is now turned to val
5026 
5027  CHECK(tree["ship_to"]["city"].val() == "East Centerville");
5028  CHECK(tree["ship_to"]["state"].val() == "KS");
5029 }
5030 
5031 /** demonstrates how to use the API to programatically create anchors
5032  * and aliases */
5034 {
5035  // part 1: anchor/ref
5036  {
5037  ryml::Tree t;
5038  t.rootref() |= ryml::MAP|ryml::BLOCK;
5039  t["kanchor"] = "2";
5040  t["kanchor"].set_key_anchor("kanchor");
5041  t["vanchor"] = "3";
5042  t["vanchor"].set_val_anchor("vanchor");
5043  // to set a reference, need to call .set_val_ref()/.set_key_ref()
5044  t["kref"].set_val_ref("kanchor");
5045  t["vref"].set_val_ref("vanchor");
5046  t["nref"] = "*vanchor"; // NOTE: this is not set as a reference in the tree!
5047  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
5048  "&kanchor kanchor: 2" "\n"
5049  "vanchor: &vanchor 3" "\n"
5050  "kref: *kanchor" "\n"
5051  "vref: *vanchor" "\n"
5052  // note that ryml emits nref with quotes to disambiguate
5053  // (because no style was set)
5054  "nref: '*vanchor'" "\n"
5055  "");
5056  t.resolve();
5057  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
5058  "kanchor: 2" "\n"
5059  "vanchor: 3" "\n"
5060  "kref: kanchor" "\n"
5061  "vref: 3" "\n"
5062  // note that nref was not resolved
5063  "nref: '*vanchor'" "\n"
5064  "");
5065  }
5066 
5067  // part 2: simple inheritance (ie, adding `<<: *anchor` nodes)
5068  {
5070  "orig: &orig {foo: bar, baz: bat}" "\n"
5071  "copy: {}" "\n"
5072  "notcopy: {}" "\n"
5073  "notref: {}" "\n"
5074  "");
5075  t["copy"]["<<"].set_val_ref("orig");
5076  t["notcopy"]["test"].set_val_ref("orig");
5077  t["notcopy"]["<<"].set_val_ref("orig");
5078  t["notref"]["<<"] = "*orig"; // not a reference! .set_val_ref() was not called
5079  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
5080  "orig: &orig {foo: bar,baz: bat}" "\n"
5081  "copy: {<<: *orig}" "\n"
5082  "notcopy: {test: *orig,<<: *orig}" "\n"
5083  "notref: {<<: '*orig'}" "\n"
5084  "");
5085  t.resolve();
5086  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
5087  "orig: {foo: bar,baz: bat}" "\n"
5088  "copy: {foo: bar,baz: bat}" "\n"
5089  "notcopy: {test: {foo: bar,baz: bat},foo: bar,baz: bat}" "\n"
5090  "notref: {<<: '*orig'}" "\n"
5091  "");
5092  }
5093 
5094  // part 3: multiple inheritance (ie, `<<: [*ref1,*ref2,*etc]`)
5095  {
5097  "orig1: &orig1 {foo: bar}" "\n"
5098  "orig2: &orig2 {baz: bat}" "\n"
5099  "orig3: &orig3 {and: more}" "\n"
5100  "copy: {}" "\n"
5101  "");
5102  ryml::NodeRef seq = t["copy"]["<<"];
5103  seq |= ryml::SEQ;
5104  seq.append_child().set_val_ref("orig1");
5105  seq.append_child().set_val_ref("orig2");
5106  seq.append_child().set_val_ref("orig3");
5107  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
5108  "orig1: &orig1 {foo: bar}" "\n"
5109  "orig2: &orig2 {baz: bat}" "\n"
5110  "orig3: &orig3 {and: more}" "\n"
5111  "copy: {<<: [*orig1,*orig2,*orig3]}" "\n"
5112  "");
5113  t.resolve();
5114  CHECK(ryml::emitrs_yaml<std::string>(t) == ""
5115  "orig1: {foo: bar}" "\n"
5116  "orig2: {baz: bat}" "\n"
5117  "orig3: {and: more}" "\n"
5118  "copy: {foo: bar,baz: bat,and: more}" "\n");
5119  }
5120 }
5121 
5122 
5123 //-----------------------------------------------------------------------------
5124 
5126 {
5127  const std::string yaml = ""
5128  "--- !!map" "\n"
5129  "a: 0" "\n"
5130  "b: 1" "\n"
5131  "--- !map" "\n"
5132  "a: b" "\n"
5133  "--- !!seq" "\n"
5134  "- a" "\n"
5135  "- b" "\n"
5136  "--- !!str a b" "\n"
5137  "--- !!str 'a: b'" "\n"
5138  "---" "\n"
5139  "!!str a: b" "\n"
5140  "--- !!set" "\n"
5141  "? a" "\n"
5142  "? b" "\n"
5143  "--- !!set" "\n"
5144  "a:" "\n"
5145  "--- !!seq" "\n"
5146  "- !!int 0" "\n"
5147  "- !!str 1" "\n"
5148  "";
5149  const ryml::Tree tree = ryml::parse_in_arena(ryml::to_csubstr(yaml));
5150  const ryml::ConstNodeRef root = tree.rootref();
5151  CHECK(root.is_stream());
5152  CHECK(root.num_children() == 9);
5153  for(ryml::ConstNodeRef doc : root.children())
5154  CHECK(doc.is_doc());
5155  // tags are kept verbatim from the source:
5156  CHECK(root[0].has_val_tag());
5157  CHECK(root[0].val_tag() == "!!map"); // valid only if the node has a val tag
5158  CHECK(root[1].val_tag() == "!map");
5159  CHECK(root[2].val_tag() == "!!seq");
5160  CHECK(root[3].val_tag() == "!!str");
5161  CHECK(root[4].val_tag() == "!!str");
5162  CHECK(root[5]["a"].has_key_tag());
5163  CHECK(root[5]["a"].key_tag() == "!!str"); // valid only if the node has a key tag
5164  CHECK(root[6].val_tag() == "!!set");
5165  CHECK(root[7].val_tag() == "!!set");
5166  CHECK(root[8].val_tag() == "!!seq");
5167  CHECK(root[8][0].val_tag() == "!!int");
5168  CHECK(root[8][1].val_tag() == "!!str");
5169  // ryml also provides a complete toolbox to deal with tags.
5170  // there is an enumeration for the standard YAML tags:
5171  CHECK(ryml::to_tag("!map") == ryml::TAG_NONE);
5172  CHECK(ryml::to_tag("!!map") == ryml::TAG_MAP);
5173  CHECK(ryml::to_tag("!!seq") == ryml::TAG_SEQ);
5174  CHECK(ryml::to_tag("!!str") == ryml::TAG_STR);
5175  CHECK(ryml::to_tag("!!int") == ryml::TAG_INT);
5176  CHECK(ryml::to_tag("!!set") == ryml::TAG_SET);
5177  // given a tag enum, you can fetch the short tag string:
5179  CHECK(ryml::from_tag(ryml::TAG_MAP) == "!!map");
5180  CHECK(ryml::from_tag(ryml::TAG_SEQ) == "!!seq");
5181  CHECK(ryml::from_tag(ryml::TAG_STR) == "!!str");
5182  CHECK(ryml::from_tag(ryml::TAG_INT) == "!!int");
5183  CHECK(ryml::from_tag(ryml::TAG_SET) == "!!set");
5184  // you can also fetch the long tag string:
5186  CHECK(ryml::from_tag_long(ryml::TAG_MAP) == "<tag:yaml.org,2002:map>");
5187  CHECK(ryml::from_tag_long(ryml::TAG_SEQ) == "<tag:yaml.org,2002:seq>");
5188  CHECK(ryml::from_tag_long(ryml::TAG_STR) == "<tag:yaml.org,2002:str>");
5189  CHECK(ryml::from_tag_long(ryml::TAG_INT) == "<tag:yaml.org,2002:int>");
5190  CHECK(ryml::from_tag_long(ryml::TAG_SET) == "<tag:yaml.org,2002:set>");
5191  // and likewise:
5192  CHECK(ryml::to_tag("!map") == ryml::TAG_NONE);
5193  CHECK(ryml::to_tag("<tag:yaml.org,2002:map>") == ryml::TAG_MAP);
5194  CHECK(ryml::to_tag("<tag:yaml.org,2002:seq>") == ryml::TAG_SEQ);
5195  CHECK(ryml::to_tag("<tag:yaml.org,2002:str>") == ryml::TAG_STR);
5196  CHECK(ryml::to_tag("<tag:yaml.org,2002:int>") == ryml::TAG_INT);
5197  CHECK(ryml::to_tag("<tag:yaml.org,2002:set>") == ryml::TAG_SET);
5198  // to normalize a tag as much as possible, use normalize_tag():
5199  CHECK(ryml::normalize_tag("!!map") == "!!map");
5200  CHECK(ryml::normalize_tag("!<tag:yaml.org,2002:map>") == "!!map");
5201  CHECK(ryml::normalize_tag("<tag:yaml.org,2002:map>") == "!!map");
5202  CHECK(ryml::normalize_tag("tag:yaml.org,2002:map") == "!!map");
5203  CHECK(ryml::normalize_tag("!<!!map>") == "<!!map>");
5204  CHECK(ryml::normalize_tag("!map") == "!map");
5205  CHECK(ryml::normalize_tag("!my!foo") == "!my!foo");
5206  // and also for the long form:
5207  CHECK(ryml::normalize_tag_long("!!map") == "<tag:yaml.org,2002:map>");
5208  CHECK(ryml::normalize_tag_long("!<tag:yaml.org,2002:map>") == "<tag:yaml.org,2002:map>");
5209  CHECK(ryml::normalize_tag_long("<tag:yaml.org,2002:map>") == "<tag:yaml.org,2002:map>");
5210  CHECK(ryml::normalize_tag_long("tag:yaml.org,2002:map") == "<tag:yaml.org,2002:map>");
5211  CHECK(ryml::normalize_tag_long("!<!!map>") == "<!!map>");
5212  CHECK(ryml::normalize_tag_long("!map") == "!map");
5213  // The tree provides the following methods applying to every node
5214  // with a key and/or val tag:
5215  ryml::Tree normalized_tree = tree;
5216  normalized_tree.normalize_tags(); // normalize all tags in short form
5217  CHECK(ryml::emitrs_yaml<std::string>(normalized_tree) == ""
5218  "--- !!map" "\n"
5219  "a: 0" "\n"
5220  "b: 1" "\n"
5221  "--- !map" "\n"
5222  "a: b" "\n"
5223  "--- !!seq" "\n"
5224  "- a" "\n"
5225  "- b" "\n"
5226  "--- !!str a b" "\n"
5227  "--- !!str 'a: b'" "\n"
5228  "---" "\n"
5229  "!!str a: b" "\n"
5230  "--- !!set" "\n"
5231  "a: " "\n"
5232  "b: " "\n"
5233  "--- !!set" "\n"
5234  "a: " "\n"
5235  "--- !!seq" "\n"
5236  "- !!int 0" "\n"
5237  "- !!str 1" "\n"
5238  "");
5239  ryml::Tree normalized_tree_long = tree;
5240  normalized_tree_long.normalize_tags_long(); // normalize all tags in short form
5241  CHECK(ryml::emitrs_yaml<std::string>(normalized_tree_long) == ""
5242  "--- !<tag:yaml.org,2002:map>" "\n"
5243  "a: 0" "\n"
5244  "b: 1" "\n"
5245  "--- !map" "\n"
5246  "a: b" "\n"
5247  "--- !<tag:yaml.org,2002:seq>" "\n"
5248  "- a" "\n"
5249  "- b" "\n"
5250  "--- !<tag:yaml.org,2002:str> a b" "\n"
5251  "--- !<tag:yaml.org,2002:str> 'a: b'" "\n"
5252  "---" "\n"
5253  "!<tag:yaml.org,2002:str> a: b" "\n"
5254  "--- !<tag:yaml.org,2002:set>" "\n"
5255  "a: " "\n"
5256  "b: " "\n"
5257  "--- !<tag:yaml.org,2002:set>" "\n"
5258  "a: " "\n"
5259  "--- !<tag:yaml.org,2002:seq>" "\n"
5260  "- !<tag:yaml.org,2002:int> 0" "\n"
5261  "- !<tag:yaml.org,2002:str> 1" "\n"
5262  "");
5263 }
5264 
5265 
5266 //-----------------------------------------------------------------------------
5267 
5269 {
5270  const std::string yaml = ""
5271  "%TAG !m! !my-" "\n"
5272  "--- # Bulb here" "\n"
5273  "!m!light fluorescent" "\n"
5274  "..." "\n"
5275  "%TAG !m! !meta-" "\n"
5276  "--- # Color here" "\n"
5277  "!m!light green" "\n"
5278  "";
5279  // tags are not resolved by default:
5281  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
5282  "%TAG !m! !my-" "\n"
5283  "--- !m!light fluorescent" "\n"
5284  "..." "\n"
5285  "%TAG !m! !meta-" "\n"
5286  "--- !m!light green" "\n"
5287  "");
5288  // Use Tree::resolve_tags() to accomplish this in an existing
5289  // tree:
5290  ryml::TagCache tag_cache; // reduces memory requirements by reusing resolved tags
5291  tree.resolve_tags(tag_cache);
5292  CHECK(ryml::emitrs_yaml<std::string>(tree) == ""
5293  "%TAG !m! !my-" "\n"
5294  "--- !<!my-light> fluorescent" "\n"
5295  "..." "\n"
5296  "%TAG !m! !meta-" "\n"
5297  "--- !<!meta-light> green" "\n"
5298  "");
5299  // You can also Use ParserOptions to force resolution of tags
5300  // while parsing:
5302  ryml::Tree resolved_tree = ryml::parse_in_arena(ryml::to_csubstr(yaml), opts);
5303  CHECK(ryml::emitrs_yaml<std::string>(resolved_tree) == ""
5304  "%TAG !m! !my-" "\n"
5305  "--- !<!my-light> fluorescent" "\n"
5306  "..." "\n"
5307  "%TAG !m! !meta-" "\n"
5308  "--- !<!meta-light> green" "\n"
5309  "");
5310  // see also tree.normalize_tags()
5311  // see also tree.normalize_tags_long()
5312 }
5313 
5314 
5315 //-----------------------------------------------------------------------------
5316 
5318 {
5319  std::string yml = ""
5320  "---" "\n"
5321  "a: 0" "\n"
5322  "b: 1" "\n"
5323  "---" "\n"
5324  "c: 2" "\n"
5325  "d: 3" "\n"
5326  "---" "\n"
5327  "- 4" "\n"
5328  "- 5" "\n"
5329  "- 6" "\n"
5330  "- 7" "\n"
5331  "";
5333  CHECK(ryml::emitrs_yaml<std::string>(tree) == yml);
5334 
5335  // iteration through docs
5336  {
5337  // using the node API
5338  const ryml::ConstNodeRef stream = tree.rootref();
5339  CHECK(stream.is_root());
5340  CHECK(stream.is_stream());
5341  CHECK(!stream.is_doc());
5342  CHECK(stream.num_children() == 3);
5343  for(const ryml::ConstNodeRef doc : stream.children())
5344  CHECK(doc.is_doc());
5345  CHECK(tree.docref(0).id() == stream.child(0).id());
5346  CHECK(tree.docref(1).id() == stream.child(1).id());
5347  CHECK(tree.docref(2).id() == stream.child(2).id());
5348  // equivalent: using the lower level index API
5349  const ryml::id_type stream_id = tree.root_id();
5350  CHECK(tree.is_root(stream_id));
5351  CHECK(tree.is_stream(stream_id));
5352  CHECK(!tree.is_doc(stream_id));
5353  CHECK(tree.num_children(stream_id) == 3);
5354  for(ryml::id_type doc_id = tree.first_child(stream_id);
5355  doc_id != ryml::NONE;
5356  doc_id = tree.next_sibling(stream_id))
5357  CHECK(tree.is_doc(doc_id));
5358  CHECK(tree.doc(0) == tree.child(stream_id, 0));
5359  CHECK(tree.doc(1) == tree.child(stream_id, 1));
5360  CHECK(tree.doc(2) == tree.child(stream_id, 2));
5361 
5362  // using the node API
5363  CHECK(stream[0].is_doc());
5364  CHECK(stream[0].is_map());
5365  CHECK(stream[0]["a"].val() == "0");
5366  CHECK(stream[0]["b"].val() == "1");
5367  // equivalent: using the index API
5368  const ryml::id_type doc0_id = tree.first_child(stream_id);
5369  CHECK(tree.is_doc(doc0_id));
5370  CHECK(tree.is_map(doc0_id));
5371  CHECK(tree.val(tree.find_child(doc0_id, "a")) == "0");
5372  CHECK(tree.val(tree.find_child(doc0_id, "b")) == "1");
5373 
5374  // using the node API
5375  CHECK(stream[1].is_doc());
5376  CHECK(stream[1].is_map());
5377  CHECK(stream[1]["c"].val() == "2");
5378  CHECK(stream[1]["d"].val() == "3");
5379  // equivalent: using the index API
5380  const ryml::id_type doc1_id = tree.next_sibling(doc0_id);
5381  CHECK(tree.is_doc(doc1_id));
5382  CHECK(tree.is_map(doc1_id));
5383  CHECK(tree.val(tree.find_child(doc1_id, "c")) == "2");
5384  CHECK(tree.val(tree.find_child(doc1_id, "d")) == "3");
5385 
5386  // using the node API
5387  CHECK(stream[2].is_doc());
5388  CHECK(stream[2].is_seq());
5389  CHECK(stream[2][0].val() == "4");
5390  CHECK(stream[2][1].val() == "5");
5391  CHECK(stream[2][2].val() == "6");
5392  CHECK(stream[2][3].val() == "7");
5393  // equivalent: using the index API
5394  const ryml::id_type doc2_id = tree.next_sibling(doc1_id);
5395  CHECK(tree.is_doc(doc2_id));
5396  CHECK(tree.is_seq(doc2_id));
5397  CHECK(tree.val(tree.child(doc2_id, 0)) == "4");
5398  CHECK(tree.val(tree.child(doc2_id, 1)) == "5");
5399  CHECK(tree.val(tree.child(doc2_id, 2)) == "6");
5400  CHECK(tree.val(tree.child(doc2_id, 3)) == "7");
5401  }
5402 
5403  // Note: since json does not have streams, you cannot emit the above
5404  // tree as json when you start from the root:
5405  //CHECK(ryml::emitrs_json<std::string>(tree) == yml); // RUNTIME ERROR!
5406 
5407  // but, althouth emitting streams as json is not possible,
5408  // you can iterate through individual documents and emit
5409  // them separately:
5410  {
5411  const std::string expected_json[] = {
5412  "{" "\n"
5413  " \"a\": 0," "\n"
5414  " \"b\": 1" "\n"
5415  "}" "\n"
5416  "",
5417  "{" "\n"
5418  " \"c\": 2," "\n"
5419  " \"d\": 3" "\n"
5420  "}" "\n"
5421  "",
5422  "[" "\n"
5423  " 4," "\n"
5424  " 5," "\n"
5425  " 6," "\n"
5426  " 7" "\n"
5427  "]" "\n"
5428  "",
5429  };
5430  // using the node API
5431  {
5432  ryml::id_type count = 0;
5433  const ryml::ConstNodeRef stream = tree.rootref();
5434  CHECK(stream.num_children() == (ryml::id_type)C4_COUNTOF(expected_json));
5435  for(ryml::ConstNodeRef doc : stream.children())
5436  {
5437  CHECK(ryml::emitrs_json<std::string>(doc) == expected_json[count++]);
5438  }
5439  }
5440  // equivalent: using the index API
5441  {
5442  ryml::id_type count = 0;
5443  const ryml::id_type stream_id = tree.root_id();
5444  CHECK(tree.num_children(stream_id) == (ryml::id_type)C4_COUNTOF(expected_json));
5445  for(ryml::id_type doc_id = tree.first_child(stream_id);
5446  doc_id != ryml::NONE;
5447  doc_id = tree.next_sibling(doc_id))
5448  {
5449  CHECK(ryml::emitrs_json<std::string>(tree, doc_id) == expected_json[count++]);
5450  }
5451  }
5452  }
5453 }
5454 
5455 
5456 //-----------------------------------------------------------------------------
5457 
5458 // To avoid imposing a particular type of error handling, ryml uses
5459 // error handler callbacks. This enables users to use exceptions, or
5460 // setjmp()/longjmp(), or plain calls to abort(), as they see fit.
5461 //
5462 // However, it is important to note that the error callbacks must never
5463 // return to the caller! Otherwise, an infinite loop or program crash
5464 // will likely occur.
5465 //
5466 // For this reason, to recover from an error when exceptions are disabled,
5467 // then a non-local jump must be performed using setjmp()/longjmp().
5468 // The code below demonstrates both flows.
5469 //
5470 // ryml provides default error handlers, which call
5471 // std::abort(). You can use the cmake option and the macro
5472 // RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS to have the default error
5473 // handler throw an exception instead.
5474 
5475 /** demonstrates how to set a custom error handler for ryml */
5477 {
5478  ErrorHandlerExample errh; // browse the implementation of this
5479  // class to understand more details
5480  errh.check_disabled();
5481  // set the global error handlers. Note the error callbacks must
5482  // never return: they must either throw an exception, use setjmp()
5483  // and longjmp(), or abort. Otherwise, the parser will enter into
5484  // an infinite loop, or the program may crash.
5486  errh.check_enabled();
5487  CHECK(errh.check_error_occurs([&]{
5488  ryml::Tree tree = ryml::parse_in_arena("errorhandler.yml", "[a: b\n}");
5489  }));
5490  ryml::set_callbacks(errh.original_callbacks); // restore defaults.
5491  errh.check_disabled();
5492 }
5493 
5494 
5495 //-----------------------------------------------------------------------------
5496 
5498 {
5499  auto cause_basic_error = []{
5500  ryml::Tree t;
5501  ryml::csubstr tag_handle = {}, tag_prefix = {}; // invalid, not filled
5502  t.add_tag_directive(tag_handle, tag_prefix, 0);
5503  };
5504  {
5505  ScopedErrorHandlerExample errh; // set the example callbacks (scoped)
5506  CHECK(errh.check_error_occurs(cause_basic_error));
5507  }
5508 #ifdef _RYML_WITH_EXCEPTIONS
5509  bool gotit = false;
5510  try
5511  {
5512  cause_basic_error();
5513  }
5514  catch(ryml::ExceptionBasic const& exc)
5515  {
5516  gotit = true;
5517  ryml::csubstr msg = ryml::to_csubstr(exc.what());
5518  CHECK(!exc.errdata_basic.location.name.empty());
5519  CHECK(!msg.empty());
5520  }
5521  CHECK(gotit);
5522 #endif
5523 }
5524 
5525 
5527 {
5528  ryml::csubstr ymlsrc = ""
5529  "{" "\n"
5530  " a: b" "\n"
5531  " [" "\n"
5532  "";
5533  ryml::csubstr ymlfile = "file.yml";
5534  auto cause_parse_error = [&]{
5535  return ryml::parse_in_arena(ymlfile, ymlsrc);
5536  };
5537  // the YAML in ymlsrc must cause a parse error while it is being
5538  // parsed. We use our error handler to catch that error, and save
5539  // the error info:
5540  ErrorHandlerExample errh;
5541  {
5543  CHECK(errh.check_error_occurs(cause_parse_error));
5544  // the handler in errh saves the error info in itself. Let's
5545  // use that to see the messages we get.
5546  //
5547  // this message is the short message passed into the parse
5548  // error handler:
5549  CHECK(errh.saved_msg_short == "invalid character: '['");
5550  // this message was created inside the handler, by calling
5551  // ryml::err_parse_format():
5552  CHECK(ryml::to_csubstr(errh.saved_msg_full).begins_with("file.yml:3: col=4 (12B): ERROR: [parse] invalid character: '['"));
5553  // If you keep the YAML source buffer around, you can also use
5554  // it to create/print a larger error message showing the
5555  // YAML source code context which causes the error:
5556  std::string msg_ctx = errh.saved_msg_full + "\n";
5557  ryml::location_format_with_context([&msg_ctx](ryml::csubstr s){
5558  msg_ctx.append(s.str, s.len);
5559  }, errh.saved_parse_loc, ymlsrc, "err");
5560  CHECK(ryml::to_csubstr(msg_ctx).begins_with("file.yml:3: col=4 (12B): ERROR: [parse] invalid character: '['"));
5561  CHECK(ryml::to_csubstr(msg_ctx).ends_with(
5562  "file.yml:3: col=4 (12B): err:" "\n"
5563  "err:" "\n"
5564  "err: [" "\n"
5565  "err: |" "\n"
5566  "err: (here)" "\n"
5567  "err:" "\n"
5568  "err: see region:" "\n"
5569  "err:" "\n"
5570  "err: {" "\n"
5571  "err: a: b" "\n"
5572  "err: [" "\n"
5573  "err: |" "\n"
5574  "err: (here)" "\n"
5575  ""));
5576  //
5577  // Let's now check the location (see the message above):
5578  CHECK(errh.saved_parse_loc.name == ymlfile);
5579  CHECK(errh.saved_parse_loc.line == 3);
5580  CHECK(errh.saved_parse_loc.col == 4);
5581  CHECK(errh.saved_parse_loc.offset == 12);
5582  CHECK(errh.saved_parse_loc.offset <= ymlsrc.len);
5583  // ... and this is the location in the ryml source code file where
5584  // this error was found:
5585  CHECK(!errh.saved_basic_loc.name.empty());
5586  CHECK(errh.saved_basic_loc.line > 0);
5587  CHECK(errh.saved_basic_loc.col > 0);
5588  CHECK(errh.saved_basic_loc.offset > 0);
5590  }
5591  // A parse error is also a basic error. If no parse error handler
5592  // is set, then ryml falls back to a basic error:
5593  {
5594  ryml::Callbacks cb = errh.callbacks();
5595  cb.m_error_parse = nullptr;
5596  ryml::set_callbacks(cb);
5597  CHECK(ryml::get_callbacks().m_error_parse == nullptr);
5598  CHECK(errh.check_error_occurs(cause_parse_error));
5599  // we got a basic error instead of a parse error:
5600  CHECK(errh.saved_msg_short == "invalid character: '['");
5601  // notice that the full message now displays this as a basic
5602  // error:
5603  CHECK(errh.saved_msg_full == "file.yml:3: col=4 (12B): ERROR: [basic] invalid character: '['");
5604  // the yml location is now in the location saved from the basic error
5605  CHECK(errh.saved_basic_loc.name == ymlfile);
5606  CHECK(errh.saved_basic_loc.line == 3);
5607  CHECK(errh.saved_basic_loc.col == 4);
5608  CHECK(errh.saved_basic_loc.offset == 12);
5609  CHECK(errh.saved_basic_loc.offset <= ymlsrc.len);
5610  CHECK(errh.saved_parse_loc.name == ryml::csubstr{});
5615  }
5616 #ifdef _RYML_WITH_EXCEPTIONS
5617  bool gotit = false;
5618  try
5619  {
5620  cause_parse_error();
5621  }
5622  catch(ryml::ExceptionParse const& exc)
5623  {
5624  gotit = true;
5625  ryml::csubstr msg = ryml::to_csubstr(exc.what());
5626  CHECK(exc.errdata_parse.ymlloc.name == ymlfile);
5627  CHECK(exc.errdata_parse.ymlloc.line == 3);
5628  CHECK(exc.errdata_parse.ymlloc.col == 4);
5629  CHECK(exc.errdata_parse.ymlloc.offset == 12);
5630  CHECK(exc.errdata_parse.ymlloc.offset <= ymlsrc.len);
5631  // the message saved in the exception is just the concrete error description:
5632  CHECK(msg == "invalid character: '['");
5633  // to print richer error messages, ryml provides helpers to
5634  // format that description into a complete error message,
5635  // containing location and source context indication:
5636  std::string full;
5637  auto dumpfn = [&full](ryml::csubstr s) { full.append(s.str, s.len); };
5638  ryml::err_parse_format(dumpfn, msg, exc.errdata_parse);
5639  full += '\n';
5640  ryml::location_format_with_context(dumpfn, exc.errdata_parse.ymlloc, ymlsrc, "err", 3);
5641  CHECK(ryml::to_csubstr(full).begins_with("file.yml:3: col=4 (12B): ERROR: [parse] invalid character: '['"));
5642  CHECK(ryml::to_csubstr(full).ends_with(
5643  "file.yml:3: col=4 (12B): err:" "\n"
5644  "err:" "\n"
5645  "err: [" "\n"
5646  "err: |" "\n"
5647  "err: (here)" "\n"
5648  "err:" "\n"
5649  "err: see region:" "\n"
5650  "err:" "\n"
5651  "err: {" "\n"
5652  "err: a: b" "\n"
5653  "err: [" "\n"
5654  "err: |" "\n"
5655  "err: (here)" "\n"
5656  ""));
5657  }
5658  CHECK(gotit);
5659  gotit = false;
5660  try
5661  {
5662  cause_parse_error();
5663  }
5664  catch(ryml::ExceptionBasic const& exc) // use references! don't slice the exception
5665  {
5666  gotit = true;
5667  ryml::csubstr msg = ryml::to_csubstr(exc.what());
5668  CHECK(!exc.errdata_basic.location.name.empty());
5669  CHECK(!msg.empty());
5670  }
5671  CHECK(gotit);
5672 #endif
5673 }
5674 
5675 
5676 /** Visit errors happen when an error is triggered while reading from
5677  * a node. */
5679 {
5680  ryml::csubstr ymlfile = "file.yml";
5681  ryml::csubstr ymlsrc = "float: 123.456";
5682  ErrorHandlerExample errh;
5683  {
5685  ryml::Tree tree = ryml::parse_in_arena(ymlfile, ymlsrc);
5686  CHECK(errh.check_error_occurs([&]{
5687  int intval = 0;
5688  tree["float"] >> intval; // cannot deserialize 123.456 to int
5689  }));
5690  // the handler in errh saves the error info in itself. Let's
5691  // use that to see the messages we get.
5692  //
5693  // this message is the short message passed into the visit error
5694  CHECK(errh.saved_msg_short == "could not deserialize value");
5695  // this message was created inside the handler, by calling
5696  // ryml::err_visit_format():
5697  CHECK(ryml::csubstr::npos != ryml::to_csubstr(errh.saved_msg_full).find("ERROR: [visit] could not deserialize value"));
5698  // The location of the visit error is of the C++ source file where
5699  // the error was detected -- NOT of the YAML source file:
5700  CHECK(errh.saved_basic_loc.name != ymlfile);
5701  // However, note that the tree and node id are available:
5702  CHECK(errh.saved_visit_tree == &tree);
5703  CHECK(errh.saved_visit_id == tree["float"].id());
5704  // see sample_error_visit_location() for an example on how
5705  // to extract the location.
5706  }
5707  // visit errors also fall back to basic errors when the visit
5708  // handler is not set (similar to the behavior of ExceptionVisit):
5709  {
5710  ryml::Callbacks cb = errh.callbacks();
5711  cb.m_error_visit = nullptr;
5712  ryml::set_callbacks(cb);
5713  CHECK(ryml::get_callbacks().m_error_visit == nullptr);
5714  ryml::Tree tree = ryml::parse_in_arena(ymlfile, ymlsrc);
5715  CHECK(errh.check_error_occurs([&]{
5716  int intval = 0;
5717  tree["float"] >> intval; // cannot deserialize 123.456 to int
5718  }));
5719  // we got a basic error instead of a visit error:
5720  CHECK(errh.saved_msg_short == "could not deserialize value");
5721  // notice that the full message now displays this as a basic
5722  // error:
5723  CHECK(ryml::csubstr::npos != ryml::to_csubstr(errh.saved_msg_full).find("ERROR: [basic] could not deserialize value"));
5724  // the tree and id are not set, because this was called as a basic error
5725  CHECK(errh.saved_visit_tree == nullptr);
5726  CHECK(errh.saved_visit_id == ryml::NONE);
5728  }
5729 #ifdef _RYML_WITH_EXCEPTIONS
5730  // when using the default ryml callbacks (see
5731  // RYML_NO_DEFAULT_CALLBACKS), and
5732  // RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS is defined, the ryml
5733  // parse handler throws an exception of type ryml::ExceptionVisit,
5734  // which is derived from ryml::ExceptionBasic.
5735  {
5736  const ryml::Tree tree = ryml::parse_in_arena(ymlfile, ymlsrc);
5737  bool gotit = false;
5738  try
5739  {
5740  int intval = 0;
5741  tree["float"] >> intval; // cannot deserialize 123.456 to int
5742  }
5743  catch(ryml::ExceptionVisit const& exc)
5744  {
5745  gotit = true;
5746  ryml::csubstr msg = ryml::to_csubstr(exc.what());
5747  CHECK(!exc.errdata_visit.cpploc.name.empty());
5748  CHECK(exc.errdata_visit.tree == &tree);
5749  CHECK(exc.errdata_visit.node == tree["float"].id());
5750  CHECK(!msg.empty());
5751  }
5752  CHECK(gotit);
5753  }
5754  // you can also catch the exception as its base,
5755  // ryml::ExceptionBasic:
5756  {
5757  const ryml::Tree tree = ryml::parse_in_arena(ymlfile, ymlsrc);
5758  bool gotit = false;
5759  try
5760  {
5761  int intval = 0;
5762  tree["float"] >> intval; // cannot deserialize 123.456 to int
5763  }
5764  catch(ryml::ExceptionBasic const& exc) // use references! don't slice the exception
5765  {
5766  gotit = true;
5767  ryml::csubstr msg = ryml::to_csubstr(exc.what());
5768  CHECK(!exc.errdata_basic.location.name.empty());
5769  CHECK(!msg.empty());
5770  }
5771  CHECK(gotit);
5772  }
5773 #endif
5774 }
5775 
5776 
5777 /** It is possible to obtain the YAML location from a visit
5778  * error: when the tree is obtained from parsing YAML, the messages
5779  * may be enriched by using a parser set to track the locations. See
5780  * @ref sample_location_tracking() for more details on how to use
5781  * locations. */
5783 {
5785  // we will use locations to show the YAML source context of the
5786  // node where the visit error was triggered. This is a very
5787  // convenient feature to show detailed messages when deserializing
5788  // data read from a file (but do note this is opt-in, and it is
5789  // not mandatory). See sample_location_tracking() for more details
5790  // on location tracking.
5792  ryml::EventHandlerTree evt_handler{};
5793  ryml::Parser parser(&evt_handler, opts);
5794  ryml::csubstr ymlfile = "file.yml";
5795  ryml::csubstr ymlsrc = ""
5796  "foo: bar" "\n"
5797  "char: a" "\n"
5798  "int: a" "\n"
5799  "float: 123.456" "\n"
5800  "";
5801  const ryml::Tree tree = ryml::parse_in_arena(&parser, ymlfile, ymlsrc);
5802  // This function will cause a visit error when being called:
5803  auto cause_visit_error = [&]{
5804  int intval = 0;
5805  tree["float"] >> intval; // cannot deserialize 123.456 to int
5806  };
5807  // Like with the parse error, we will use our error handler to
5808  // catch that visit error, and save the error info:
5809  CHECK(evt_handler.callbacks() == errh.callbacks());
5810  CHECK(parser.callbacks() == errh.callbacks());
5811  CHECK(tree.callbacks() == errh.callbacks());
5812  {
5813  CHECK(errh.check_error_occurs(cause_visit_error));
5814  // the handler in errh saves the error info in itself. Let's
5815  // use that to see the messages we get.
5816  //
5817  // this message is the short message passed into the visit error
5818  CHECK(errh.saved_msg_short == "could not deserialize value");
5819  // this message was created inside the handler, by calling
5820  // ryml::err_visit_format():
5821  CHECK(ryml::csubstr::npos != ryml::to_csubstr(errh.saved_msg_full).find("ERROR: [visit] could not deserialize value"));
5822  // The location of the visit error is of the C++ source file where
5823  // the error was detected -- NOT of the YAML source file:
5824  CHECK(errh.saved_basic_loc.name != ymlfile);
5825  // However, note that the tree and node id are available:
5826  CHECK(errh.saved_visit_tree == &tree);
5827  CHECK(errh.saved_visit_id == tree["float"].id());
5828  // ... which we can use to get the location in the YAML source
5829  // from the parser (but see @ref sample_location_tracking()):
5830  ryml::Location ymlloc = errh.saved_visit_tree->location(parser, errh.saved_visit_id);
5831  CHECK(ymlloc.name == ymlfile);
5832  // In turn, we can use format_location_context() to
5833  // print/create an error message pointing at the YAML source
5834  // code:
5835  std::string msg = errh.saved_msg_full;
5836  ryml::location_format_with_context([&msg](ryml::csubstr s){
5837  msg.append(s.str, s.len);
5838  }, ymlloc, ymlsrc, "err", /*number of lines to show before the error*/3);
5839  CHECK(ryml::to_csubstr(msg).ends_with(
5840  "file.yml:3: col=0 (24B): err:" "\n"
5841  "err:" "\n"
5842  "err: float: 123.456" "\n"
5843  "err: |" "\n"
5844  "err: (here)" "\n"
5845  "err:" "\n"
5846  "err: see region:" "\n"
5847  "err:" "\n"
5848  "err: foo: bar" "\n"
5849  "err: char: a" "\n"
5850  "err: int: a" "\n"
5851  "err: float: 123.456" "\n"
5852  "err: |" "\n"
5853  "err: (here)" "\n"
5854  ""));
5855  }
5856 }
5857 
5858 
5859 //-----------------------------------------------------------------------------
5860 
5861 // Please note the following about the use of custom allocators with
5862 // ryml. Due to [the static initialization order
5863 // fiasco](https://en.cppreference.com/w/cpp/language/siof), if you
5864 // use static ryml trees or parsers, you need to make sure that their
5865 // callbacks have the same lifetime. So you can't use ryml's default
5866 // callbacks structure, as it is declared in a ryml file, and the standard
5867 // provides no guarantee on the relative initialization order, such
5868 // that it is constructed before and destroyed after your
5869 // variables (in fact you are pretty much guaranteed to see this
5870 // fail). So please carefully consider your choices, and ponder
5871 // whether you really need to use ryml static trees and parsers. If
5872 // you do need this, then you will need to declare and use a ryml
5873 // callbacks structure that outlives the tree and/or parser.
5874 //
5875 // See also sample_static_trees() for an example on how to use
5876 // trees with static lifetime.
5877 
5878 /** @addtogroup doc_sample_helpers
5879  * @{ */
5881 {
5882  std::vector<char> memory_pool = std::vector<char>(10u * 1024u); // 10KB
5883  size_t num_allocs = 0, alloc_size = 0, corr_size = 0;
5884  size_t num_deallocs = 0, dealloc_size = 0;
5885 
5886  void *allocate(size_t len)
5887  {
5888  void *ptr = &memory_pool[alloc_size];
5889  alloc_size += len;
5890  ++num_allocs;
5891  // ensure the ptr is aligned
5892  uintptr_t uptr = (uintptr_t)ptr;
5893  const uintptr_t align = alignof(max_align_t);
5894  if (uptr % align)
5895  {
5896  uintptr_t prev = uptr - (uptr % align);
5897  uintptr_t next = prev + align;
5898  uintptr_t corr = next - uptr;
5899  ptr = (void*)(((char*)ptr) + corr);
5900  corr_size += corr;
5901  }
5902  C4_CHECK_MSG(alloc_size + corr_size <= memory_pool.size(),
5903  "out of memory! requested=%zu+%zu available=%zu\n",
5904  alloc_size, corr_size, memory_pool.size());
5905  return ptr;
5906  }
5907 
5908  void free(void *mem, size_t len)
5909  {
5910  CHECK((char*)mem >= &memory_pool.front() && (char*)mem < &memory_pool.back());
5911  CHECK((char*)mem+len >= &memory_pool.front() && (char*)mem+len <= &memory_pool.back());
5912  dealloc_size += len;
5913  ++num_deallocs;
5914  // no need to free here
5915  }
5916 
5917  // bridge
5919  {
5920  ryml::Callbacks cb = {};
5921  return cb.set_user_data(this)
5924  }
5925  static void* s_allocate(size_t len, void* /*hint*/, void *this_)
5926  {
5927  return ((GlobalAllocatorExample*)this_)->allocate(len);
5928  }
5929  static void s_free(void *mem, size_t len, void *this_)
5930  {
5931  ((GlobalAllocatorExample*)this_)->free(mem, len);
5932  }
5933 
5934  // checking
5936  {
5937  check_and_reset();
5938  }
5940  {
5941  std::cout << "size: alloc=" << alloc_size << " dealloc=" << dealloc_size << std::endl;
5942  std::cout << "count: #allocs=" << num_allocs << " #deallocs=" << num_deallocs << std::endl;
5944  CHECK(alloc_size >= dealloc_size); // failure here means a double free
5945  CHECK(alloc_size == dealloc_size); // failure here means a leak
5946  num_allocs = 0;
5947  num_deallocs = 0;
5948  alloc_size = 0;
5949  dealloc_size = 0;
5950  }
5951 };
5952 /** @} */
5953 
5954 
5955 /** demonstrates how to set the global allocator for ryml */
5957 {
5959 
5960  // save the existing callbacks for restoring
5961  ryml::Callbacks defaults = ryml::get_callbacks();
5962 
5963  // set to our callbacks
5965 
5966  // verify that the allocator is in effect
5967  ryml::Callbacks const& current = ryml::get_callbacks();
5968  CHECK(current.m_allocate == &mem.s_allocate);
5969  CHECK(current.m_free == &mem.s_free);
5970 
5971  // so far nothing was allocated
5972  CHECK(mem.alloc_size == 0);
5973 
5974  // parse one tree and check
5975  (void)ryml::parse_in_arena(R"({foo: bar})");
5976  mem.check_and_reset();
5977 
5978  // parse another tree and check
5979  (void)ryml::parse_in_arena(R"([a, b, c, d, {foo: bar, money: pennys}])");
5980  mem.check_and_reset();
5981 
5982  // verify that by reserving we save allocations
5983  {
5984  ryml::EventHandlerTree evt_handler;
5985  ryml::Parser parser(&evt_handler); // reuse a parser
5986  ryml::Tree tree; // reuse a tree
5987 
5988  tree.reserve(10); // reserve the number of nodes
5989  tree.reserve_arena(100); // reserve the arena size
5990  parser.reserve_stack(10); // reserve the parser depth.
5991 
5992  // since the parser stack uses Small Storage Optimization,
5993  // allocations will only happen with capacities higher than 16.
5994  CHECK(mem.num_allocs == 2); // tree, tree_arena and NOT the parser
5995 
5996  parser.reserve_stack(20); // reserve the parser depth.
5997  CHECK(mem.num_allocs == 3); // tree, tree_arena and now the parser as well
5998 
5999  // verify that no other allocations occur when parsing
6000  size_t size_before = mem.alloc_size;
6001  parse_in_arena(&parser, "", R"([a, b, c, d, {foo: bar, money: pennys}])", &tree);
6002  CHECK(mem.alloc_size == size_before);
6003  CHECK(mem.num_allocs == 3);
6004  }
6005  mem.check_and_reset();
6006 
6007  // restore defaults.
6008  ryml::set_callbacks(defaults);
6009 }
6010 
6011 
6012 //-----------------------------------------------------------------------------
6013 
6014 /** @addtogroup doc_sample_helpers
6015  * @{ */
6016 
6017 /** an example for a per-tree memory allocator */
6019 {
6020  std::vector<char> memory_pool = std::vector<char>(10u * 1024u); // 10KB
6021  size_t num_allocs = 0, alloc_size = 0;
6022  size_t num_deallocs = 0, dealloc_size = 0;
6023 
6025  {
6026  // Above we used static functions to bridge to our methods.
6027  // To show a different approach, we employ lambdas here.
6028  // Note that there can be no captures in the lambdas
6029  // because these are C-style function pointers.
6030  ryml::Callbacks cb;
6031  cb.m_user_data = (void*) this;
6032  cb.m_allocate = [](size_t len, void *, void *data){ return ((PerTreeMemoryExample*) data)->allocate(len); };
6033  cb.m_free = [](void *mem, size_t len, void *data){ return ((PerTreeMemoryExample*) data)->free(mem, len); };
6034  return cb;
6035  }
6036 
6037  void *allocate(size_t len)
6038  {
6039  void *ptr = &memory_pool[alloc_size];
6040  alloc_size += len;
6041  ++num_allocs;
6042  if(C4_UNLIKELY(alloc_size > memory_pool.size()))
6043  {
6044  std::cerr << "out of memory! requested=" << alloc_size << " vs " << memory_pool.size() << " available" << std::endl;
6045  std::abort();
6046  }
6047  return ptr;
6048  }
6049 
6050  void free(void *mem, size_t len)
6051  {
6052  CHECK((char*)mem >= &memory_pool.front() && (char*)mem < &memory_pool.back());
6053  CHECK((char*)mem+len >= &memory_pool.front() && (char*)mem+len <= &memory_pool.back());
6054  dealloc_size += len;
6055  ++num_deallocs;
6056  // no need to free here
6057  }
6058 
6059  // checking
6061  {
6062  check_and_reset();
6063  }
6065  {
6066  std::cout << "size: alloc=" << alloc_size << " dealloc=" << dealloc_size << std::endl;
6067  std::cout << "count: #allocs=" << num_allocs << " #deallocs=" << num_deallocs << std::endl;
6069  CHECK(alloc_size >= dealloc_size); // failure here means a double free
6070  CHECK(alloc_size == dealloc_size); // failure here means a leak
6071  num_allocs = 0;
6072  num_deallocs = 0;
6073  alloc_size = 0;
6074  dealloc_size = 0;
6075  }
6076 };
6077 /** @} */
6078 
6080 {
6084 
6085  // the trees will use the memory in the resources above,
6086  // with each tree using a separate resource
6087  {
6088  // Watchout: ensure that the lifetime of the callbacks target
6089  // exceeds the lifetime of the tree.
6090  ryml::EventHandlerTree evt_handler(mrp.callbacks());
6091  ryml::Parser parser(&evt_handler);
6092  ryml::Tree tree1(mr1.callbacks());
6093  ryml::Tree tree2(mr2.callbacks());
6094 
6095  ryml::csubstr yml1 = "{a: b}";
6096  ryml::csubstr yml2 = "{c: d, e: f, g: [h, i, 0, 1, 2, 3]}";
6097 
6098  parse_in_arena(&parser, "file1.yml", yml1, &tree1);
6099  parse_in_arena(&parser, "file2.yml", yml2, &tree2);
6100  }
6101 
6102  CHECK(mrp.num_allocs == 0); // YAML depth not large enough to warrant a parser allocation
6103  CHECK(mr1.alloc_size <= mr2.alloc_size); // because yml2 has more nodes
6104 }
6105 
6106 
6107 //-----------------------------------------------------------------------------
6108 
6109 
6110 /** shows how to work around the static initialization order fiasco
6111  * when using a static-duration ryml tree
6112  * @see https://en.cppreference.com/w/cpp/language/siof */
6114 {
6115  // Static trees may incur a static initialization order
6116  // problem. This happens because a default-constructed tree will
6117  // obtain the callbacks from the current global setting, which may
6118  // not have been initialized due to undefined static
6119  // initialization order:
6120  //
6121  // ERROR! depends on ryml::get_callbacks() which may not have been initialized.
6122  //static ryml::Tree tree;
6123  //
6124  // To work around the issue, declare static callbacks
6125  // to explicitly initialize the static tree:
6126  static ryml::Callbacks callbacks = default_callbacks(); // use default callback members
6127  static ryml::Tree tree(callbacks); // OK
6128  // now you can use the tree as normal:
6129  ryml::parse_in_arena(R"(doe: "a deer, a female deer")", &tree);
6130  CHECK(tree["doe"].val() == "a deer, a female deer");
6131 }
6132 
6133 
6134 //-----------------------------------------------------------------------------
6135 //-----------------------------------------------------------------------------
6136 //-----------------------------------------------------------------------------
6137 /** demonstrates how to obtain the (zero-based) location of a node
6138  * from a recently parsed tree */
6140 {
6141  // NOTE: locations are zero-based. If you intend to show the
6142  // location to a human user, you may want to pre-increment the line
6143  // and column by 1.
6144  ryml::csubstr yaml = ""
6145  "{" "\n"
6146  "aa: contents," "\n"
6147  "foo: [one, [two, three]]" "\n"
6148  "}" "\n"
6149  "";
6150  // A parser is needed to track locations, and it has to be
6151  // explicitly set to do it. Location tracking is disabled by
6152  // default.
6153  ryml::ParserOptions opts = {};
6154  opts.locations(true); // enable locations, default is false
6155  ryml::EventHandlerTree evt_handler = {};
6156  ryml::Parser parser(&evt_handler, opts);
6157  CHECK(parser.options().locations());
6158  // When locations are enabled, the first task while parsing will
6159  // consist of building and caching (in the parser) a
6160  // source-to-node lookup structure to accelerate location lookups.
6161  //
6162  // The cost of building the location accelerator is linear in the
6163  // size of the source buffer. This increased cost is the reason
6164  // for the opt-in requirement. When locations are disabled there
6165  // is no cost.
6166  //
6167  // Building the location accelerator may trigger an allocation,
6168  // but this can and should be avoided by reserving prior to
6169  // parsing:
6170  parser.reserve_locations(50u); // reserve for 50 lines
6171  // Now the structure will be built during parsing:
6172  ryml::Tree tree = parse_in_arena(&parser, "source.yml", yaml);
6173  // After this, we are ready to query the location from the parser:
6174  ryml::Location loc = tree.rootref().location(parser);
6175  // As for the complexity of the query: for large buffers it is
6176  // O(log(numlines)). For short source buffers (30 lines and less),
6177  // it is O(numlines), as a plain linear search is faster in this
6178  // case.
6179  CHECK(parser.location_contents(loc).begins_with("{"));
6180  CHECK(loc.offset == 0u);
6181  CHECK(loc.line == 0u);
6182  CHECK(loc.col == 0u);
6183  // on the next call, we only pay O(log(numlines)) because the
6184  // rebuild is already available:
6185  loc = tree["aa"].location(parser);
6186  CHECK(parser.location_contents(loc).begins_with("aa"));
6187  CHECK(loc.offset == 2u);
6188  CHECK(loc.line == 1u);
6189  CHECK(loc.col == 0u);
6190  // KEYSEQ in flow style: points at the key
6191  loc = tree["foo"].location(parser);
6192  CHECK(parser.location_contents(loc).begins_with("foo"));
6193  CHECK(loc.offset == 16u);
6194  CHECK(loc.line == 2u);
6195  CHECK(loc.col == 0u);
6196  loc = tree["foo"][0].location(parser);
6197  CHECK(parser.location_contents(loc).begins_with("one"));
6198  CHECK(loc.line == 2u);
6199  CHECK(loc.col == 6u);
6200  // SEQ in flow style: location points at the opening '[' (there's no key)
6201  loc = tree["foo"][1].location(parser);
6202  CHECK(parser.location_contents(loc).begins_with("["));
6203  CHECK(loc.line == 2u);
6204  CHECK(loc.col == 11u);
6205  loc = tree["foo"][1][0].location(parser);
6206  CHECK(parser.location_contents(loc).begins_with("two"));
6207  CHECK(loc.line == 2u);
6208  CHECK(loc.col == 12u);
6209  loc = tree["foo"][1][1].location(parser);
6210  CHECK(parser.location_contents(loc).begins_with("three"));
6211  CHECK(loc.line == 2u);
6212  CHECK(loc.col == 17u);
6213  // NOTE. The parser locations always point at the latest buffer to
6214  // be parsed with the parser object, so they must be queried using
6215  // the corresponding latest tree to be parsed. This means that if
6216  // the parser is reused, earlier trees will loose the possibility
6217  // of querying for location. It is undefined behavior to query the
6218  // parser for the location of a node from an earlier tree:
6219  ryml::Tree docval = parse_in_arena(&parser, "docval.yaml", "this is a docval");
6220  // From now on, none of the locations from the previous tree can
6221  // be queried:
6222  //loc = tree.rootref().location(parser); // ERROR, undefined behavior
6223  loc = docval.rootref().location(parser); // OK. this is the latest tree from this parser
6224  CHECK(parser.location_contents(loc).begins_with("this is a docval"));
6225  CHECK(loc.line == 0u);
6226  CHECK(loc.col == 0u);
6227 
6228  // NOTES ABOUT CONTAINER LOCATIONS
6229  ryml::Tree tree2 = parse_in_arena(&parser, "containers.yaml",
6230  "" "\n"
6231  "a new: buffer" "\n"
6232  "to: be parsed" "\n"
6233  "map with key:" "\n"
6234  " first: value" "\n"
6235  " second: value" "\n"
6236  "seq with key:" "\n"
6237  " - first value" "\n"
6238  " - second value" "\n"
6239  " -" "\n"
6240  " - nested first value" "\n"
6241  " - nested second value" "\n"
6242  " -" "\n"
6243  " nested first: value" "\n"
6244  " nested second: value" "\n"
6245  "");
6246  // (Likewise, the docval tree can no longer be used to query.)
6247  //
6248  // For key-less block-style maps, the location of the container
6249  // points at the first child's key. For example, in this case
6250  // the root does not have a key, so its location is taken
6251  // to be at the first child:
6252  loc = tree2.rootref().location(parser);
6253  CHECK(parser.location_contents(loc).begins_with("a new"));
6254  CHECK(loc.offset == 1u);
6255  CHECK(loc.line == 1u);
6256  CHECK(loc.col == 0u);
6257  // note the first child points exactly at the same place:
6258  loc = tree2["a new"].location(parser);
6259  CHECK(parser.location_contents(loc).begins_with("a new"));
6260  CHECK(loc.offset == 1u);
6261  CHECK(loc.line == 1u);
6262  CHECK(loc.col == 0u);
6263  loc = tree2["to"].location(parser);
6264  CHECK(parser.location_contents(loc).begins_with("to"));
6265  CHECK(loc.line == 2u);
6266  CHECK(loc.col == 0u);
6267  // but of course, if the block-style map is a KEYMAP, then the
6268  // location is the map's key, and not the first child's key:
6269  loc = tree2["map with key"].location(parser);
6270  CHECK(parser.location_contents(loc).begins_with("map with key"));
6271  CHECK(loc.line == 3u);
6272  CHECK(loc.col == 0u);
6273  loc = tree2["map with key"]["first"].location(parser);
6274  CHECK(parser.location_contents(loc).begins_with("first"));
6275  CHECK(loc.line == 4u);
6276  CHECK(loc.col == 2u);
6277  loc = tree2["map with key"]["second"].location(parser);
6278  CHECK(parser.location_contents(loc).begins_with("second"));
6279  CHECK(loc.line == 5u);
6280  CHECK(loc.col == 2u);
6281  // same thing for KEYSEQ:
6282  loc = tree2["seq with key"].location(parser);
6283  CHECK(parser.location_contents(loc).begins_with("seq with key"));
6284  CHECK(loc.line == 6u);
6285  CHECK(loc.col == 0u);
6286  loc = tree2["seq with key"][0].location(parser);
6287  CHECK(parser.location_contents(loc).begins_with("first value"));
6288  CHECK(loc.line == 7u);
6289  CHECK(loc.col == 4u);
6290  loc = tree2["seq with key"][1].location(parser);
6291  CHECK(parser.location_contents(loc).begins_with("second value"));
6292  CHECK(loc.line == 8u);
6293  CHECK(loc.col == 4u);
6294  // SEQ nested in SEQ: container location points at the first child's "- " dash
6295  loc = tree2["seq with key"][2].location(parser);
6296  CHECK(parser.location_contents(loc).begins_with("- nested first value"));
6297  CHECK(loc.line == 10u);
6298  CHECK(loc.col == 4u);
6299  loc = tree2["seq with key"][2][0].location(parser);
6300  CHECK(parser.location_contents(loc).begins_with("nested first value"));
6301  CHECK(loc.line == 10u);
6302  CHECK(loc.col == 6u);
6303  // MAP nested in SEQ: same as above: point to key
6304  loc = tree2["seq with key"][3].location(parser);
6305  CHECK(parser.location_contents(loc).begins_with("nested first: "));
6306  CHECK(loc.line == 13u);
6307  CHECK(loc.col == 4u);
6308  loc = tree2["seq with key"][3][0].location(parser);
6309  CHECK(parser.location_contents(loc).begins_with("nested first: "));
6310  CHECK(loc.line == 13u);
6311  CHECK(loc.col == 4u);
6312 }
6313 
6314 
6315 //-----------------------------------------------------------------------------
6316 //-----------------------------------------------------------------------------
6317 //-----------------------------------------------------------------------------
6318 
6319 
6320 /** @addtogroup doc_sample_helpers
6321  * @{ */
6322 
6323 namespace /*anon*/ {
6324 static int num_checks = 0;
6325 static int num_failed_checks = 0;
6326 static bool quiet_mode = false;
6327 } // namespace /*anon*/
6328 
6329 void handle_args(int argc, const char* argv[])
6330 {
6331  auto arg_matches = [](const char *arg, const char *shortform, const char *longform) {
6332  return (0 == strcmp(arg, shortform) || 0 == strcmp(arg, longform));
6333  };
6334  for(int i = 1; i < argc; ++i)
6335  if(arg_matches(argv[i], "-q", "--quiet"))
6336  quiet_mode = true;
6337 }
6338 
6339 bool report_check(int line, const char *predicate, bool result)
6340 {
6341  ++num_checks;
6342  const char *msg = predicate ? "OK! " : "OK!";
6343  if(!result)
6344  {
6345  ++num_failed_checks; // LCOV_EXCL_LINE
6346  msg = predicate ? "FAIL: " : "FAIL"; // LCOV_EXCL_LINE
6347  }
6348  if(!result || !quiet_mode)
6349  {
6350  std::cout << __FILE__ << ':' << line << ": " << msg << (predicate ? predicate : "") << std::endl;
6351  }
6352  return result;
6353 }
6354 
6355 
6357 {
6358  std::cout << "Completed " << num_checks << " checks." << std::endl;
6359  if(num_failed_checks)
6360  std::cout << "ERROR: " << num_failed_checks << '/' << num_checks << " checks failed." << std::endl;
6361  else
6362  std::cout << "SUCCESS!" << std::endl;
6363  return num_failed_checks;
6364 }
6365 
6366 
6367 //-----------------------------------------------------------------------------
6368 // methods to provide default callbacks (needed when
6369 // RYML_NO_DEFAULT_CALLBACKS is defined)
6370 
6371 namespace {
6372 // LCOV_EXCL_START
6373 /** dump (part of an) error message to terminal
6374  * @ingroup doc_sample_helpers */
6375 void errdump(ryml::csubstr s)
6376 {
6377  if(s.len)
6378  fwrite(s.str, 1, s.len, stderr); // NOLINT
6379 }
6380 /** finish printing an error message, and flush
6381  * @ingroup doc_sample_helpers */
6382 void errend()
6383 {
6384  fputc('\n', stderr); // NOLINT
6385  fflush(NULL); // NOLINT
6386 }
6387 // LCOV_EXCL_STOP
6388 } // namespace
6389 
6390 
6391 /** a bare-bones implementation of the callbacks
6392  * @ingroup doc_sample_helpers */
6394 {
6395  return ryml::Callbacks{}
6396  .set_allocate([](size_t len, void* , void *){
6397  return malloc(len); // NOLINT
6398  })
6399  .set_free([](void* mem, size_t, void *){
6400  free(mem); // NOLINT
6401  })
6402  //
6403  // The error callbacks won't be called in this quickstart,
6404  // because no errors are expected. But we implement them here
6405  // to show how a bare-bones implementation looks like.
6406  //
6407  // For a different (more involved) implementation of the error
6408  // callbacks, see the implementation of ErrorHandlerExample
6409  // below.
6410  //
6411  // LCOV_EXCL_START
6412  .set_error_basic([](ryml::csubstr msg, ryml::ErrorDataBasic const& errdata, void *){
6413  ryml::err_basic_format(errdump, msg, errdata); // format the message, printing to stderr
6414  errend(); // print newline and flush
6415  abort(); // abort (must never return: abort, or exception or setjmp)
6416  })
6417  .set_error_parse([](ryml::csubstr msg, ryml::ErrorDataParse const& errdata, void *){
6418  ryml::err_parse_format(errdump, msg, errdata); // format the message, printing to out
6419  errend(); // print newline and flush
6420  abort(); // abort (must never return: abort, or exception or setjmp)
6421  })
6422  .set_error_visit([](ryml::csubstr msg, ryml::ErrorDataVisit const& errdata, void *){
6423  ryml::err_visit_format(errdump, msg, errdata); // format the message, printing to out
6424  errend(); // print newline and flush
6425  abort(); // abort (must never return: abort, or exception or setjmp)
6426  });
6427  // LCOV_EXCL_STOP
6428 }
6429 
6430 /** set up default callbacks when ryml does not provide them
6431  * (ie when @ref RYML_NO_DEFAULT_CALLBACKS is defined)
6432  * @ingroup doc_sample_helpers */
6434 {
6435 #ifdef RYML_NO_DEFAULT_CALLBACKS
6437 #endif
6438 }
6439 
6440 
6441 //-----------------------------------------------------------------------------
6442 // methods for the example error handler
6443 
6444 #ifndef C4_EXCEPTIONS
6445 /*environment for setjmp*/
6446 static std::jmp_buf s_jmp_env;
6447 static std::string s_jmp_msg;
6448 #endif
6449 
6450 /** checking that an assertion occurs while calling fn. assertions are
6451  * enabled if @ref RYML_USE_ASSERT is defined.
6452  * @ingroup doc_sample_helpers */
6453 template<class Fn>
6455 {
6456  #if RYML_USE_ASSERT
6457  return check_error_occurs(std::forward<Fn>(fn));
6458  #else
6459  (void)fn; // do nothing otherwise, as there would be undefined behavior
6460  return true;
6461  #endif
6462 }
6463 /** checking that an error occurs while calling fn
6464  * @ingroup doc_sample_helpers */
6465 template<class Fn>
6467 {
6468  saved_msg_short.clear();
6469  saved_msg_full.clear();
6471  saved_basic_loc = {};
6472  saved_parse_loc = {};
6473  saved_visit_tree = {};
6475  bool got_error = false;
6476  #ifdef C4_EXCEPTIONS
6477  try
6478  {
6479  std::forward<Fn>(fn)();
6480  }
6481  catch(std::exception const&)
6482  {
6483  got_error = true;
6484  }
6485  #else
6486  if(setjmp(s_jmp_env) == 0)
6487  {
6488  std::forward<Fn>(fn)();
6489  }
6490  else
6491  {
6492  got_error = true;
6493  }
6494  #endif
6495  return got_error;
6496 }
6497 
6498 namespace {
6499 /** interrupt execution
6500  * @ingroup doc_sample_helpers */
6501 [[noreturn]] void stopexec(std::string const& s)
6502 {
6503  #ifdef C4_EXCEPTIONS
6504  throw std::runtime_error(s);
6505  #else
6506  s_jmp_msg = s;
6507  std::longjmp(s_jmp_env, 1); // jump to the corresponding call to setjmp().
6508  #endif
6509 }
6510 } // namespace
6511 /** this is where the callback implementation goes. Remember that it must not return.
6512  * @ingroup doc_sample_helpers
6513  * */
6514 [[noreturn]] void ErrorHandlerExample::on_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const& errdata)
6515 {
6516  saved_msg_short.assign(msg.str, msg.len);
6517  // build a full error message with location
6518  ryml::err_basic_format([this](ryml::csubstr s){
6519  saved_msg_full.append(s.str, s.len);
6520  }, msg, errdata);
6521  // Save the error params for subsequent testing in the quickstart.
6523  saved_basic_loc = errdata.location;
6524  stopexec(saved_msg_short);
6525 }
6526 /** this is where the callback implementation goes. Remember that it must not return.
6527  * @ingroup doc_sample_helpers
6528  * @see ryml::format_location_context
6529  * */
6530 [[noreturn]] void ErrorHandlerExample::on_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const& errdata)
6531 {
6532  saved_msg_short.assign(msg.str, msg.len);
6533  // build a full error message with location
6534  ryml::err_parse_format([this](ryml::csubstr s){
6535  saved_msg_full.append(s.str, s.len);
6536  }, msg, errdata);
6537  // Save the error params for subsequent testing in the quickstart.
6538  //
6539  // To add the source context, the source buffer is required. If
6540  // the caller is interested in enriching the full message with the
6541  // source buffer context, he can ensure that the source buffer is
6542  // kept, and then arrange the handler to access it.
6543  // For now, we assign the full message without context:
6545  saved_basic_loc = errdata.cpploc;
6546  saved_parse_loc = errdata.ymlloc;
6547  stopexec(saved_msg_full);
6548 }
6549 /** this is where the callback implementation goes. Remember that it must not return.
6550  * @ingroup doc_sample_helpers
6551  * */
6552 [[noreturn]] void ErrorHandlerExample::on_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const& errdata)
6553 {
6554  saved_msg_short.assign(msg.str, msg.len);
6555  // build a full error message with location
6556  ryml::err_visit_format([this](ryml::csubstr s){
6557  saved_msg_full.append(s.str, s.len);
6558  }, msg, errdata);
6559  // Save the error params for subsequent testing in the quickstart.
6560  //
6561  // To add the source context, the source buffer is required. If
6562  // the caller is interested in enriching the full message with the
6563  // source buffer context, he can ensure that the source buffer is
6564  // kept, and then arrange the handler to access it.
6565  // For now, we assign the full message without context:
6567  saved_basic_loc = errdata.cpploc;
6568  saved_visit_tree = errdata.tree;
6569  saved_visit_id = errdata.node;
6570  stopexec(saved_msg_full);
6571 }
6572 
6573 /** trampoline function to call the object's method */
6574 [[noreturn]] void ErrorHandlerExample::s_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const& errdata, void *this_)
6575 {
6576  static_cast<ErrorHandlerExample*>(this_)->on_error_basic(msg, errdata);
6577 }
6578 /** trampoline function to call the object's method */
6579 [[noreturn]] void ErrorHandlerExample::s_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const& errdata, void *this_)
6580 {
6581  static_cast<ErrorHandlerExample*>(this_)->on_error_parse(msg, errdata);
6582 }
6583 /** trampoline function to call the object's method */
6584 [[noreturn]] void ErrorHandlerExample::s_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const& errdata, void *this_)
6585 {
6586  static_cast<ErrorHandlerExample*>(this_)->on_error_visit(msg, errdata);
6587 }
6588 
6589 
6590 /** a helper to create the Callbacks object for the custom error handler
6591  * @ingroup doc_sample_helpers
6592  * */
6594 {
6596  copy.set_user_data(this)
6600  return copy;
6601 }
6602 
6603 /** test that this handler is currently set */
6605 {
6606  ryml::Callbacks const& current = ryml::get_callbacks();
6607  CHECK(current.m_error_basic == &s_error_basic);
6608  CHECK(current.m_error_parse == &s_error_parse);
6609  CHECK(current.m_error_visit == &s_error_visit);
6611  CHECK(current.m_free == original_callbacks.m_free);
6612 }
6613 /** test that this handler is currently not set */
6615 {
6616  ryml::Callbacks const& current = ryml::get_callbacks();
6617  CHECK(current.m_error_basic != &s_error_basic);
6618  CHECK(current.m_error_parse != &s_error_parse);
6619  CHECK(current.m_error_visit != &s_error_visit);
6621  CHECK(current.m_free == original_callbacks.m_free);
6622 }
6623 
6624 
6625 //-----------------------------------------------------------------------------
6626 // helper functions for sample_parse_file()
6627 
6628 C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4996) // fopen: this function may be unsafe
6629 C4_SUPPRESS_WARNING_CLANG_WITH_PUSH("-Wdeprecated-declarations") // fopen is deprecated
6630 /** load a file from disk into an existing CharContainer */
6631 template<class CharContainer>
6632 size_t file_get_contents(const char *filename, CharContainer *v)
6633 {
6634  std::FILE *fp = std::fopen(filename, "rb"); // NOLINT
6635  if(fp == nullptr) _RYML_ERR_BASIC("{}: could not open file", filename);
6636  std::fseek(fp, 0, SEEK_END); // NOLINT
6637  long sz = std::ftell(fp); // NOLINT
6638  v->resize(static_cast<typename CharContainer::size_type>(sz));
6639  if(sz)
6640  {
6641  std::rewind(fp); // NOLINT
6642  size_t ret = std::fread(&(*v)[0], 1, v->size(), fp);
6643  if(ret != (size_t)sz) _RYML_ERR_BASIC("{}: failed to read: expect {}B, got {}B", filename, sz, ret);
6644  }
6645  std::fclose(fp); // NOLINT
6646  return v->size();
6647 }
6648 
6649 /** load a file from disk and return a newly created CharContainer */
6650 template<class CharContainer>
6651 CharContainer file_get_contents(const char *filename)
6652 {
6653  CharContainer cc;
6654  file_get_contents(filename, &cc);
6655  return cc;
6656 }
6657 
6658 /** save a buffer into a file */
6659 template<class CharContainer>
6660 void file_put_contents(const char *filename, CharContainer const& v, const char* access)
6661 {
6662  file_put_contents(filename, v.empty() ? "" : &v[0], v.size(), access);
6663 }
6664 
6665 /** save a buffer into a file */
6666 void file_put_contents(const char *filename, const char *buf, size_t sz, const char* access)
6667 {
6668  std::FILE *fp = std::fopen(filename, access);
6669  if(fp == nullptr) _RYML_ERR_BASIC("{}: could not open file", filename);
6670  std::fwrite(buf, 1, sz, fp); // NOLINT
6671  std::fclose(fp); // NOLINT
6672 }
6673 C4_SUPPRESS_WARNING_CLANG_POP
6674 C4_SUPPRESS_WARNING_MSVC_POP
6675 /** @} */ // doc_sample_helpers
6676 
6677 /** @} */ // doc_quickstart
6678 
6679 C4_SUPPRESS_WARNING_GCC_CLANG_POP
Holds a pointer to an existing tree, and a node id.
Definition: node.hpp:856
Tree const * tree() const noexcept
Definition: node.hpp:927
id_type id() const noexcept
Definition: node.hpp:928
bool invalid() const noexcept
Definition: node.hpp:910
bool readable() const noexcept
because a ConstNodeRef cannot be used to write to the tree, readable() has the same meaning as !...
Definition: node.hpp:913
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition: node.hpp:996
void set_key_style(NodeType_e style)
Definition: node.hpp:1145
void set_style_conditionally(NodeType type_mask, NodeType rem_style_flags, NodeType add_style_flags, bool recurse=false)
Definition: node.hpp:1148
void clear_style(bool recurse=false)
Definition: node.hpp:1147
void set_val_style(NodeType_e style)
Definition: node.hpp:1146
id_type id() const noexcept
Definition: node.hpp:1121
bool invalid() const noexcept
true if the object is not referring to any existing or seed node.
Definition: node.hpp:1067
void set_container_style(NodeType_e style)
Definition: node.hpp:1144
bool readable() const noexcept
true if the object is not invalid and not in seed state.
Definition: node.hpp:1071
NodeRef append_child()
Definition: node.hpp:1414
size_t set_val_serialized(T const &v)
Definition: node.hpp:1263
bool is_seed() const noexcept
true if the object is not invalid and in seed state.
Definition: node.hpp:1069
void set_val_ref(csubstr val_ref)
Definition: node.hpp:1142
This is the main driver of parsing logic: it scans the YAML or JSON source for tokens,...
void reserve_stack(id_type capacity)
Reserve a certain capacity for the parsing stack.
csubstr location_contents(Location const &loc) const
Get the string starting at a particular location, to the end of the parsed source buffer.
ParserOptions const & options() const
Get the options used to build this parser object.
Callbacks const & callbacks() const
Get the current callbacks in the parser.
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 clear()
clear the tree and zero every node
Definition: tree.cpp:330
id_type first_child(id_type node) const
Definition: tree.hpp:510
bool is_stream(id_type node) const
Definition: tree.hpp:408
void set_val_ref(id_type node, csubstr ref)
Definition: tree.hpp:613
id_type root_id() const
Get the id of the root node. The tree must not be empty.
Definition: tree.hpp:335
NodeRef rootref()
Get the root as a NodeRef.
Definition: tree.cpp:56
void resolve_tags(TagCache &cache, bool all=true)
Resolve tags in the tree such as "!!str" -> "<tag:yaml.org,2002:str>", "!foo" -> "<!...
Definition: tree.cpp:1555
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:971
bool is_map(id_type node) const
Definition: tree.hpp:411
void reserve(id_type node_capacity=RYML_DEFAULT_TREE_CAPACITY)
Definition: tree.cpp:292
void clear_arena()
Definition: tree.hpp:280
bool is_root(id_type node) const
Definition: tree.hpp:460
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:959
bool in_arena(csubstr s) const
return true if the given substring is part of the tree's string arena
Definition: tree.hpp:887
Callbacks const & callbacks() const
Definition: tree.hpp:288
void clear_style(id_type node, bool recurse=false)
Definition: tree.cpp:1406
id_type next_sibling(id_type node) const
Definition: tree.hpp:505
ConstNodeRef crootref() const
Get the root as a ConstNodeRef.
Definition: tree.cpp:65
csubstr const & key(id_type node) const
Definition: tree.hpp:385
id_type id(NodeData const *n) const
get the index of a node belonging to this tree. n can be nullptr, in which case NONE is returned
Definition: tree.hpp:300
void set_style_conditionally(id_type node, NodeType type_mask, NodeType rem_style_flags, NodeType add_style_flags, bool recurse=false)
Definition: tree.cpp:1416
void normalize_tags()
Definition: tree.cpp:1573
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:909
void set_val_anchor(id_type node, csubstr anchor)
Definition: tree.hpp:611
NodeRef docref(id_type i)
get the i-th document of the stream
Definition: tree.cpp:104
bool is_doc(id_type node) const
Definition: tree.hpp:409
void set_container_style(id_type node, NodeType_e style)
Definition: tree.hpp:578
size_t arena_capacity() const
get the current capacity of the tree's internal arena
Definition: tree.hpp:876
id_type doc(id_type i) const
gets the i document node index.
Definition: tree.hpp:530
void set_val(id_type node, csubstr val)
Definition: tree.hpp:605
void normalize_tags_long()
Definition: tree.cpp:1580
csubstr const & val(id_type node) const
Definition: tree.hpp:391
void resolve(ReferenceResolver *rr, bool clear_anchors=true)
Resolve references (aliases <- anchors), by forwarding to ReferenceResolver::resolve(); refer to Refe...
Definition: tree.cpp:1202
bool is_seq(id_type node) const
Definition: tree.hpp:412
id_type find_child(id_type node, csubstr const &key) const
Definition: tree.cpp:1257
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:1914
void add_tag_directive(csubstr handle, csubstr prefix, id_type id)
Definition: tree.cpp:1446
id_type first_sibling(id_type node) const
Definition: tree.hpp:521
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:930
id_type num_children(id_type node) const
O(num_children)
Definition: tree.cpp:1212
csubstr arena() const
get the current arena
Definition: tree.hpp:882
id_type size() const
Definition: tree.hpp:284
id_type child(id_type node, id_type pos) const
Definition: tree.cpp:1220
void set_key_anchor(id_type node, csubstr anchor)
Definition: tree.hpp:610
Definitions of error utilities used by ryml.
provides type-safe facilities for formatting arguments to string buffers
left_< T > left(T val, size_t width, char padchar=' ')
tag type to mark an argument to be aligned left.
Definition: format.hpp:519
right_< T > right(T val, size_t width, char padchar=' ')
tag function to mark an argument to be aligned right
Definition: format.hpp:557
const_base64_wrapper base64(Args const &...args)
mark a variable to be written in base64 format
Definition: base64.hpp:96
size_t base64_decode(csubstr encoded, blob data)
decode the base64 encoding in the given buffer
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
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
Callbacks const & get_callbacks()
get the global callbacks
Definition: common.cpp:94
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:1113
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:653
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:1044
substr cat_sub(substr buf, Args const &...args)
like c4::cat() but return a substr instead of a size
Definition: format.hpp:663
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:1224
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:1156
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:778
substr catsep_sub(substr buf, Args &&...args)
like c4::catsep() but return a substr instead of a size
Definition: format.hpp:790
@ FTOA_FLEX
print the real number in flexible format (like g)
Definition: charconv.hpp:198
@ FTOA_SCIENT
print the real number in scientific format (like e)
Definition: charconv.hpp:196
@ FTOA_FLOAT
print the real number in floating point format (like f)
Definition: charconv.hpp:194
@ FTOA_HEXA
print the real number in hexadecimal format (like a)
Definition: charconv.hpp:200
bool indent_flow_ml() const noexcept
Definition: emit.hpp:88
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...
Definition: emit.hpp:765
size_t emit_yaml(Tree const &t, id_type id, EmitOptions const &opts, FILE *f)
(1) emit YAML to the given file, starting at the given node.
Definition: emit.hpp:416
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
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
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
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
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:1334
substr format_sub(substr buf, csubstr fmt, Args const &...args)
like c4::format() but return a substr instead of a size
Definition: format.hpp:960
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:940
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:1267
integral_< intptr_t > hex(std::nullptr_t)
format null as an hexadecimal value
Definition: format.hpp:160
integral_< intptr_t > bin(std::nullptr_t)
format null as a binary 0-1 value
Definition: format.hpp:212
integral_< intptr_t > oct(std::nullptr_t)
format null as an octal value
Definition: format.hpp:185
bool scalar_is_null(csubstr s) noexcept
YAML-sense query of nullity.
Definition: node_type.cpp:289
@ KEY_DQUO
mark key scalar as double quoted "
Definition: node_type.hpp:69
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition: node_type.hpp:39
@ KEY
is member of a map
Definition: node_type.hpp:37
@ VAL_STYLE
mask of all the scalar styles for val (not container styles!)
Definition: node_type.hpp:93
@ FLOW_SL
mark container with single-line flow style (seqs as '[val1,val2], maps as '{key: val,...
Definition: node_type.hpp:60
@ FLOW_ML
mark container with multi-line flow style (seqs as '[ val1, val2 ], maps as '{ key: val,...
Definition: node_type.hpp:61
@ VAL
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:38
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
Definition: node_type.hpp:40
@ VAL_SQUO
mark val scalar as single quoted '
Definition: node_type.hpp:68
@ KEY_STYLE
mask of all the scalar styles for key (not container styles!)
Definition: node_type.hpp:92
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
Definition: node_type.hpp:72
@ BLOCK
mark container with block style (seqs as '- val ', maps as 'key: val')
Definition: node_type.hpp:62
@ VAL_DQUO
mark val scalar as double quoted "
Definition: node_type.hpp:70
@ CONTAINER_STYLE
Definition: node_type.hpp:97
@ KEY_SQUO
mark key scalar as single quoted '
Definition: node_type.hpp:67
@ VAL_LITERAL
mark val scalar as multiline, block literal |
Definition: node_type.hpp:64
@ KEY_FOLDED
mark key scalar as multiline, block folded >
Definition: node_type.hpp:65
void parse_in_arena(Parser *parser, csubstr filename, csubstr yaml, Tree *t, 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:92
void parse_json_in_arena(Parser *parser, csubstr filename, csubstr json, Tree *t, 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:113
void parse_in_place(Parser *parser, csubstr filename, substr yaml, Tree *t, id_type node_id)
(1) parse YAML into an existing tree node.
Definition: parse.cpp:38
void sample_anchors_and_aliases()
demonstrates usage with anchors and alias references.
void sample_emit_to_file()
demonstrates how to emit to a FILE*
void sample_formatting()
ryml provides facilities for formatting/deformatting (imported from c4core into the ryml namespace).
void sample_quick_overview()
a brief tour over most features
Definition: quickstart.cpp:394
void sample_style_flow_ml_indent()
[experimental] control the indentation of emitted FLOW_ML containers
void sample_per_tree_allocator()
void sample_lightning_overview()
a lightning tour over most features see sample_quick_overview
Definition: quickstart.cpp:340
void sample_user_container_types()
shows how to serialize/deserialize container types.
void sample_empty_null_values()
Shows how to deal with empty/null values.
void sample_global_allocator()
demonstrates how to set the global allocator for ryml
void sample_base64()
demonstrates how to read and write base64-encoded blobs.
void sample_error_visit()
Visit errors happen when an error is triggered while reading from a node.
void sample_static_trees()
shows how to work around the static initialization order fiasco when using a static-duration ryml tre...
void sample_parse_file()
demonstrate how to load a YAML file from disk to parse with ryml.
void sample_style_flow_ml_filter()
[experimental] set the parser to pick FLOW_SL even if the container being parsed is FLOW_ML
void sample_json()
shows how to parse and emit JSON.
void sample_error_parse()
void sample_parse_in_arena()
demonstrate parsing of a read-only YAML source buffer
void sample_emit_nested_node()
just like parsing into a nested node, you can also emit from a nested node.
void sample_style()
[experimental] query/set/modify node style to control formatting of emitted YAML code.
void sample_location_tracking()
demonstrates how to obtain the (zero-based) location of a node from a recently parsed tree
void sample_parse_reuse_parser()
Demonstrates reuse of an existing parser.
void sample_iterate_trees()
shows how to programatically iterate through trees
void sample_docs()
void sample_create_trees()
shows how to programatically create trees
void sample_parse_reuse_tree_and_parser()
for ultimate speed when parsing multiple times, reuse both the tree and parser
void sample_emit_to_container()
demonstrates how to emit to a linear container of char
void sample_parse_in_place()
demonstrate in-place parsing of a mutable YAML source buffer.
void sample_fundamental_types()
ryml provides facilities for serializing and deserializing the C++ fundamental types,...
void sample_std_types()
demonstrates usage with the std implementations provided by ryml in the ryml_std.hpp header
void sample_float_precision()
control precision of serialized floats
void sample_anchors_and_aliases_create()
demonstrates how to use the API to programatically create anchors and aliases
void sample_user_scalar_types()
to add scalar types (ie leaf types converting to/from string), define the functions above for those t...
void sample_error_visit_location()
It is possible to obtain the YAML location from a visit error: when the tree is obtained from parsing...
void sample_error_basic()
void sample_tag_directives()
void sample_tags()
void sample_error_handler()
demonstrates how to set a custom error handler for ryml
void sample_emit_to_stream()
demonstrates how to emit to a stream-like structure
void sample_tree_arena()
demonstrates explicit and implicit interaction with the tree's string arena.
void sample_parse_reuse_tree()
demonstrate reuse/modification of tree when parsing
void sample_substr()
demonstrate usage of ryml::substr and ryml::csubstr
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:422
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:416
real_< T > real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT)
Definition: format.hpp:366
void write(ryml::NodeRef *n, my_seq_type< T > const &seq)
bool read(ryml::ConstNodeRef const &n, my_seq_type< T > *seq)
bool from_chars(ryml::csubstr buf, vec2< T > *v)
static void s_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const &errdata, void *this_)
trampoline function to call the object's method
bool report_check(int line, const char *predicate, bool result)
static void s_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const &errdata, void *this_)
trampoline function to call the object's method
bool check_assertion_occurs(Fn &&fn)
checking that an assertion occurs while calling fn.
void on_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const &errdata)
this is where the callback implementation goes.
static std::string s_jmp_msg
int report_checks()
void file_put_contents(const char *filename, CharContainer const &v, const char *access="wb")
save a buffer into a file
void ensure_callbacks()
set up default callbacks when ryml does not provide them (ie when RYML_NO_DEFAULT_CALLBACKS is define...
static void s_error_visit(ryml::csubstr msg, ryml::ErrorDataVisit const &errdata, void *this_)
trampoline function to call the object's method
void on_error_parse(ryml::csubstr msg, ryml::ErrorDataParse const &errdata)
this is where the callback implementation goes.
bool check_error_occurs(Fn &&fn)
checking that an error occurs while calling fn
ryml::Callbacks default_callbacks()
a bare-bones implementation of the callbacks
void check_disabled() const
test that this handler is currently not set
CharContainer file_get_contents(const char *filename)
load a file from disk and return a newly created CharContainer
#define CHECK(predicate)
a quick'n'dirty assertion to verify a predicate
Definition: quickstart.cpp:309
ryml::Callbacks callbacks()
a helper to create the Callbacks object for the custom error handler
void on_error_basic(ryml::csubstr msg, ryml::ErrorDataBasic const &errdata)
this is where the callback implementation goes.
static std::jmp_buf s_jmp_env
void check_enabled() const
test that this handler is currently set
void handle_args(int argc, const char *argv[])
size_t to_chars(ryml::substr buf, vec2< T > v)
Key< K > key(K &k)
Definition: node.hpp:46
csubstr to_csubstr(substr s) noexcept
neutral version for use in generic code
Definition: substr.hpp:2204
substr to_substr(substr s) noexcept
neutral version for use in generic code
Definition: substr.hpp:2202
csubstr from_tag_long(YamlTag_e tag)
Definition: tag.cpp:130
csubstr normalize_tag_long(csubstr tag)
Definition: tag.cpp:31
csubstr normalize_tag(csubstr tag)
Definition: tag.cpp:19
csubstr from_tag(YamlTag_e tag)
Definition: tag.cpp:170
YamlTag_e to_tag(csubstr tag)
Definition: tag.cpp:68
@ TAG_SET
!!set Unordered set of non-equal values.
Definition: tag.hpp:39
@ TAG_INT
!!float Mathematical integers.
Definition: tag.hpp:45
@ TAG_SEQ
!!seq Sequence of arbitrary values.
Definition: tag.hpp:40
@ TAG_NONE
Definition: tag.hpp:34
@ TAG_STR
!!str A sequence of zero or more Unicode characters.
Definition: tag.hpp:48
@ TAG_MAP
!!map Unordered set of key: value pairs without duplicates.
Definition: tag.hpp:36
size_t uncat(csubstr buf, Arg &a, Args &...more)
deserialize the arguments from the given buffer.
Definition: format.hpp:694
size_t uncatsep(csubstr buf, csubstr sep, Arg &a, Args &...more)
deserialize the arguments from the given buffer, using a separator.
Definition: format.hpp:823
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:991
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
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:244
@ npos
a null string position
Definition: common.hpp:258
@ NONE
an index to none
Definition: common.hpp:251
Definition: ryml.hpp:6
int main(int, const char *[])
an example error handler, required for some of the quickstart examples.
Definition: quickstart.cpp:241
ryml::Location saved_basic_loc
Definition: quickstart.cpp:268
ryml::Callbacks original_callbacks
Definition: quickstart.cpp:243
std::string saved_msg_full
Definition: quickstart.cpp:266
ryml::id_type saved_visit_id
Definition: quickstart.cpp:271
std::string saved_msg_short
Definition: quickstart.cpp:265
ryml::Tree const * saved_visit_tree
Definition: quickstart.cpp:270
ryml::Location saved_parse_loc
Definition: quickstart.cpp:269
std::string saved_msg_full_with_context
Definition: quickstart.cpp:267
void free(void *mem, size_t len)
void * allocate(size_t len)
std::vector< char > memory_pool
static void s_free(void *mem, size_t len, void *this_)
ryml::Callbacks callbacks()
static void * s_allocate(size_t len, void *, void *this_)
an example for a per-tree memory allocator
std::vector< char > memory_pool
ryml::Callbacks callbacks() const
void free(void *mem, size_t len)
void * allocate(size_t len)
Shows how to create a scoped error handler.
Definition: quickstart.cpp:278
A c-style callbacks class to customize behavior on errors or allocation.
Definition: common.hpp:541
pfn_error_basic m_error_basic
a pointer to a basic error handler function
Definition: common.hpp:545
pfn_error_parse m_error_parse
a pointer to a parse error handler function
Definition: common.hpp:546
Callbacks & set_error_visit(pfn_error_visit error_visit=nullptr)
Set or reset the error_visit callback.
Definition: common.cpp:186
Callbacks & set_free(pfn_free free=nullptr)
Set or reset the free callback.
Definition: common.cpp:159
void * m_user_data
data to be forwarded in every call to a callback
Definition: common.hpp:542
pfn_allocate m_allocate
a pointer to an allocate handler function
Definition: common.hpp:543
Callbacks & set_error_parse(pfn_error_parse error_parse=nullptr)
Set or reset the error_parse callback.
Definition: common.cpp:177
Callbacks & set_allocate(pfn_allocate allocate=nullptr)
Set or reset the allocate callback.
Definition: common.cpp:150
Callbacks & set_error_basic(pfn_error_basic error_basic=nullptr)
Set or reset the error_basic callback.
Definition: common.cpp:168
pfn_error_visit m_error_visit
a pointer to a visit error handler function
Definition: common.hpp:547
pfn_free m_free
a pointer to a free handler function
Definition: common.hpp:544
Callbacks & set_user_data(void *user_data)
Set the user data.
Definition: common.cpp:144
A lightweight object containing options to be used when emitting.
Definition: emit.hpp:60
Data for a basic error.
Definition: common.hpp:315
Location location
location where the error was detected (may be from YAML or C++ source code)
Definition: common.hpp:316
Data for a parse error.
Definition: common.hpp:324
Location cpploc
location in the C++ source file where the error was detected.
Definition: common.hpp:325
Location ymlloc
location in the YAML source buffer where the error was detected.
Definition: common.hpp:326
Data for a visit error.
Definition: common.hpp:334
Location cpploc
location in the C++ source file where the error was detected.
Definition: common.hpp:335
Tree const * tree
tree where the error was detected
Definition: common.hpp:336
id_type node
node where the error was detected
Definition: common.hpp:337
The event handler to create a ryml Tree.
Exception thrown by the default basic error implementation.
Definition: error.hpp:478
ErrorDataBasic errdata_basic
error data
Definition: error.hpp:481
const char * what() const noexcept override
Definition: error.hpp:480
Exception thrown by the default parse error implementation.
Definition: error.hpp:497
ErrorDataParse errdata_parse
Definition: error.hpp:499
Exception thrown by the default visit error implementation.
Definition: error.hpp:514
ErrorDataVisit errdata_visit
Definition: error.hpp:516
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition: common.hpp:284
size_t col
column
Definition: common.hpp:287
size_t line
line
Definition: common.hpp:286
size_t offset
number of bytes from the beginning of the source buffer
Definition: common.hpp:285
csubstr name
name of the file
Definition: common.hpp:288
Options to give to the parser to control its behavior.
Definition: common.hpp:350
ParserOptions & resolve_tags(bool enabled) noexcept
enable/disable resolution of YAML tags during parsing.
Definition: common.hpp:404
ParserOptions & locations(bool enabled) noexcept
enable/disable source location tracking
Definition: common.hpp:429
ParserOptions & detect_flow_ml(bool enabled) noexcept
enable/disable detection of FLOW_ML container style.
Definition: common.hpp:386
Accelerator structure to reduce memory requirements by enabling reuse of resolved tags.
Definition: tag.hpp:71
mark a tree or node to be emitted as yaml when using operator<<, with options.
Definition: emit.hpp:552
auto first_child() RYML_NOEXCEPT -> Impl
Forward to Tree::first_child().
Definition: node.hpp:344
auto children() RYML_NOEXCEPT -> children_view
get an iterable view over children.
Definition: node.hpp:767
bool is_val() const RYML_NOEXCEPT
Forward to Tree::is_val().
Definition: node.hpp:245
bool is_root() const RYML_NOEXCEPT
Forward to Tree::is_root().
Definition: node.hpp:310
auto last_child() RYML_NOEXCEPT -> Impl
Forward to Tree::last_child().
Definition: node.hpp:348
bool is_stream() const RYML_NOEXCEPT
Forward to Tree::is_stream().
Definition: node.hpp:238
auto parent() RYML_NOEXCEPT -> Impl
Forward to Tree::parent().
Definition: node.hpp:340
bool has_child(ConstImpl const &n) const RYML_NOEXCEPT
Forward to Tree::has_child().
Definition: node.hpp:314
auto next_sibling() RYML_NOEXCEPT -> Impl
Forward to Tree::next_sibling().
Definition: node.hpp:364
auto last_sibling() RYML_NOEXCEPT -> Impl
Forward to Tree::last_sibling().
Definition: node.hpp:372
bool is_map() const RYML_NOEXCEPT
Forward to Tree::is_map().
Definition: node.hpp:241
id_type num_siblings() const RYML_NOEXCEPT
O(num_children).
Definition: node.hpp:388
bool has_key() const RYML_NOEXCEPT
Forward to Tree::has_key().
Definition: node.hpp:244
csubstr key() const RYML_NOEXCEPT
Forward to Tree::key().
Definition: node.hpp:211
auto first_sibling() RYML_NOEXCEPT -> Impl
Forward to Tree::first_sibling().
Definition: node.hpp:368
csubstr val() const RYML_NOEXCEPT
Forward to Tree::val().
Definition: node.hpp:216
bool is_doc() const RYML_NOEXCEPT
Forward to Tree::is_doc().
Definition: node.hpp:239
id_type num_children() const RYML_NOEXCEPT
O(num_children).
Definition: node.hpp:387
auto child(id_type pos) RYML_NOEXCEPT -> Impl
Forward to Tree::child().
Definition: node.hpp:352
bool is_block() const RYML_NOEXCEPT
Forward to Tree::is_block().
Definition: node.hpp:280
Location location(Parser const &parser) const
Definition: node.hpp:627
bool is_seq() const RYML_NOEXCEPT
Forward to Tree::is_seq().
Definition: node.hpp:242
bool has_val() const RYML_NOEXCEPT
Forward to Tree::has_val().
Definition: node.hpp:243
auto prev_sibling() RYML_NOEXCEPT -> Impl
Forward to Tree::prev_sibling().
Definition: node.hpp:360
example scalar type, serialized only
example scalar type, serialized only
example scalar type, serialized only
example user container type: map-like
std::map< K, V > map_member
example user container type: seq-like
std::vector< T > seq_member
example user container type with nested container members.
vec2< int > v2
my_map_type< int, int > map
vec3< int > v3
vec4< int > v4
my_seq_type< int > seq
example scalar type, deserialized only
example scalar type, deserialized only
example scalar type, deserialized only
example scalar type, serialized and deserialized
example scalar type, serialized and deserialized
example scalar type, serialized and deserialized