Tutorial#

This is the AddressBook sample from tests/addressbook.capnp, the same schema as Cap’n C++. The runnable form is tests/example-test.cpp (Examples.RoundTripPerson).

Generate C#

From a source checkout, after meson compile -C build:

$ capnp compile -Icompiler -o./build/capnpc-c tests/addressbook.capnp

That writes addressbook.capnp.c / .h next to the schema. Generated headers #include "c.capnp.h". Link lib/capn.c, lib/capn-malloc.c, lib/capn-stream.c.

Schema#

using C = import "/c.capnp";
$C.fieldgetset;

struct Person {
  id @0 :UInt32;
  name @1 :Text;
  email @2 :Text;
  phones @3 :List(PhoneNumber);
  employment :union {
    unemployed @4 :Void;
    employer @5 :Text;
    school @6 :Text;
    selfEmployed @7 :Void;
  }
}

struct AddressBook {
  people @0 :List(Person);
}

Write#

Zero-init optional pointer fields. Call capn_set_root after write_* or the framed bytes are a legal empty message.

static capn_text txt(const char *s) {
  return (capn_text){.len = (int)strlen(s), .str = s, .seg = NULL};
}

struct capn c;
capn_init_malloc(&c);
struct capn_segment *cs = capn_root(&c).seg;

struct Person p = {
  .id = 17,
  .name = txt("Firstname Lastname"),
  .email = txt("username@domain.com"),
};
p.employment_which = Person_employment_school;
p.employment.school = txt("of life");

p.phones = new_Person_PhoneNumber_list(cs, 2);
struct Person_PhoneNumber pn0 = {
  .number = txt("123"), .type = Person_PhoneNumber_Type_work,
};
set_Person_PhoneNumber(&pn0, p.phones, 0);

Person_ptr pp = new_Person(cs);
write_Person(&p, pp);
capn_set_root(&c, pp.p);

uint8_t buf[4096];
ssize_t sz = capn_write_mem(&c, buf, sizeof buf, 0);  /* unpacked */
capn_free(&c);

Packed: pass 1 as the last argument of capn_write_mem. Those bytes memcmp official capnp convert binary:packed when the object order matches schema order.

Read#

struct capn rc;
capn_init_mem(&rc, buf, sz, 0);
Person_ptr rroot;
rroot.p = capn_getp(capn_root(&rc), 0, 1);
struct Person rp;
read_Person(&rp, rroot);

/* rp.id == 17 */
/* rp.name.str / rp.name.len is "Firstname Lastname" */
/* Person_has_name(rroot) is 1; a wire-null Text has has_=0 and get_ "" */

struct Person_PhoneNumber rpn0;
get_Person_PhoneNumber(&rpn0, rp.phones, 0);
capn_free(&rc);

Canonical form#

struct capn canon;
capn_init_malloc(&canon);
if (capn_canonicalize(&c, &canon) == 0) {
    /* one segment, no far pointers; empty struct B = -1 */
}
capn_free(&canon);

capn_canonicalize memcmp=s =capnp convert binary:canonical on the AddressBook fixture. See wire for the layout rules.