cannam@147: // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors cannam@147: // Licensed under the MIT License: cannam@147: // cannam@147: // Permission is hereby granted, free of charge, to any person obtaining a copy cannam@147: // of this software and associated documentation files (the "Software"), to deal cannam@147: // in the Software without restriction, including without limitation the rights cannam@147: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@147: // copies of the Software, and to permit persons to whom the Software is cannam@147: // furnished to do so, subject to the following conditions: cannam@147: // cannam@147: // The above copyright notice and this permission notice shall be included in cannam@147: // all copies or substantial portions of the Software. cannam@147: // cannam@147: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@147: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@147: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@147: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@147: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@147: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@147: // THE SOFTWARE. cannam@147: cannam@147: // This sample code appears in the documentation for the C++ implementation. cannam@147: // cannam@147: // If Cap'n Proto is installed, build the sample like: cannam@147: // capnp compile -oc++ addressbook.capnp cannam@147: // c++ -std=c++11 -Wall addressbook.c++ addressbook.capnp.c++ `pkg-config --cflags --libs capnp` -o addressbook cannam@147: // cannam@147: // If Cap'n Proto is not installed, but the source is located at $SRC and has been cannam@147: // compiled in $BUILD (often both are simply ".." from here), you can do: cannam@147: // $BUILD/capnp compile -I$SRC/src -o$BUILD/capnpc-c++ addressbook.capnp cannam@147: // c++ -std=c++11 -Wall addressbook.c++ addressbook.capnp.c++ -I$SRC/src -L$BUILD/.libs -lcapnp -lkj -o addressbook cannam@147: // cannam@147: // Run like: cannam@147: // ./addressbook write | ./addressbook read cannam@147: // Use "dwrite" and "dread" to use dynamic code instead. cannam@147: cannam@147: // TODO(test): Needs cleanup. cannam@147: cannam@147: #include "addressbook.capnp.h" cannam@147: #include cannam@147: #include cannam@147: #include cannam@147: cannam@147: using addressbook::Person; cannam@147: using addressbook::AddressBook; cannam@147: cannam@147: void writeAddressBook(int fd) { cannam@147: ::capnp::MallocMessageBuilder message; cannam@147: cannam@147: AddressBook::Builder addressBook = message.initRoot(); cannam@147: ::capnp::List::Builder people = addressBook.initPeople(2); cannam@147: cannam@147: Person::Builder alice = people[0]; cannam@147: alice.setId(123); cannam@147: alice.setName("Alice"); cannam@147: alice.setEmail("alice@example.com"); cannam@147: // Type shown for explanation purposes; normally you'd use auto. cannam@147: ::capnp::List::Builder alicePhones = cannam@147: alice.initPhones(1); cannam@147: alicePhones[0].setNumber("555-1212"); cannam@147: alicePhones[0].setType(Person::PhoneNumber::Type::MOBILE); cannam@147: alice.getEmployment().setSchool("MIT"); cannam@147: cannam@147: Person::Builder bob = people[1]; cannam@147: bob.setId(456); cannam@147: bob.setName("Bob"); cannam@147: bob.setEmail("bob@example.com"); cannam@147: auto bobPhones = bob.initPhones(2); cannam@147: bobPhones[0].setNumber("555-4567"); cannam@147: bobPhones[0].setType(Person::PhoneNumber::Type::HOME); cannam@147: bobPhones[1].setNumber("555-7654"); cannam@147: bobPhones[1].setType(Person::PhoneNumber::Type::WORK); cannam@147: bob.getEmployment().setUnemployed(); cannam@147: cannam@147: writePackedMessageToFd(fd, message); cannam@147: } cannam@147: cannam@147: void printAddressBook(int fd) { cannam@147: ::capnp::PackedFdMessageReader message(fd); cannam@147: cannam@147: AddressBook::Reader addressBook = message.getRoot(); cannam@147: cannam@147: for (Person::Reader person : addressBook.getPeople()) { cannam@147: std::cout << person.getName().cStr() << ": " cannam@147: << person.getEmail().cStr() << std::endl; cannam@147: for (Person::PhoneNumber::Reader phone: person.getPhones()) { cannam@147: const char* typeName = "UNKNOWN"; cannam@147: switch (phone.getType()) { cannam@147: case Person::PhoneNumber::Type::MOBILE: typeName = "mobile"; break; cannam@147: case Person::PhoneNumber::Type::HOME: typeName = "home"; break; cannam@147: case Person::PhoneNumber::Type::WORK: typeName = "work"; break; cannam@147: } cannam@147: std::cout << " " << typeName << " phone: " cannam@147: << phone.getNumber().cStr() << std::endl; cannam@147: } cannam@147: Person::Employment::Reader employment = person.getEmployment(); cannam@147: switch (employment.which()) { cannam@147: case Person::Employment::UNEMPLOYED: cannam@147: std::cout << " unemployed" << std::endl; cannam@147: break; cannam@147: case Person::Employment::EMPLOYER: cannam@147: std::cout << " employer: " cannam@147: << employment.getEmployer().cStr() << std::endl; cannam@147: break; cannam@147: case Person::Employment::SCHOOL: cannam@147: std::cout << " student at: " cannam@147: << employment.getSchool().cStr() << std::endl; cannam@147: break; cannam@147: case Person::Employment::SELF_EMPLOYED: cannam@147: std::cout << " self-employed" << std::endl; cannam@147: break; cannam@147: } cannam@147: } cannam@147: } cannam@147: cannam@147: #include "addressbook.capnp.h" cannam@147: #include cannam@147: #include cannam@147: #include cannam@147: #include cannam@147: #include cannam@147: cannam@147: using ::capnp::DynamicValue; cannam@147: using ::capnp::DynamicStruct; cannam@147: using ::capnp::DynamicEnum; cannam@147: using ::capnp::DynamicList; cannam@147: using ::capnp::List; cannam@147: using ::capnp::Schema; cannam@147: using ::capnp::StructSchema; cannam@147: using ::capnp::EnumSchema; cannam@147: cannam@147: using ::capnp::Void; cannam@147: using ::capnp::Text; cannam@147: using ::capnp::MallocMessageBuilder; cannam@147: using ::capnp::PackedFdMessageReader; cannam@147: cannam@147: void dynamicWriteAddressBook(int fd, StructSchema schema) { cannam@147: // Write a message using the dynamic API to set each cannam@147: // field by text name. This isn't something you'd cannam@147: // normally want to do; it's just for illustration. cannam@147: cannam@147: MallocMessageBuilder message; cannam@147: cannam@147: // Types shown for explanation purposes; normally you'd cannam@147: // use auto. cannam@147: DynamicStruct::Builder addressBook = cannam@147: message.initRoot(schema); cannam@147: cannam@147: DynamicList::Builder people = cannam@147: addressBook.init("people", 2).as(); cannam@147: cannam@147: DynamicStruct::Builder alice = cannam@147: people[0].as(); cannam@147: alice.set("id", 123); cannam@147: alice.set("name", "Alice"); cannam@147: alice.set("email", "alice@example.com"); cannam@147: auto alicePhones = alice.init("phones", 1).as(); cannam@147: auto phone0 = alicePhones[0].as(); cannam@147: phone0.set("number", "555-1212"); cannam@147: phone0.set("type", "mobile"); cannam@147: alice.get("employment").as() cannam@147: .set("school", "MIT"); cannam@147: cannam@147: auto bob = people[1].as(); cannam@147: bob.set("id", 456); cannam@147: bob.set("name", "Bob"); cannam@147: bob.set("email", "bob@example.com"); cannam@147: cannam@147: // Some magic: We can convert a dynamic sub-value back to cannam@147: // the native type with as()! cannam@147: List::Builder bobPhones = cannam@147: bob.init("phones", 2).as>(); cannam@147: bobPhones[0].setNumber("555-4567"); cannam@147: bobPhones[0].setType(Person::PhoneNumber::Type::HOME); cannam@147: bobPhones[1].setNumber("555-7654"); cannam@147: bobPhones[1].setType(Person::PhoneNumber::Type::WORK); cannam@147: bob.get("employment").as() cannam@147: .set("unemployed", ::capnp::VOID); cannam@147: cannam@147: writePackedMessageToFd(fd, message); cannam@147: } cannam@147: cannam@147: void dynamicPrintValue(DynamicValue::Reader value) { cannam@147: // Print an arbitrary message via the dynamic API by cannam@147: // iterating over the schema. Look at the handling cannam@147: // of STRUCT in particular. cannam@147: cannam@147: switch (value.getType()) { cannam@147: case DynamicValue::VOID: cannam@147: std::cout << ""; cannam@147: break; cannam@147: case DynamicValue::BOOL: cannam@147: std::cout << (value.as() ? "true" : "false"); cannam@147: break; cannam@147: case DynamicValue::INT: cannam@147: std::cout << value.as(); cannam@147: break; cannam@147: case DynamicValue::UINT: cannam@147: std::cout << value.as(); cannam@147: break; cannam@147: case DynamicValue::FLOAT: cannam@147: std::cout << value.as(); cannam@147: break; cannam@147: case DynamicValue::TEXT: cannam@147: std::cout << '\"' << value.as().cStr() << '\"'; cannam@147: break; cannam@147: case DynamicValue::LIST: { cannam@147: std::cout << "["; cannam@147: bool first = true; cannam@147: for (auto element: value.as()) { cannam@147: if (first) { cannam@147: first = false; cannam@147: } else { cannam@147: std::cout << ", "; cannam@147: } cannam@147: dynamicPrintValue(element); cannam@147: } cannam@147: std::cout << "]"; cannam@147: break; cannam@147: } cannam@147: case DynamicValue::ENUM: { cannam@147: auto enumValue = value.as(); cannam@147: KJ_IF_MAYBE(enumerant, enumValue.getEnumerant()) { cannam@147: std::cout << cannam@147: enumerant->getProto().getName().cStr(); cannam@147: } else { cannam@147: // Unknown enum value; output raw number. cannam@147: std::cout << enumValue.getRaw(); cannam@147: } cannam@147: break; cannam@147: } cannam@147: case DynamicValue::STRUCT: { cannam@147: std::cout << "("; cannam@147: auto structValue = value.as(); cannam@147: bool first = true; cannam@147: for (auto field: structValue.getSchema().getFields()) { cannam@147: if (!structValue.has(field)) continue; cannam@147: if (first) { cannam@147: first = false; cannam@147: } else { cannam@147: std::cout << ", "; cannam@147: } cannam@147: std::cout << field.getProto().getName().cStr() cannam@147: << " = "; cannam@147: dynamicPrintValue(structValue.get(field)); cannam@147: } cannam@147: std::cout << ")"; cannam@147: break; cannam@147: } cannam@147: default: cannam@147: // There are other types, we aren't handling them. cannam@147: std::cout << "?"; cannam@147: break; cannam@147: } cannam@147: } cannam@147: cannam@147: void dynamicPrintMessage(int fd, StructSchema schema) { cannam@147: PackedFdMessageReader message(fd); cannam@147: dynamicPrintValue(message.getRoot(schema)); cannam@147: std::cout << std::endl; cannam@147: } cannam@147: cannam@147: int main(int argc, char* argv[]) { cannam@147: StructSchema schema = Schema::from(); cannam@147: if (argc != 2) { cannam@147: std::cerr << "Missing arg." << std::endl; cannam@147: return 1; cannam@147: } else if (strcmp(argv[1], "write") == 0) { cannam@147: writeAddressBook(1); cannam@147: } else if (strcmp(argv[1], "read") == 0) { cannam@147: printAddressBook(0); cannam@147: } else if (strcmp(argv[1], "dwrite") == 0) { cannam@147: dynamicWriteAddressBook(1, schema); cannam@147: } else if (strcmp(argv[1], "dread") == 0) { cannam@147: dynamicPrintMessage(0, schema); cannam@147: } else { cannam@147: std::cerr << "Invalid arg: " << argv[1] << std::endl; cannam@147: return 1; cannam@147: } cannam@147: return 0; cannam@147: } cannam@147: