rapidyaml  0.16.0
parseandemitYAML,anddoitfast
Overview

Functions

void sample_lightning_overview ()
 lightning overview of most common features More...
 
void sample_quick_overview ()
 quick overview of most common features More...
 

Detailed Description

Function Documentation

void sample_lightning_overview ( )

lightning overview of most common features

a lightning tour over most features see sample_quick_overview

Definition at line 409 of file quickstart.cpp.

Referenced by main().

410 {
411  // Parse YAML code in place, potentially mutating the buffer:
412  char yml_buf[] = "{foo: 1, bar: [2, 3], john: doe}";
413  ryml::Tree tree = ryml::parse_in_place(yml_buf);
414 
415  // read from the tree:
416  ryml::NodeRef bar = tree["bar"];
417  CHECK(bar[0].val() == "2");
418  CHECK(bar[1].val() == "3");
419  CHECK(bar[0].val().str == yml_buf + 15); // points at the source buffer
420  CHECK(bar[1].val().str == yml_buf + 18);
421 
422  // deserializing:
423  int bar0 = 0, bar1 = 0;
424  bar[0].load(&bar0); // also checks the node is readable, and conversion succeeded
425  bar[1].load(&bar1); // see also .deserialize()
426  CHECK(bar0 == 2);
427  CHECK(bar1 == 3);
428 
429  // serializing:
430  bar[0].save(10); // creates a string in the tree's arena
431  bar[1].save(11); // see also .set_serialized()
432  CHECK(bar[0].val() == "10");
433  CHECK(bar[1].val() == "11");
434 
435  // add nodes
436  tree["new"].set_val("node");
437  bar.append_child().save(12);
438  CHECK(bar[2].val() == "12");
439 
440  // emit tree
441  std::string expected = "{foo: 1,bar: [10,11,12],john: doe,new: node}";
442  // emit tree to std::string
443  CHECK(ryml::emitrs_yaml<std::string>(tree) == expected);
444  // emit tree to FILE*
445  ryml::emit_yaml(tree, stdout); printf("\n");
446  // emit tree to ostream
447  std::cout << tree << "\n";
448 
449  // emit node
450  ryml::ConstNodeRef foo = tree["foo"];
451  expected = "foo: 1\n";
452  // emit node to std::string
453  CHECK(ryml::emitrs_yaml<std::string>(foo) == expected);
454  // emit node to FILE*
455  ryml::emit_yaml(foo, stdout);
456  // emit node to ostream
457  std::cout << foo;
458 }
void set_val(id_type node, csubstr val) RYML_NOEXCEPT
Definition: tree.hpp:688
const char * str
Definition: node_type.cpp:70
void load(T *v, bool check_readable=true) const
(1) deserialize the node's contents (val or container) to the given variable, forwarding to the user-...
Definition: node.hpp:345
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition: node.hpp:1062
void save(T const &k)
Definition: node.hpp:1337
#define CHECK(predicate)
a testing assertion, used only in this quickstart
Definition: quickstart.cpp:310
Holds a pointer to an existing tree, and a node id.
Definition: node.hpp:736
substr emit_yaml(Tree const &t, EmitOptions const &opts, substr buf, bool error_on_excess)
(1) emit YAML to the given buffer.
Definition: emit_buf.cpp:28
NodeRef append_child()
Definition: node.hpp:1715
void parse_in_place(Parser *parser, csubstr filename, substr yaml, Tree *tree, id_type node_id)
(1) parse YAML into an existing tree node.
Definition: parse.cpp:165
void sample_quick_overview ( )

quick overview of most common features

a brief tour over most features

Definition at line 464 of file quickstart.cpp.

Referenced by main().

