Tutorial.md (5493B)
1 # Introduction # 2 3 A typical example, loading a configuration file, might look like this: 4 5 ```cpp 6 YAML::Node config = YAML::LoadFile("config.yaml"); 7 8 if (config["lastLogin"]) { 9 std::cout << "Last logged in: " << config["lastLogin"].as<DateTime>() << "\n"; 10 } 11 12 const std::string username = config["username"].as<std::string>(); 13 const std::string password = config["password"].as<std::string>(); 14 login(username, password); 15 config["lastLogin"] = getCurrentDateTime(); 16 17 std::ofstream fout("config.yaml"); 18 fout << config; 19 ``` 20 21 # Basic Parsing and Node Editing # 22 23 All nodes in a YAML document (including the root) are represented by `YAML::Node`. You can check what kind it is: 24 25 ```cpp 26 YAML::Node node = YAML::Load("[1, 2, 3]"); 27 assert(node.Type() == YAML::NodeType::Sequence); 28 assert(node.IsSequence()); // a shortcut! 29 ``` 30 31 Collection nodes (sequences and maps) act somewhat like STL vectors and maps: 32 33 ```cpp 34 YAML::Node primes = YAML::Load("[2, 3, 5, 7, 11]"); 35 for (std::size_t i=0;i<primes.size();i++) { 36 std::cout << primes[i].as<int>() << "\n"; 37 } 38 // or: 39 for (YAML::const_iterator it=primes.begin();it!=primes.end();++it) { 40 std::cout << it->as<int>() << "\n"; 41 } 42 43 primes.push_back(13); 44 assert(primes.size() == 6); 45 ``` 46 47 and 48 49 ```cpp 50 YAML::Node lineup = YAML::Load("{1B: Prince Fielder, 2B: Rickie Weeks, LF: Ryan Braun}"); 51 for(YAML::const_iterator it=lineup.begin();it!=lineup.end();++it) { 52 std::cout << "Playing at " << it->first.as<std::string>() << " is " << it->second.as<std::string>() << "\n"; 53 } 54 55 lineup["RF"] = "Corey Hart"; 56 lineup["C"] = "Jonathan Lucroy"; 57 assert(lineup.size() == 5); 58 ``` 59 60 Querying for keys does **not** create them automatically (this makes handling optional map entries very easy) 61 62 ```cpp 63 YAML::Node node = YAML::Load("{name: Brewers, city: Milwaukee}"); 64 if (node["name"]) { 65 std::cout << node["name"].as<std::string>() << "\n"; 66 } 67 if (node["mascot"]) { 68 std::cout << node["mascot"].as<std::string>() << "\n"; 69 } 70 assert(node.size() == 2); // the previous call didn't create a node 71 ``` 72 73 If you're not sure what kind of data you're getting, you can query the type of a node: 74 75 ```cpp 76 switch (node.Type()) { 77 case Null: // ... 78 case Scalar: // ... 79 case Sequence: // ... 80 case Map: // ... 81 case Undefined: // ... 82 } 83 ``` 84 85 or ask directly whether it's a particular type, e.g.: 86 87 ```cpp 88 if (node.IsSequence()) { 89 // ... 90 } 91 ``` 92 93 # Building Nodes # 94 95 You can build `YAML::Node` from scratch: 96 97 ```cpp 98 YAML::Node node; // starts out as null 99 node["key"] = "value"; // it now is a map node 100 node["seq"].push_back("first element"); // node["seq"] automatically becomes a sequence 101 node["seq"].push_back("second element"); 102 103 node["mirror"] = node["seq"][0]; // this creates an alias 104 node["seq"][0] = "1st element"; // this also changes node["mirror"] 105 node["mirror"] = "element #1"; // and this changes node["seq"][0] - they're really the "same" node 106 107 node["self"] = node; // you can even create self-aliases 108 node[node["mirror"]] = node["seq"]; // and strange loops :) 109 ``` 110 111 The above node is now: 112 113 ```yaml 114 &1 115 key: value 116 &2 seq: [&3 "element #1", second element] 117 mirror: *3 118 self: *1 119 *3 : *2 120 ``` 121 122 # How Sequences Turn Into Maps # 123 124 Sequences can be turned into maps by asking for non-integer keys. For example, 125 126 ```cpp 127 YAML::Node node = YAML::Load("[1, 2, 3]"); 128 node[1] = 5; // still a sequence, [1, 5, 3] 129 node.push_back(-3) // still a sequence, [1, 5, 3, -3] 130 node["key"] = "value"; // now it's a map! {0: 1, 1: 5, 2: 3, 3: -3, key: value} 131 ``` 132 133 Indexing a sequence node by an index that's not in its range will _usually_ turn it into a map, but if the index is one past the end of the sequence, then the sequence will grow by one to accommodate it. (That's the **only** exception to this rule.) For example, 134 135 ```cpp 136 YAML::Node node = YAML::Load("[1, 2, 3]"); 137 node[3] = 4; // still a sequence, [1, 2, 3, 4] 138 node[10] = 10; // now it's a map! {0: 1, 1: 2, 2: 3, 3: 4, 10: 10} 139 ``` 140 141 # Converting To/From Native Data Types # 142 143 Yaml-cpp has built-in conversion to and from most built-in data types, as well as `std::vector`, `std::list`, and `std::map`. The following examples demonstrate when those conversions are used: 144 145 ```cpp 146 YAML::Node node = YAML::Load("{pi: 3.14159, [0, 1]: integers}"); 147 148 // this needs the conversion from Node to double 149 double pi = node["pi"].as<double>(); 150 151 // this needs the conversion from double to Node 152 node["e"] = 2.71828; 153 154 // this needs the conversion from Node to std::vector<int> (*not* the other way around!) 155 std::vector<int> v; 156 v.push_back(0); 157 v.push_back(1); 158 std::string str = node[v].as<std::string>(); 159 ``` 160 161 To use yaml-cpp with your own data types, you need to specialize the YAML::convert<> template class. For example, suppose you had a simple `Vec3` class: 162 163 ```cpp 164 struct Vec3 { double x, y, z; /* etc - make sure you have overloaded operator== */ }; 165 ``` 166 167 You could write 168 169 ```cpp 170 namespace YAML { 171 template<> 172 struct convert<Vec3> { 173 static Node encode(const Vec3& rhs) { 174 Node node; 175 node.push_back(rhs.x); 176 node.push_back(rhs.y); 177 node.push_back(rhs.z); 178 return node; 179 } 180 181 static bool decode(const Node& node, Vec3& rhs) { 182 if(!node.IsSequence() || node.size() != 3) { 183 return false; 184 } 185 186 rhs.x = node[0].as<double>(); 187 rhs.y = node[1].as<double>(); 188 rhs.z = node[2].as<double>(); 189 return true; 190 } 191 }; 192 } 193 ``` 194 195 Then you could use `Vec3` wherever you could use any other type: 196 197 ```cpp 198 YAML::Node node = YAML::Load("start: [1, 3, 0]"); 199 Vec3 v = node["start"].as<Vec3>(); 200 node["end"] = Vec3(2, -1, 0); 201 ```