diff --git a/.gitignore b/.gitignore index 76d9560..c3a9d9d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,8 @@ test-driver *.log *.trs +tests/*.out + *.o *.a *.so diff --git a/Makefile.am b/Makefile.am index 8622c90..d2ea63c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -56,6 +56,8 @@ check_PROGRAMS += \ capn_test_SOURCES = \ tests/capn-test.cpp \ tests/capn-stream-test.cpp \ + tests/example-test.cpp \ + tests/example.capnp.c \ compiler/test.capnp.c \ compiler/schema-test.cpp \ compiler/schema.capnp.c diff --git a/README.md b/README.md index a9d9fa8..eac0af5 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,78 @@ capnpc-c This is a C plugin for [Cap'n Proto](http://kentonv.github.io/capnproto), an efficient protocol for sharing data and capabilities. -This is only the plugin, to properly make use of it you need to download, build -and install capnpc and then build and install this project and then you can -utilize it as: +This is only the code generator plugin, to properly make use of it you +need to download, build and install capnpc and then build and install +this project and then you can utilize it as: - capnpc compiler/test.capnp -oc +```sh +capnpc compiler/test.capnp -oc +``` [![Build Status](https://travis-ci.org/opensourcerouting/c-capnproto.svg?branch=master)](https://travis-ci.org/opensourcerouting/c-capnproto) -## status +## Building on Linux + +```sh +git clone https://github.com/opensourcerouting/c-capnproto +cd c-capnproto +git submodule update --init --recursive +autoreconf -f -i -s +./configure +make +make check +``` + +## Usage + +### Generating C code from a `.capnp` schema file + +The `compiler` directory contains the C language plugin (`capnpc-c`) for use with the `capnp` tool: https://capnproto.org/capnp-tool.html. + +`capnp` will by default search `$PATH` for `capnpc-c` - if it's on your PATH, you can generate code for your schema as follows: + +```sh +capnp compile -o c myschema.capnp +``` + +Otherwise, you can specify the path to the c plugin: + +```sh +capnp compile -o ./capnpc-c myschema.capnp +``` + +`capnp` generates a C struct that corresponds to each capn proto struct, along with read/write functions that convert to/from capn proto form. + +If you want accessor functions for struct members, use attribute `fieldgetset` in your `.capnp` file as follows: + +```capnp +using C = import "${c-capnproto}/compiler/c.capnp"; + +$C.fieldgetset; + +struct MyStruct {} +``` + +### Example C code + +See the unit tests in [`tests/example-test.cpp`](tests/example-test.cpp). The example schema file is [`tests/example.capnp`](tests/example.capnp). The tests are written in C++, but only use C features. + +You need to compile these runtime library files and link them into your own project's binaries: + +* [`lib/capn.c`](lib/capn.c) +* [`lib/capn-malloc.c`](lib/capn-malloc.c) +* [`lib/capn-stream.c`](lib/capn-stream.c) + +Your include path must contain the runtime library directory [`lib`](lib). Header file [`lib/capnp_c.h`](lib/capnp_c.h) contains the public interfaces of the library. + +For further reference, please see the other unit tests in [`tests`](tests), and header file [`lib/capnp_c.h`](lib/capnp_c.h). + +The project [`quagga-capnproto`](https://github.com/opensourcerouting/quagga-capnproto) uses `c-capnproto` and contains some good examples, as found with [this github repository search](https://github.com/opensourcerouting/quagga-capnproto/search?utf8=%E2%9C%93&q=capn&type=): + +* Serialization in function [`bgp_notify_send()`](https://github.com/opensourcerouting/quagga-capnproto/blob/27061648f3418fac0d217b16a46add534343e841/bgpd/bgp_zmq.c#L81-L96) in file `quagga-capnproto/bgpd/bgp_zmq.c` +* Deserialization in function [`qzc_callback()`](https://github.com/opensourcerouting/quagga-capnproto/blob/27061648f3418fac0d217b16a46add534343e841/lib/qzc.c#L249-L257) in file `quagga-capnproto/lib/qzc.c` + +## Status This is a merge of 3 forks of [James McKaskill's great work](https://github.com/jmckaskill/c-capnproto), which has been untouched for @@ -21,33 +84,3 @@ a while: - [liamstask's fork](https://github.com/liamstask/c-capnproto) - [baruch's fork](https://github.com/baruch/c-capnproto) - [kylemanna's fork](https://github.com/kylemanna/c-capnproto) - - -## usage - -The `compiler` directory contains the C language plugin (`capnpc-c`) for use with the `capnp` tool: https://capnproto.org/capnp-tool.html. - -`capnp` will by default search `$PATH` for `capnpc-c` - if it's on your PATH, you can generate code for your schema as follows: - - $ capnp compile -o c myschema.capnp - -Otherwise, you can specify the path to the c plugin: - - $ capnp compile -o ./capnpc-c myschema.capnp - -`capnp` generates a C struct that corresponds to each capn proto struct, along with read/write functions that convert to/from capn proto form. - -TBD whether it will make sense in the future to provide accessor functions for struct members, rather than converting entire structs. - -## building on linux - -``` -git clone https://github.com/opensourcerouting/c-capnproto -cd c-capnproto -git submodule update --init --recursive -autoreconf -f -i -s -./configure -make -make check -``` - diff --git a/tests/example-test.cpp b/tests/example-test.cpp new file mode 100644 index 0000000..cfec3f7 --- /dev/null +++ b/tests/example-test.cpp @@ -0,0 +1,72 @@ +/* example-test.cpp + * + * Some simple examples using c-capnproto + * + * Copyright (C) 2017 Alex Helfet + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +#include +#include + +#include "capnp_c.h" +#include "example.capnp.h" + +TEST(Examples, RoundTrip) { + uint8_t buf[4096]; + ssize_t sz = 0; + + const char *s = "Hello."; + + { + // Set initial object in `g`. + const capn_text capn_val0 = { + .len = strlen(s), + .str = s, + .seg = NULL + }; + struct Greeting g = { + .text = capn_val0, + .timesToSay = 17, + }; + + // Serialize `g` to `buf[0..sz-1]`. + struct capn c; + capn_init_malloc(&c); + capn_ptr cr = capn_root(&c); + struct capn_segment *cs = cr.seg; + Greeting_ptr gp = new_Greeting(cs); + write_Greeting(&g, gp); + int setp_ret = capn_setp(capn_root(&c), 0, gp.p); + ASSERT_EQ(0, setp_ret); + sz = capn_write_mem(&c, buf, sizeof(buf), 0 /* packed */); + capn_free(&c); + } + + { + // Write serialized object to file system. + FILE *f = fopen("tests/example-test.cpp.Greeting.out", "wb"); + ASSERT_NE(f, (void*)0); + fwrite(buf, 1 /* size */, sz /* count */, f); + int close_ret = fclose(f); + ASSERT_EQ(0, close_ret); + } + + { + // Deserialize `buf[0..sz-1]` to `rg`. + struct capn rc; + int init_mem_ret = capn_init_mem(&rc, buf, sz, 0 /* packed */); + ASSERT_EQ(0, init_mem_ret); + Greeting_ptr rroot; + struct Greeting rg; + rroot.p = capn_getp(capn_root(&rc), 0 /* off */, 1 /* resolve */); + read_Greeting(&rg, rroot); + + // Assert deserialized values in `rg` + EXPECT_EQ(rg.timesToSay, 17); + EXPECT_EQ(strlen(s), (uint32_t) rg.text.len); + EXPECT_EQ(0, strncmp(s, rg.text.str, strlen(s))); + } +} diff --git a/tests/example.capnp b/tests/example.capnp new file mode 100644 index 0000000..36ebda2 --- /dev/null +++ b/tests/example.capnp @@ -0,0 +1,10 @@ +@0xd120e9a4c43868ab; + +using C = import "../compiler/c.capnp"; + +$C.fieldgetset; + +struct Greeting { + text @0 :Text = "Default greeting: hello world!"; + timesToSay @1 :UInt32; +} diff --git a/tests/example.capnp.c b/tests/example.capnp.c new file mode 100644 index 0000000..0c4dfed --- /dev/null +++ b/tests/example.capnp.c @@ -0,0 +1,66 @@ +#include "example.capnp.h" +/* AUTO GENERATED - DO NOT EDIT */ +static const capn_text capn_val0 = {0,"",0}; +static const uint8_t capn_buf[32] = { + 68,101,102,97,117,108,116,32, + 103,114,101,101,116,105,110,103, + 58,32,104,101,108,108,111,32, + 119,111,114,108,100,33,0,0 +}; +static const struct capn_segment capn_seg = {{0},0,0,0,(char*)&capn_buf[0],32,32,0}; +static capn_text capn_val1 = {30,(char*)&capn_buf[0],(struct capn_segment*)&capn_seg}; + +Greeting_ptr new_Greeting(struct capn_segment *s) { + Greeting_ptr p; + p.p = capn_new_struct(s, 8, 1); + return p; +} +Greeting_list new_Greeting_list(struct capn_segment *s, int len) { + Greeting_list p; + p.p = capn_new_list(s, len, 8, 1); + return p; +} +void read_Greeting(struct Greeting *s, Greeting_ptr p) { + capn_resolve(&p.p); + s->text = capn_get_text(p.p, 0, capn_val1); + s->timesToSay = capn_read32(p.p, 0); +} +void write_Greeting(const struct Greeting *s, Greeting_ptr p) { + capn_resolve(&p.p); + capn_set_text(p.p, 0, (s->text.str != capn_val1.str) ? s->text : capn_val0); + capn_write32(p.p, 0, s->timesToSay); +} +void get_Greeting(struct Greeting *s, Greeting_list l, int i) { + Greeting_ptr p; + p.p = capn_getp(l.p, i, 0); + read_Greeting(s, p); +} +void set_Greeting(const struct Greeting *s, Greeting_list l, int i) { + Greeting_ptr p; + p.p = capn_getp(l.p, i, 0); + write_Greeting(s, p); +} + +capn_text Greeting_get_text(Greeting_ptr p) +{ + capn_text text; + text = capn_get_text(p.p, 0, capn_val1); + return text; +} + +uint32_t Greeting_get_timesToSay(Greeting_ptr p) +{ + uint32_t timesToSay; + timesToSay = capn_read32(p.p, 0); + return timesToSay; +} + +void Greeting_set_text(Greeting_ptr p, capn_text text) +{ + capn_set_text(p.p, 0, (text.str != capn_val1.str) ? text : capn_val0); +} + +void Greeting_set_timesToSay(Greeting_ptr p, uint32_t timesToSay) +{ + capn_write32(p.p, 0, timesToSay); +} diff --git a/tests/example.capnp.h b/tests/example.capnp.h new file mode 100644 index 0000000..9342d23 --- /dev/null +++ b/tests/example.capnp.h @@ -0,0 +1,54 @@ +#ifndef CAPN_D120E9A4C43868AB +#define CAPN_D120E9A4C43868AB +/* AUTO GENERATED - DO NOT EDIT */ +#include + +#if CAPN_VERSION != 1 +#error "version mismatch between capnp_c.h and generated code" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct Greeting; + +typedef struct {capn_ptr p;} Greeting_ptr; + +typedef struct {capn_ptr p;} Greeting_list; + +struct Greeting { + capn_text text; + uint32_t timesToSay; +}; + +static const size_t Greeting_word_count = 1; + +static const size_t Greeting_pointer_count = 1; + +static const size_t Greeting_struct_bytes_count = 16; + +capn_text Greeting_get_text(Greeting_ptr p); + +uint32_t Greeting_get_timesToSay(Greeting_ptr p); + +void Greeting_set_text(Greeting_ptr p, capn_text text); + +void Greeting_set_timesToSay(Greeting_ptr p, uint32_t timesToSay); + +Greeting_ptr new_Greeting(struct capn_segment*); + +Greeting_list new_Greeting_list(struct capn_segment*, int len); + +void read_Greeting(struct Greeting*, Greeting_ptr); + +void write_Greeting(const struct Greeting*, Greeting_ptr); + +void get_Greeting(struct Greeting*, Greeting_list, int i); + +void set_Greeting(const struct Greeting*, Greeting_list, int i); + +#ifdef __cplusplus +} +#endif +#endif