465 {
466  // Parse YAML code in place, potentially mutating the buffer:
467  char yml_buf[] = ""
468  "foo: 1" "\n"
469  "bar: [2, 3]" "\n"
470  "john: doe" "\n"
471  "";
472  ryml::Tree tree = ryml::parse_in_place(yml_buf);
473 
474  // The resulting tree contains only views to the parsed string. If
475  // the string was parsed in place, then the string must outlive
476  // the tree! This works here because both `yml_buf` and `tree`
477  // live on the same scope, so have the same lifetime.
478 
479  // It is also possible to:
480  //
481  // - parse a read-only buffer using parse_in_arena(). This
482  // copies the YAML buffer to the tree's arena, and spares the
483  // headache of the string's lifetime.
484  //
485  // - reuse an existing tree (advised)
486  //
487  // - reuse an existing parser (advised)
488  //
489  // - parse into an existing node deep in a tree
490  //
491  // Note: it will always be significantly faster to parse in place
492  // and reuse tree+parser.
493  //
494  // Below you will find samples that show how to achieve reuse; but
495  // please note that for brevity and clarity, many of the examples
496  // here are parsing in the arena, and not reusing tree or parser.
497 
498 
499  //------------------------------------------------------------------
500  // API overview
501 
502  // The ryml tree has a two-level API:
503  //
504  // The lower level index API is based on the indices of nodes,
505  // where the node's id is the node's position in the tree's data
506  // array. This API is very efficient, but somewhat difficult to use:
507  ryml::id_type root_id = tree.root_id();
508  ryml::id_type bar_id = tree.find_child(root_id, "bar"); // need to get the index right
509  CHECK(tree.is_map(root_id)); // all of the index methods are in the tree
510  CHECK(tree.is_seq(bar_id)); // ... and receive the subject index
511 
512  // The node API is a lightweight abstraction sitting on top of the
513  // index API, but offering a much more convenient interaction:
514  ryml::ConstNodeRef root = tree.rootref(); // a const node reference
515  ryml::ConstNodeRef bar = tree["bar"];
516  CHECK(root.is_map());
517  CHECK(bar.is_seq());
518 
519  // A node ref is a lightweight handle to the tree and associated id:
520  CHECK(root.tree() == &tree); // a node ref points at its tree, WITHOUT refcount
521  CHECK(root.id() == root_id); // a node ref's id is the index of the node
522  CHECK(bar.id() == bar_id); // a node ref's id is the index of the node
523 
524  // The node API translates very cleanly to the index API, so most
525  // of the code examples below are using the node API.
526 
527  // WARNING. A node ref holds a raw pointer to the tree. Care must
528  // be taken to ensure the lifetimes match, so that a node will
529  // never access the tree after the tree goes out of scope.
530 
531 
532  //------------------------------------------------------------------
533  // To read the parsed tree
534 
535  // ConstNodeRef::operator[] does a lookup, is O(num_children[node]).
536  CHECK(tree["foo"].is_keyval());
537  CHECK(tree["foo"].val() == "1"); // get the val of a node (must be leaf node, otherwise it is a container and has no val)
538  CHECK(tree["foo"].key() == "foo"); // get the key of a node (must be child of a map, otherwise it has no key)
539  CHECK(tree["bar"].is_seq());
540  CHECK(tree["bar"].has_key());
541  CHECK(tree["bar"].key() == "bar");
542  // maps use string keys, seqs use index keys:
543  CHECK(tree["bar"][0].val() == "2");
544  CHECK(tree["bar"][1].val() == "3");
545  CHECK(tree["john"].val() == "doe");
546  // An index key is the position of the child within its parent,
547  // so even maps can also use int keys, if the key position is
548  // known.
549  CHECK(tree[0].id() == tree["foo"].id());
550  CHECK(tree[1].id() == tree["bar"].id());
551  CHECK(tree[2].id() == tree["john"].id());
552  // Tree::operator[](int) searches a ***root*** child by its position.
553  CHECK(tree[0].id() == tree["foo"].id()); // 0: first child of root
554  CHECK(tree[1].id() == tree["bar"].id()); // 1: second child of root
555  CHECK(tree[2].id() == tree["john"].id()); // 2: third child of root
556  // NodeRef::operator[](int) searches a ***node*** child by its position:
557  CHECK(bar[0].val() == "2"); // 0 means first child of bar
558  CHECK(bar[1].val() == "3"); // 1 means second child of bar
559  // NodeRef::operator[](string):
560  // A string key is the key of the node: lookup is by name. So it
561  // is only available for maps, and it is NOT available for seqs,
562  // since seq members do not have keys.
563  CHECK(tree["foo"].key() == "foo");
564  CHECK(tree["bar"].key() == "bar");
565  CHECK(tree["john"].key() == "john");
566  CHECK(bar.is_seq());
567  // CHECK(bar["BOOM!"].is_seed()); // error, seqs do not have key lookup
568 
569  // Note that maps can also use index keys as well as string keys:
570  CHECK(root["foo"].id() == root[0].id());
571  CHECK(root["bar"].id() == root[1].id());
572  CHECK(root["john"].id() == root[2].id());
573 
574  // IMPORTANT. The ryml tree uses an index-based linked list for
575  // storing children, so the complexity of
576  // `Tree::operator[csubstr]` and `Tree::operator[id_type]` is O(n),
577  // linear on the number of root children. If you use
578  // `Tree::operator[]` with a large tree where the root has many
579  // children, you will see a performance hit.
580  //
581  // To avoid this hit, you can create your own accelerator
582  // structure. For example, before doing a lookup, do a single
583  // traverse at the root level to fill an `map<csubstr,id_type>`
584  // mapping key names to node indices; with a node index, a lookup
585  // (via `Tree::get()`) is O(1), so this way you can get O(log n)
586  // lookup from a key. (But please do not use `std::map` if you
587  // care about performance; use something else like a flat map or
588  // sorted vector).
589  //
590  // As for node refs, the difference from `NodeRef::operator[]` and
591  // `ConstNodeRef::operator[]` to `Tree::operator[]` is that the
592  // latter refers to the root node, whereas the former are invoked
593  // on their target node. But the lookup process works the same for
594  // both and their algorithmic complexity is the same: they are
595  // both linear in the number of direct children. But of course,
596  // depending on the data, that number may be very different from
597  // one to another.
598 
599 
600  //------------------------------------------------------------------
601  // Hierarchy:
602 
603  {
604  ryml::ConstNodeRef foo = root.first_child();
605  ryml::ConstNodeRef john = root.last_child();
606  CHECK(tree.size() == 6); // O(1) number of nodes in the tree
607  CHECK(root.num_children() == 3); // O(num_children[root])
608  CHECK(foo.num_siblings() == 3); // O(num_children[parent(foo)])
609  CHECK(foo.parent().id() == root.id()); // parent() is O(1)
610  CHECK(root.first_child().id() == root["foo"].id()); // first_child() is O(1)
611  CHECK(root.last_child().id() == root["john"].id()); // last_child() is O(1)
612  CHECK(john.first_sibling().id() == foo.id());
613  CHECK(foo.last_sibling().id() == john.id());
614  // prev_sibling(), next_sibling(): (both are O(1))
615  CHECK(foo.num_siblings() == root.num_children());
616  CHECK(foo.prev_sibling().id() == ryml::NONE); // foo is the first_child()
617  CHECK(foo.next_sibling().key() == "bar");
618  CHECK(foo.next_sibling().next_sibling().key() == "john");
619  CHECK(foo.next_sibling().next_sibling().next_sibling().id() == ryml::NONE); // john is the last_child()
620  }
621 
622 
623  //------------------------------------------------------------------
624  // Iterating:
625  {
626  ryml::csubstr expected_keys[] = {"foo", "bar", "john"};
627  // iterate children using the high-level node API:
628  {
629  ryml::id_type count = 0;
630  for(ryml::ConstNodeRef const& child : root.children())
631  CHECK(child.key() == expected_keys[count++]);
632  }
633  // iterate siblings using the high-level node API:
634  {
635  ryml::id_type count = 0;
636  for(ryml::ConstNodeRef const& child : root["foo"].siblings())
637  CHECK(child.key() == expected_keys[count++]);
638  }
639  // iterate children using the lower-level tree index API:
640  {
641  ryml::id_type count = 0;
642  for(ryml::id_type child_id = tree.first_child(root_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
643  CHECK(tree.key(child_id) == expected_keys[count++]);
644  }
645  // iterate siblings using the lower-level tree index API:
646  // (notice the only difference from above is in the loop
647  // preamble, which calls tree.first_sibling(bar_id) instead of
648  // tree.first_child(root_id))
649  {
650  ryml::id_type count = 0;
651  for(ryml::id_type child_id = tree.first_sibling(bar_id); child_id != ryml::NONE; child_id = tree.next_sibling(child_id))
652  CHECK(tree.key(child_id) == expected_keys[count++]);
653  }
654  }
655 
656 
657  //------------------------------------------------------------------
658  // Gotchas:
659 
660  // ryml uses assertions to prevent you from trying to obtain
661  // things that do not exist. For example:
662 
663  {
664  ryml::ConstNodeRef seq_node = tree["bar"];
665  ryml::ConstNodeRef val_node = seq_node[0];
666 
667  CHECK(seq_node.is_seq()); // seq is a container
668  CHECK(!seq_node.has_val()); // ... so it has no val
669  //CHECK(seq_node.val() == BOOM!); // ... so attempting to get a val is undefined behavior
670 
671  CHECK(val_node.parent() == seq_node); // belongs to a seq
672  CHECK(!val_node.has_key()); // ... so it has no key
673  //CHECK(val_node.key() == BOOM!); // ... so attempting to get a key is undefined behavior
674 
675  CHECK(val_node.is_val()); // this node is a val
676  //CHECK(val_node.first_child() == BOOM!); // ... so attempting to get a child is undefined behavior
677 
678  // assertions are also present in methods that /may/ read the val:
679  CHECK(seq_node.is_seq()); // seq is a container
680  //CHECK(seq_node.val_is_null() BOOM!); // so cannot get the val to check
681  }
682 
683  // By default, assertions are enabled unless the NDEBUG macro is
684  // defined (which happens in release builds).
685  //
686  // This adheres to the pay-only-for-what-you-use philosophy: if
687  // you are sure that your intent is correct, why would you need to
688  // pay the runtime cost for the assertions?
689  //
690  // The downside, of course, is that you may be unsure, or your
691  // code may be wrong; and then release builds may end up doing
692  // something wrong.
693  //
694  // So in that case, you can use the appropriate ryml predicates to
695  // check your intent (as in the examples above), or you can
696  // explicitly enable/disable assertions, by defining the macro
697  // RYML_USE_ASSERT to a proper value (see c4/yml/common.hpp). This
698  // will make problematic code trigger a call to the appropriate
699  // error callback.
700  //
701  // Also, to be clear, this does not apply to parse errors
702  // occurring when the YAML is parsed. Parse errors are always
703  // detected by the parser: this is not done through assertions,
704  // but through the appropriate error checking mechanism. So
705  // checking for these errors is always enabled and cannot be
706  // switched off. As for deserialization errors, see below.
707 
708 
709  //------------------------------------------------------------------
710  // Deserializing:
711  //
712  // - use .load(T*)/.load_key(T*) for lazy code with checked reads
713  // - ryml checks preconditions (node readability, etc), or triggers error
714  // - ryml checks deserialization, or triggers error
715  // - more expensive because of all the checks and error marshalling
716  // - optionally, can disable precondition checks with bool parameter
717  //
718  // - use .deserialize()/.deserialize_key() for sure reads
719  // - ryml asserts preconditions (readability, etc) (err on debug builds, UB otherwise)
720  // - ryml returns bool for deserialization status, [[nodiscard]]
721  // - cheaper but potentially dangerous
722  //
723  {
724  // fundamental types: ryml provides deserialization facilities
725  // .load() checks for readability + conversion errors
726  {
727  int foo = 0, bar0 = 0, bar1 = 0;
728  root["foo"].load(&foo); // calls err_visit() on failure
729  root["bar"][0].load(&bar0);
730  root["bar"][1].load(&bar1);
731  CHECK(foo == 1);
732  CHECK(bar0 == 2);
733  CHECK(bar1 == 3);
734  // .load() can also be called from the tree
735  }
736  // .deserialize() asserts readability, user checks conversion errors
737  {
738  int foo = 0, bar0 = 0, bar1 = 0;
739  CHECK(root["foo"].deserialize(&foo)); // user must check return value
740  CHECK(root["bar"][0].deserialize(&bar0));
741  CHECK(root["bar"][1].deserialize(&bar1));
742  CHECK(foo == 1);
743  CHECK(bar0 == 2);
744  CHECK(bar1 == 3);
745  // .deserialize() can also be called from the tree
746  }
747  // std containers: ryml optionally provides deserialization
748  // facilities. see serialization samples below.
749  {
750  std::string john_str, bar_str;
751  root["john"].load(&john_str);
752  root["bar"].load_key(&bar_str);
753  CHECK(john_str == "doe");
754  CHECK(bar_str == "bar");
755  std::vector<int> bar_actual, bar_expected{{2, 3}};
756  root["bar"].load(&bar_actual);
757  CHECK(bar_actual == bar_expected);
758  }
759  }
760 
761 
762  //------------------------------------------------------------------
763  // Modifying existing nodes: assigning vs serializing
764 
765  // As implied by its name, ConstNodeRef is a reference to a const
766  // node. It can be used to read from the node, but not write to it
767  // or modify the hierarchy of the node. If any modification is
768  // desired then a NodeRef must be used instead:
769  ryml::NodeRef wroot = tree.rootref(); // writeable root
770 
771  // .set_val() assigns an existing string to the receiving node.
772  // The contents are NOT copied, and the string pointer will be in
773  // effect until the tree goes out of scope! So BEWARE to only
774  // assign from strings outliving the tree.
775  wroot["foo"].set_val("says you");
776  wroot["bar"][0].set_val("-2");
777  wroot["bar"][1].set_val("-3");
778  wroot["john"].set_val("ron");
779  // Now the tree is _pointing_ at the memory of the strings above.
780  // In this case it is OK because those are static strings, located
781  // in the executable's static section, and will outlive the tree.
782  CHECK(root["foo"].val() == "says you");
783  CHECK(root["bar"][0].val() == "-2");
784  CHECK(root["bar"][1].val() == "-3");
785  CHECK(root["john"].val() == "ron");
786  // But WATCHOUT: do not assign from temporary objects:
787  // {
788  // std::string bad("will dangle");
789  // root["john"] = bad;
790  // }
791  // CHECK(root["john"] == "dangling"); // CRASH! the string was deallocated
792 
793  // For non-string types, or for strings whose lifetime is
794  // problematic WRT the tree, you can create and save a string in
795  // the tree using .set_serialized() (or its equivalent .save() if
796  // you want to have ryml do the error checking). It first
797  // serializes values to a string arena owned by the tree, then
798  // assigns the serialized string to the receiving node. This
799  // avoids constraints with the lifetime, since the arena lives
800  // with the tree.
801  CHECK(tree.arena().empty());
802  wroot["foo"].set_serialized("says who"); // requires to_chars(). see serialization samples below.
803  wroot["bar"][0].set_serialized(20);
804  wroot["bar"][1].set_serialized(30);
805  wroot["john"].set_serialized("deere");
806  CHECK(root["foo"].val() == "says who");
807  CHECK(root["bar"][0].val() == "20");
808  CHECK(root["bar"][1].val() == "30");
809  CHECK(root["john"].val() == "deere");
810  CHECK(tree.arena() == "says who2030deere"); // the result of serializations to the tree arena
811  // with .set_serialize()/.save(), the crash above is avoided:
812  {
813  std::string ok("in_scope");
814  // root["john"].set_val(ok); // don't, will dangle
815  wroot["john"].set_serialized(ok); // OK, copy to the tree's arena
816  }
817  CHECK(root["john"].val() == "in_scope"); // OK! val is now in the tree's arena
818  // serializing floating points:
819  wroot["float"].set_serialized(2.4f);
820  // to force a particular precision or float format:
821  // (see sample_float_precision() and sample_formatting())
822  wroot["digits"].set_serialized(ryml::fmt::real(2.4, /*num_digits*/6, ryml::FTOA_FLOAT));
823  CHECK(tree.arena() == "says who2030deerein_scope2.42.400000"); // the result of serializations to the tree arena
824 
825 
826  //------------------------------------------------------------------
827  // Adding new nodes:
828 
829  // adding a keyval node to a map:
830  CHECK(root.num_children() == 5);
831  wroot["newkeyval"].set_val("shiny and new"); // using these strings
832  c4::yml::NodeRef chsrl = wroot.append_child();
833  chsrl.set_key_serialized("newkeyval (serialized)"); // serializes and assigns the serialization
834  chsrl.set_serialized("shiny and new (serialized)"); // serializes and assigns the serialization
835  CHECK(root.num_children() == 7);
836  CHECK(root["newkeyval"].key() == "newkeyval");
837  CHECK(root["newkeyval"].val() == "shiny and new");
838  CHECK(root["newkeyval (serialized)"].key() == "newkeyval (serialized)");
839  CHECK(root["newkeyval (serialized)"].val() == "shiny and new (serialized)");
840  CHECK( ! tree.in_arena(root["newkeyval"].key())); // it's using directly the static string above
841  CHECK( ! tree.in_arena(root["newkeyval"].val())); // it's using directly the static string above
842  CHECK( tree.in_arena(root["newkeyval (serialized)"].key())); // it's using a serialization of the string above
843  CHECK( tree.in_arena(root["newkeyval (serialized)"].val())); // it's using a serialization of the string above
844  // adding a val node to a seq:
845  CHECK(root["bar"].num_children() == 2);
846  wroot["bar"][2].set_val("oh so nice");
847  wroot["bar"][3].set_serialized("oh so nice (serialized)");
848  CHECK(root["bar"].num_children() == 4);
849  CHECK(root["bar"][2].val() == "oh so nice");
850  CHECK(root["bar"][3].val() == "oh so nice (serialized)");
851  // adding a seq node:
852  CHECK(root.num_children() == 7);
853  wroot["newseq"].set_seq();
854  chsrl = wroot.append_child();
855  chsrl.set_key_serialized("newseq (serialized)");
856  chsrl.set_seq();
857  CHECK(root.num_children() == 9);
858  CHECK(root["newseq"].num_children() == 0);
859  CHECK(root["newseq"].is_seq());
860  CHECK(root["newseq (serialized)"].num_children() == 0);
861  CHECK(root["newseq (serialized)"].is_seq());
862  // adding a map node:
863  CHECK(root.num_children() == 9);
864  wroot["newmap"].set_map();
865  chsrl = wroot.append_child();
866  chsrl.set_key_serialized("newmap (serialized)");
867  chsrl.set_map();
868  CHECK(root.num_children() == 11);
869  CHECK(root["newmap"].num_children() == 0);
870  CHECK(root["newmap"].is_map());
871  CHECK(root["newmap (serialized)"].num_children() == 0);
872  CHECK(root["newmap (serialized)"].is_map());
873  //
874  // When the tree is const, operator[] searches the tree for a
875  // matching key/position, and returns it, or raises an error if
876  // none is found.
877  //
878  // When the tree is mutable, operator[] will not mutate the tree
879  // until the returned node is written to. operator[] first
880  // searches the tree for a node matching the key/position and
881  // returns it if it exists; otherwise, the result is a NodeRef
882  // object which keeps in itself the required information to write
883  // to the proper place in the tree. This is called being in a
884  // "seed" state.
885  //
886  // This means that passing a key/position which does not exist
887  // will not mutate the tree, but will instead store (in the
888  // returned NodeRef) the proper place of the tree to be able to do
889  // so, if and when it is required. This is why the node is said to
890  // be in "seed" state - it allows creating the entry in the tree
891  // in the future.
892  //
893  // This is a significant difference from eg, the behavior of
894  // std::map, which mutates the map immediately within the call to
895  // operator[].
896  //
897  // All of the points above apply only if the tree is mutable. If
898  // the tree is const, then a NodeRef cannot be obtained from it;
899  // only a ConstNodeRef, which can never be used to mutate the
900  // tree.
901  //
902  CHECK(!root.has_child("I am not nothing"));
903  ryml::NodeRef nothing;
904  CHECK(nothing.invalid()); // invalid because it points at nothing
905  nothing = wroot["I am nothing"];
906  CHECK(!nothing.invalid()); // points at the tree, and a specific place in the tree
907  CHECK(nothing.is_seed()); // ... but nothing is there yet.
908  CHECK(!root.has_child("I am nothing")); // same as above
909  CHECK(!nothing.readable()); // ... and this node cannot be used to
910  // read anything from the tree
911  ryml::NodeRef something = wroot["I am something"];
912  ryml::ConstNodeRef constsomething = wroot["I am something"];
913  CHECK(!root.has_child("I am something")); // same as above
914  CHECK(!something.invalid());
915  CHECK(something.is_seed()); // same as above
916  CHECK(!something.readable()); // same as above
917  CHECK(constsomething.invalid()); // NOTE: because a ConstNodeRef cannot be
918  // used to mutate a tree, it is only valid()
919  // if it is pointing at an existing node.
920  something.set_val("indeed"); // this will commit the seed to the tree, mutating at the proper place
921  CHECK(root.has_child("I am something"));
922  CHECK(root["I am something"].val() == "indeed");
923  CHECK(!something.invalid()); // it was already valid
924  CHECK(!something.is_seed()); // now the tree has this node, so the
925  // ref is no longer a seed
926  CHECK(something.readable()); // and it is now readable
927  //
928  // now the constref is also valid (but it needs to be reassigned):
929  ryml::ConstNodeRef constsomethingnew = wroot["I am something"];
930  CHECK(!constsomethingnew.invalid());
931  CHECK(constsomethingnew.readable());
932  // note that the old constref is now stale, because it only keeps
933  // the state at creation:
934  CHECK(constsomething.invalid());
935  CHECK(!constsomething.readable());
936  //
937  // -----------------------------------------------------------
938  // Remember: a seed node cannot be used to read from the tree!
939  // -----------------------------------------------------------
940  //
941  // The seed node needs to be created and become readable first.
942  //
943  // Trying to invoke any tree-reading method on a node that is not
944  // readable will cause an assertion (see RYML_USE_ASSERT).
945  //
946  // It is your responsibility to verify that the preconditions are
947  // met. If you are not sure about the structure of your data,
948  // write your code defensively to signify your full intent:
949  //
950  ryml::NodeRef wbar = wroot["bar"];
951  if(wbar.readable() && wbar.is_seq()) // .is_seq() requires .readable()
952  {
953  CHECK(wbar[0].readable() && wbar[0].val() == "20");
954  CHECK( ! wbar[100].readable());
955  CHECK( ! wbar[100].readable() || wbar[100].val() == "100"); // <- no crash because it is not .readable(), so never tries to call .val()
956  // this would work as well:
957  CHECK( ! wbar[0].is_seed() && wbar[0].val() == "20");
958  CHECK(wbar[100].is_seed() || wbar[100].val() == "100");
959  }
960 
961 
962  //------------------------------------------------------------------
963  // .operator[]() vs .at()
964 
965  // (Const)NodeRef::operator[] is an analogue to std::vector::operator[].
966  // (Const)NodeRef::at() is an analogue to std::vector::at()
967  //
968  // at() will always check the subject node is .readable().
969  //
970  // [] is meant for the happy path: unverified in Release builds,
971  // and asserts .readable() in Debug builds (more specifically when
972  // RYML_USE_ASSERT is defined and NDEBUG is not defined).
973  {
974  // in this example we will be checking errors, so set up a
975  // temporary error handler to catch them:
976  ScopedErrorHandlerExample errh; // calls ryml::set_callbacks()
977  // instantiate the tree after errh
978  ryml::Tree err_tree = ryml::parse_in_arena("{foo: bar}");
979  // ... so that the tree uses the current callbacks:
980  CHECK(err_tree.callbacks() == errh.callbacks());
981  // node does not exist, only a seed node
982  ryml::NodeRef seed_node = err_tree["this"];
983  // ... therefore not .readable()
984  CHECK(!seed_node.readable());
985  // using .at() reliably produces an error:
986  CHECK(errh.check_error_occurs([&]{
987  return seed_node.at("is").at("an").at("invalid").at("operation");
988  // ^
989  // error occurs here because it is unreadable
990  }));
991  // ... but using [] fails only when RYML_USE_ASSERT is
992  // defined. otherwise, it's the dreaded Undefined Behavior:
993  CHECK(errh.check_assertion_occurs([&]{
994  return seed_node["is"]["an"]["invalid"]["operation"];
995  // ^
996  // assertion occurs here because it is unreadable
997  }));
998  }
999 
1000 
1001  //------------------------------------------------------------------
1002  // Emitting:
1003 
1004  ryml::csubstr expected_result =
1005  "foo: says who" "\n"
1006  "bar: [20,30,oh so nice,oh so nice (serialized)]" "\n"
1007  "john: in_scope" "\n"
1008  "float: 2.4" "\n"
1009  "digits: 2.400000" "\n"
1010  "newkeyval: shiny and new" "\n"
1011  "newkeyval (serialized): shiny and new (serialized)" "\n"
1012  "newseq: []" "\n"
1013  "newseq (serialized): []" "\n"
1014  "newmap: {}" "\n"
1015  "newmap (serialized): {}" "\n"
1016  "I am something: indeed" "\n"
1017  "";
1018 
1019  // emit to a FILE*
1020  ryml::emit_yaml(tree, stdout);
1021  // emit to a stream
1022  // (this is templated on the stream, so it works both with
1023  // standard streams like std::ostream/std::stringstream, or with
1024  // user-defined streams not inheriting from ostream)
1025  std::stringstream ss;
1026  ss << tree;
1027  std::string stream_result = ss.str();
1028  // emit to a buffer:
1029  std::string str_result = ryml::emitrs_yaml<std::string>(tree);
1030  // can emit to any given buffer:
1031  char buf[1024];
1032  ryml::csubstr buf_result = ryml::emit_yaml(tree, buf);
1033  // now check
1034  CHECK(buf_result == expected_result);
1035  CHECK(str_result == expected_result);
1036  CHECK(stream_result == expected_result);
1037  // There are many possibilities to emit to buffer;
1038  // please look at the emit sample functions below.
1039 
1040 
1041  //------------------------------------------------------------------
1042  // ConstNodeRef vs NodeRef
1043 
1044  ryml::NodeRef noderef = tree["bar"][0];
1045  ryml::ConstNodeRef constnoderef = tree["bar"][0];
1046 
1047  // ConstNodeRef cannot be used to mutate the tree:
1048  //constnoderef.set_val("21"); // compile error: method does not exist
1049  //constnoderef.set_serialized("22"); // compile error
1050  // ... but a NodeRef can:
1051  noderef.set_val("21"); // ok, can assign because it's not const
1052  CHECK(tree["bar"][0].val() == "21");
1053  noderef.set_serialized("22"); // ok, can serialize and assign because it's not const
1054  CHECK(tree["bar"][0].val() == "22");
1055 
1056  // it is not possible to obtain a NodeRef from a ConstNodeRef:
1057  // noderef = constnoderef; // compile error
1058 
1059  // it is always possible to obtain a ConstNodeRef from a NodeRef:
1060  constnoderef = noderef; // ok can assign const <- nonconst
1061 
1062  // If a tree is const, then only ConstNodeRef's can be
1063  // obtained from that tree:
1064  ryml::Tree const& consttree = tree;
1065  //noderef = consttree["bar"][0]; // compile error
1066  noderef = tree["bar"][0]; // ok
1067  constnoderef = consttree["bar"][0]; // ok
1068 
1069  // ConstNodeRef and NodeRef can be compared for equality.
1070  // Equality means they point at the same node.
1071  CHECK(constnoderef == noderef);
1072  CHECK(!(constnoderef != noderef));
1073 
1074 
1075  //------------------------------------------------------------------
1076  // Getting the location of nodes in the source:
1077  //
1078  // Location tracking is opt-in:
1079  ryml::EventHandlerTree evt_handler = {};
1080  ryml::Parser parser(&evt_handler, ryml::ParserOptions().locations(true));
1081  // Now the parser will start by building the accelerator structure:
1082  ryml::Tree tree2 = parse_in_arena(&parser, "expected.yml", expected_result);
1083  // ... and use it when querying
1084  ryml::ConstNodeRef subject_node = tree2["bar"][1];
1085  CHECK(subject_node.val() == "30");
1086  ryml::Location loc = subject_node.location(parser);
1087  CHECK(parser.location_contents(loc).begins_with("30"));
1088  CHECK(loc.line == 1u);
1089  CHECK(loc.col == 9u);
1090  // For further details in location tracking,
1091  // refer to the sample function below.
1092 
1093 
1094  //------------------------------------------------------------------
1095  // Dealing with UTF8
1096  ryml::Tree langs = ryml::parse_in_arena(""
1097  "#" "\n"
1098  "# UTF8/16/32 can be placed directly in any scalar:" "\n"
1099  "#" "\n"
1100  "en: Planet (Gas)" "\n"
1101  "fr: Planète (Gazeuse)" "\n"
1102  "ru: Планета (Газ)" "\n"
1103  "ja: 惑星(ガス)" "\n"
1104  "zh: 行星(气体)" "\n"
1105  "#" "\n"
1106  "# UTF8 decoding only happens in double-quoted strings," "\n"
1107  "# as per the YAML standard" "\n"
1108  "#" "\n"
1109  "decode this: \"\\u263A c\\x61f\\xE9\"" "\n"
1110  "and this as well: \"\\u2705 \\U0001D11E\"" "\n"
1111  "not decoded: '\\u263A \\xE2\\x98\\xBA'" "\n"
1112  "neither this: '\\u2705 \\U0001D11E'" "\n");
1113  // in-place UTF8 just works:
1114  CHECK(langs["en"].val() == "Planet (Gas)");
1115  CHECK(langs["fr"].val() == "Planète (Gazeuse)");
1116  CHECK(langs["ru"].val() == "Планета (Газ)");
1117  CHECK(langs["ja"].val() == "惑星(ガス)");
1118  CHECK(langs["zh"].val() == "行星(气体)");
1119  // and \x \u \U codepoints are decoded, but only when they appear
1120  // inside double-quoted strings, as dictated by the YAML
1121  // standard:
1122  CHECK(langs["decode this"].val() == "☺ café");
1123  CHECK(langs["and this as well"].val() == "✅ 𝄞");
1124  CHECK(langs["not decoded"].val() == "\\u263A \\xE2\\x98\\xBA");
1125  CHECK(langs["neither this"].val() == "\\u2705 \\U0001D11E");
1126 }
csubstr const & key(id_type node) const
Definition: tree.hpp:454
ConstNodeRef last_sibling() const RYML_NOEXCEPT
Forward to Tree::last_sibling().
Definition: node.hpp:861
an index to none
Definition: common.hpp:131
id_type first_child(id_type node) const
Definition: tree.hpp:577
id_type num_siblings() const RYML_NOEXCEPT
O(num_children).
Definition: node.hpp:303
ryml::Callbacks callbacks()
a helper to create the Callbacks object for the custom error handler
Location location(Parser const &parser) const
Definition: node.hpp:318
bool is_seq() const RYML_NOEXCEPT
Forward to Tree::is_seq().
Definition: node.hpp:208
ConstNodeRef parent() const RYML_NOEXCEPT
Forward to Tree::parent().
Definition: node.hpp:853
bool check_assertion_occurs(Fn &&fn)
checking that an assertion occurs while calling fn.
ConstNodeRef last_child() const RYML_NOEXCEPT
Forward to Tree::last_child().
Definition: node.hpp:855
Shows how to create a scoped error handler.
Definition: quickstart.cpp:368
The event handler to create a ryml Tree.
id_type id() const noexcept
Definition: node.hpp:822
id_type first_sibling(id_type node) const
Definition: tree.hpp:592
bool has_key() const RYML_NOEXCEPT
Forward to Tree::has_key().
Definition: node.hpp:210
void load(T *v, bool check_readable=true) const
(1) deserialize the node's contents (val or container) to the given variable, forwarding to the user-...
Definition: node.hpp:345
bool is_map(id_type node) const
Definition: tree.hpp:480
bool invalid() const noexcept
Definition: node.hpp:790
csubstr arena() const
get the current arena
Definition: tree.hpp:1317
ConstNodeRef next_sibling() const RYML_NOEXCEPT
Forward to Tree::next_sibling().
Definition: node.hpp:859
print the real number in floating point format (like f)
Definition: charconv.hpp:194
real_< T > real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT)
Definition: format.hpp:362
void set_val(csubstr val)
Definition: node.hpp:1251
void parse_in_arena(Parser *parser, csubstr filename, csubstr yaml, Tree *tree, id_type node_id)
(1) parse YAML into an existing tree node. The filename will be used in any error messages arising du...
Definition: parse.cpp:209
ConstNodeRef first_sibling() const RYML_NOEXCEPT
Forward to Tree::first_sibling().
Definition: node.hpp:860
id_type root_id() const
Get the id of the root node. The tree must not be empty. The tree can be empty only when constructed ...
Definition: tree.hpp:386
ConstNodeRef first_child() const RYML_NOEXCEPT
Forward to Tree::first_child().
Definition: node.hpp:854
A reference to a node in an existing yaml tree, offering a more convenient API than the index-based A...
Definition: node.hpp:1062
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
Definition: common.hpp:124
Options to give to the ParseEngine to control its behavior.
bool in_arena(csubstr s) const
return true if the given substring is part of the tree's string arena
Definition: tree.hpp:1327
bool is_seq(id_type node) const
Definition: tree.hpp:481
Callbacks const & callbacks() const
Definition: tree.hpp:349
Parser parser
Definition: parse.cpp:117
bool readable() const noexcept
because a ConstNodeRef cannot be used to write to the tree, readable() has the same meaning as !inval...
Definition: node.hpp:793
holds a source or yaml file position, for example when an error is detected; See also location_format...
Definition: common.hpp:228
#define CHECK(predicate)
a testing assertion, used only in this quickstart
Definition: quickstart.cpp:310
ConstNodeRef prev_sibling() const RYML_NOEXCEPT
Forward to Tree::prev_sibling().
Definition: node.hpp:858
Holds a pointer to an existing tree, and a node id.
Definition: node.hpp:736
id_type next_sibling(id_type node) const
Definition: tree.hpp:572
substr emit_yaml(Tree const &t, EmitOptions const &opts, substr buf, bool error_on_excess)
(1) emit YAML to the given buffer.
Definition: emit_buf.cpp:28
NodeRef rootref()
Get the root as a NodeRef . Note that a non-const Tree implicitly converts to NodeRef.
Definition: tree.cpp:56
This is the main driver of parsing logic: it scans the YAML or JSON source for tokens, and emits the appropriate sequence of parsing events to its event handler.
Definition: fwd.hpp:17
bool empty() const noexcept
Definition: substr.hpp:356
csubstr val() const RYML_NOEXCEPT
Forward to Tree::val().
Definition: node.hpp:179
bool check_error_occurs(Fn &&fn)
checking that an error occurs while calling fn
const_children_view children() const RYML_NOEXCEPT
get an iterable view over children
Definition: node.hpp:987
NodeRef append_child()
Definition: node.hpp:1715
bool is_val() const RYML_NOEXCEPT
Forward to Tree::is_val().
Definition: node.hpp:211
void parse_in_place(Parser *parser, csubstr filename, substr yaml, Tree *tree, id_type node_id)
(1) parse YAML into an existing tree node.
Definition: parse.cpp:165
void set_serialized(T const &v)
serialize a variable to this node.
Definition: node.hpp:1375
void set_key_serialized(T const &k)
serialize a variable, then assign the result to the node's key
Definition: node.hpp:1396
id_type size() const
Definition: tree.hpp:345
void set_seq()
Definition: node.hpp:1254
id_type find_child(id_type node, csubstr const &key) const
find child by name, or NONE if no child is found with this key like Tree::child(), but return a ReadResult with the status
Definition: tree.cpp:1249
bool has_val() const RYML_NOEXCEPT
Forward to Tree::has_val().
Definition: node.hpp:209
csubstr key() const RYML_NOEXCEPT
Forward to Tree::key().
Definition: node.hpp:174
void set_map()
Definition: node.hpp:1257