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