rapidyaml 0.15.2
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
Overview

Functions

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

Detailed Description

Function Documentation

◆ sample_lightning_overview()

void sample_lightning_overview ( )

lightning overview of most common features

a lightning tour over most features see sample_quick_overview

Definition at line 407 of file quickstart.cpp.

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

Referenced by main().

◆ sample_quick_overview()

void sample_quick_overview ( )

quick overview of most common features

a brief tour over most features

Definition at line 462 of file quickstart.cpp.

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

Referenced by main().