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