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