addressbook.c++ (9674B)
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 // This sample code appears in the documentation for the C++ implementation. 23 // 24 // If Cap'n Proto is installed, build the sample like: 25 // capnp compile -oc++ addressbook.capnp 26 // c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ `pkg-config --cflags --libs capnp` -o addressbook 27 // 28 // If Cap'n Proto is not installed, but the source is located at $SRC and has been 29 // compiled in $BUILD (often both are simply ".." from here), you can do: 30 // $BUILD/capnp compile -I$SRC/src -o$BUILD/capnpc-c++ addressbook.capnp 31 // c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ -I$SRC/src -L$BUILD/.libs -lcapnp -lkj -o addressbook 32 // 33 // Run like: 34 // ./addressbook write | ./addressbook read 35 // Use "dwrite" and "dread" to use dynamic code instead. 36 37 // TODO(test): Needs cleanup. 38 39 #include "addressbook.capnp.h" 40 #include <capnp/message.h> 41 #include <capnp/serialize-packed.h> 42 #include <iostream> 43 44 using addressbook::Person; 45 using addressbook::AddressBook; 46 47 void writeAddressBook(int fd) { 48 ::capnp::MallocMessageBuilder message; 49 50 AddressBook::Builder addressBook = message.initRoot<AddressBook>(); 51 ::capnp::List<Person>::Builder people = addressBook.initPeople(2); 52 53 Person::Builder alice = people[0]; 54 alice.setId(123); 55 alice.setName("Alice"); 56 alice.setEmail("alice@example.com"); 57 // Type shown for explanation purposes; normally you'd use auto. 58 ::capnp::List<Person::PhoneNumber>::Builder alicePhones = 59 alice.initPhones(1); 60 alicePhones[0].setNumber("555-1212"); 61 alicePhones[0].setType(Person::PhoneNumber::Type::MOBILE); 62 alice.getEmployment().setSchool("MIT"); 63 64 Person::Builder bob = people[1]; 65 bob.setId(456); 66 bob.setName("Bob"); 67 bob.setEmail("bob@example.com"); 68 auto bobPhones = bob.initPhones(2); 69 bobPhones[0].setNumber("555-4567"); 70 bobPhones[0].setType(Person::PhoneNumber::Type::HOME); 71 bobPhones[1].setNumber("555-7654"); 72 bobPhones[1].setType(Person::PhoneNumber::Type::WORK); 73 bob.getEmployment().setUnemployed(); 74 75 writePackedMessageToFd(fd, message); 76 } 77 78 void printAddressBook(int fd) { 79 ::capnp::PackedFdMessageReader message(fd); 80 81 AddressBook::Reader addressBook = message.getRoot<AddressBook>(); 82 83 for (Person::Reader person : addressBook.getPeople()) { 84 std::cout << person.getName().cStr() << ": " 85 << person.getEmail().cStr() << std::endl; 86 for (Person::PhoneNumber::Reader phone: person.getPhones()) { 87 const char* typeName = "UNKNOWN"; 88 switch (phone.getType()) { 89 case Person::PhoneNumber::Type::MOBILE: typeName = "mobile"; break; 90 case Person::PhoneNumber::Type::HOME: typeName = "home"; break; 91 case Person::PhoneNumber::Type::WORK: typeName = "work"; break; 92 } 93 std::cout << " " << typeName << " phone: " 94 << phone.getNumber().cStr() << std::endl; 95 } 96 Person::Employment::Reader employment = person.getEmployment(); 97 switch (employment.which()) { 98 case Person::Employment::UNEMPLOYED: 99 std::cout << " unemployed" << std::endl; 100 break; 101 case Person::Employment::EMPLOYER: 102 std::cout << " employer: " 103 << employment.getEmployer().cStr() << std::endl; 104 break; 105 case Person::Employment::SCHOOL: 106 std::cout << " student at: " 107 << employment.getSchool().cStr() << std::endl; 108 break; 109 case Person::Employment::SELF_EMPLOYED: 110 std::cout << " self-employed" << std::endl; 111 break; 112 } 113 } 114 } 115 116 #if !CAPNP_LITE 117 118 #include "addressbook.capnp.h" 119 #include <capnp/message.h> 120 #include <capnp/serialize-packed.h> 121 #include <iostream> 122 #include <capnp/schema.h> 123 #include <capnp/dynamic.h> 124 125 using ::capnp::DynamicValue; 126 using ::capnp::DynamicStruct; 127 using ::capnp::DynamicEnum; 128 using ::capnp::DynamicList; 129 using ::capnp::List; 130 using ::capnp::Schema; 131 using ::capnp::StructSchema; 132 using ::capnp::EnumSchema; 133 134 using ::capnp::Void; 135 using ::capnp::Text; 136 using ::capnp::MallocMessageBuilder; 137 using ::capnp::PackedFdMessageReader; 138 139 void dynamicWriteAddressBook(int fd, StructSchema schema) { 140 // Write a message using the dynamic API to set each 141 // field by text name. This isn't something you'd 142 // normally want to do; it's just for illustration. 143 144 MallocMessageBuilder message; 145 146 // Types shown for explanation purposes; normally you'd 147 // use auto. 148 DynamicStruct::Builder addressBook = 149 message.initRoot<DynamicStruct>(schema); 150 151 DynamicList::Builder people = 152 addressBook.init("people", 2).as<DynamicList>(); 153 154 DynamicStruct::Builder alice = 155 people[0].as<DynamicStruct>(); 156 alice.set("id", 123); 157 alice.set("name", "Alice"); 158 alice.set("email", "alice@example.com"); 159 auto alicePhones = alice.init("phones", 1).as<DynamicList>(); 160 auto phone0 = alicePhones[0].as<DynamicStruct>(); 161 phone0.set("number", "555-1212"); 162 phone0.set("type", "mobile"); 163 alice.get("employment").as<DynamicStruct>() 164 .set("school", "MIT"); 165 166 auto bob = people[1].as<DynamicStruct>(); 167 bob.set("id", 456); 168 bob.set("name", "Bob"); 169 bob.set("email", "bob@example.com"); 170 171 // Some magic: We can convert a dynamic sub-value back to 172 // the native type with as<T>()! 173 List<Person::PhoneNumber>::Builder bobPhones = 174 bob.init("phones", 2).as<List<Person::PhoneNumber>>(); 175 bobPhones[0].setNumber("555-4567"); 176 bobPhones[0].setType(Person::PhoneNumber::Type::HOME); 177 bobPhones[1].setNumber("555-7654"); 178 bobPhones[1].setType(Person::PhoneNumber::Type::WORK); 179 bob.get("employment").as<DynamicStruct>() 180 .set("unemployed", ::capnp::VOID); 181 182 writePackedMessageToFd(fd, message); 183 } 184 185 void dynamicPrintValue(DynamicValue::Reader value) { 186 // Print an arbitrary message via the dynamic API by 187 // iterating over the schema. Look at the handling 188 // of STRUCT in particular. 189 190 switch (value.getType()) { 191 case DynamicValue::VOID: 192 std::cout << ""; 193 break; 194 case DynamicValue::BOOL: 195 std::cout << (value.as<bool>() ? "true" : "false"); 196 break; 197 case DynamicValue::INT: 198 std::cout << value.as<int64_t>(); 199 break; 200 case DynamicValue::UINT: 201 std::cout << value.as<uint64_t>(); 202 break; 203 case DynamicValue::FLOAT: 204 std::cout << value.as<double>(); 205 break; 206 case DynamicValue::TEXT: 207 std::cout << '\"' << value.as<Text>().cStr() << '\"'; 208 break; 209 case DynamicValue::LIST: { 210 std::cout << "["; 211 bool first = true; 212 for (auto element: value.as<DynamicList>()) { 213 if (first) { 214 first = false; 215 } else { 216 std::cout << ", "; 217 } 218 dynamicPrintValue(element); 219 } 220 std::cout << "]"; 221 break; 222 } 223 case DynamicValue::ENUM: { 224 auto enumValue = value.as<DynamicEnum>(); 225 KJ_IF_MAYBE(enumerant, enumValue.getEnumerant()) { 226 std::cout << 227 enumerant->getProto().getName().cStr(); 228 } else { 229 // Unknown enum value; output raw number. 230 std::cout << enumValue.getRaw(); 231 } 232 break; 233 } 234 case DynamicValue::STRUCT: { 235 std::cout << "("; 236 auto structValue = value.as<DynamicStruct>(); 237 bool first = true; 238 for (auto field: structValue.getSchema().getFields()) { 239 if (!structValue.has(field)) continue; 240 if (first) { 241 first = false; 242 } else { 243 std::cout << ", "; 244 } 245 std::cout << field.getProto().getName().cStr() 246 << " = "; 247 dynamicPrintValue(structValue.get(field)); 248 } 249 std::cout << ")"; 250 break; 251 } 252 default: 253 // There are other types, we aren't handling them. 254 std::cout << "?"; 255 break; 256 } 257 } 258 259 void dynamicPrintMessage(int fd, StructSchema schema) { 260 PackedFdMessageReader message(fd); 261 dynamicPrintValue(message.getRoot<DynamicStruct>(schema)); 262 std::cout << std::endl; 263 } 264 265 #endif // !CAPNP_LITE 266 267 int main(int argc, char* argv[]) { 268 if (argc != 2) { 269 std::cerr << "Missing arg." << std::endl; 270 return 1; 271 } else if (strcmp(argv[1], "write") == 0) { 272 writeAddressBook(1); 273 } else if (strcmp(argv[1], "read") == 0) { 274 printAddressBook(0); 275 #if !CAPNP_LITE 276 } else if (strcmp(argv[1], "dwrite") == 0) { 277 StructSchema schema = Schema::from<AddressBook>(); 278 dynamicWriteAddressBook(1, schema); 279 } else if (strcmp(argv[1], "dread") == 0) { 280 StructSchema schema = Schema::from<AddressBook>(); 281 dynamicPrintMessage(0, schema); 282 #endif 283 } else { 284 std::cerr << "Invalid arg: " << argv[1] << std::endl; 285 return 1; 286 } 287 return 0; 288 } 289