forked from capnproto/go-capnp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
doc.go
384 lines (293 loc) · 11.9 KB
/
doc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/*
Package capnp is a Cap'n Proto library for Go.
https://capnproto.org/
Read the Getting Started guide for a tutorial on how to use this
package. https://github.com/capnproto/go-capnproto2/wiki/Getting-Started
Generating code
capnpc-go provides the compiler backend for capnp.
# First, install capnpc-go to $PATH.
go install zombiezen.com/go/capnproto2/capnpc-go
# Then, generate Go files.
capnp compile -I$GOPATH/src/zombiezen.com/go/capnproto2/std -ogo *.capnp
capnpc-go requires two annotations for all files: package and import.
package is needed to know what package to place at the head of the
generated file and what identifier to use when referring to the type
from another package. import should be the fully qualified import path
and is used to generate import statement from other packages and to
detect when two types are in the same package. For example:
using Go = import "/go.capnp";
$Go.package("main");
$Go.import("zombiezen.com/go/capnproto2/example");
For adding documentation comments to the generated code, there's the doc
annotation. This annotation adds the comment to a struct, enum or field so
that godoc will pick it up. For example:
struct Zdate $Go.doc("Zdate represents a calendar date") {
year @0 :Int16;
month @1 :UInt8;
day @2 :UInt8 ;
}
Messages and Segments
In Cap'n Proto, the unit of communication is a message. A message
consists of one or more segments -- contiguous blocks of memory. This
allows large messages to be split up and loaded independently or lazily.
Typically you will use one segment per message. Logically, a message is
organized in a tree of objects, with the root always being a struct (as
opposed to a list or primitive). Messages can be read from and written
to a stream.
The Message and Segment types are the main types that application code
will use from this package. The Message type has methods for marshaling
and unmarshaling its segments to the wire format. If the application
needs to read or write from a stream, it should use the Encoder and
Decoder types.
Pointers
The type for a generic reference to a Cap'n Proto object is Ptr. A Ptr
can refer to a struct, a list, or an interface. Ptr, Struct, List, and
Interface (the pointer types) have value semantics and refer to data in
a single segment. All of the pointer types have a notion of "valid".
An invalid pointer will return the default value from any accessor and
panic when any setter is called.
In previous versions of this package, the Pointer interface was used
instead of the Ptr struct. This interface and functions that use it are
now deprecated. See https://github.com/capnproto/go-capnproto2/wiki/New-Ptr-Type
for details about this API change.
Data accessors and setters (i.e. struct primitive fields and list
elements) do not return errors, but pointer accessors and setters do.
There are a few reasons that a read or write of a pointer can fail, but
the most common are bad pointers or allocation failures. For accessors,
an invalid object will be returned in case of an error.
Since Go doesn't have generics, wrapper types provide type safety on
lists. This package provides lists of basic types, and capnpc-go
generates list wrappers for named types. However, if you need to use
deeper nesting of lists (e.g. List(List(UInt8))), you will need to use a
PointerList and wrap the elements.
Structs
For the following schema:
struct Foo @0x8423424e9b01c0af {
num @0 :UInt32;
bar @1 :Foo;
}
capnpc-go will generate:
// Foo is a pointer to a Foo struct in a segment.
// Member functions are provided to get/set members in the
// struct.
type Foo struct{ capnp.Struct }
// Foo_TypeID is the unique identifier for the type Foo.
// It remains the same across languages and schema changes.
const Foo_TypeID = 0x8423424e9b01c0af
// NewFoo creates a new orphaned Foo struct, preferring placement in
// s. If there isn't enough space, then another segment in the
// message will be used or allocated. You can set a field of type Foo
// to this new message, but usually you will want to use the
// NewBar()-style method shown below.
func NewFoo(s *capnp.Segment) (Foo, error)
// NewRootFoo creates a new Foo struct and sets the message's root to
// it.
func NewRootFoo(s *capnp.Segment) (Foo, error)
// ReadRootFoo reads the message's root pointer and converts it to a
// Foo struct.
func ReadRootFoo(msg *capnp.Message) (Foo, error)
// Num returns the value of the num field.
func (s Foo) Num() uint32
// SetNum sets the value of the num field to v.
func (s Foo) SetNum(v uint32)
// Bar returns the value of the bar field. This can return an error
// if the pointer goes beyond the segment's range, the segment fails
// to load, or the pointer recursion limit has been reached.
func (s Foo) Bar() (Foo, error)
// HasBar reports whether the bar field was initialized (non-null).
func (s Foo) HasBar() bool
// SetBar sets the value of the bar field to v.
func (s Foo) SetBar(v Foo) error
// NewBar sets the bar field to a newly allocated Foo struct,
// preferring placement in s's segment.
func (s Foo) NewBar() (Foo, error)
// Foo_List is a value with pointer semantics. It is created for all
// structs, and is used for List(Foo) in the capnp file.
type Foo_List struct{ capnp.List }
// NewFoo_List creates a new orphaned List(Foo), preferring placement
// in s. This can then be added to a message by using a Set function
// which takes a Foo_List. sz specifies the number of elements in the
// list. The list's size cannot be changed after creation.
func NewFoo_List(s *capnp.Segment, sz int32) Foo_List
// Len returns the number of elements in the list.
func (s Foo_List) Len() int
// At returns a pointer to the i'th element. If i is an invalid index,
// this will return an invalid Foo (all getters will return default
// values, setters will fail).
func (s Foo_List) At(i int) Foo
// Foo_Promise is a promise for a Foo. Methods are provided to get
// promises of struct and interface fields.
type Foo_Promise struct{ *capnp.Pipeline }
// Get waits until the promise is resolved and returns the result.
func (p Foo_Promise) Get() (Foo, error)
// Bar returns a promise for that bar field.
func (p Foo_Promise) Bar() Foo_Promise
Groups
For each group a typedef is created with a different method set for just the
groups fields:
struct Foo {
group :Group {
field @0 :Bool;
}
}
generates the following:
type Foo struct{ capnp.Struct }
type Foo_group Foo
func (s Foo) Group() Foo_group
func (s Foo_group) Field() bool
That way the following may be used to access a field in a group:
var f Foo
value := f.Group().Field()
Note that group accessors just convert the type and so have no overhead.
Unions
Named unions are treated as a group with an inner unnamed union. Unnamed
unions generate an enum Type_Which and a corresponding Which() function:
struct Foo {
union {
a @0 :Bool;
b @1 :Bool;
}
}
generates the following:
type Foo_Which uint16
const (
Foo_Which_a Foo_Which = 0
Foo_Which_b Foo_Which = 1
)
func (s Foo) A() bool
func (s Foo) B() bool
func (s Foo) SetA(v bool)
func (s Foo) SetB(v bool)
func (s Foo) Which() Foo_Which
Which() should be checked before using the getters, and the default case must
always be handled.
Setters for single values will set the union discriminator as well as set the
value.
For voids in unions, there is a void setter that just sets the discriminator.
For example:
struct Foo {
union {
a @0 :Void;
b @1 :Void;
}
}
generates the following:
func (s Foo) SetA() // Set that we are using A
func (s Foo) SetB() // Set that we are using B
Similarly, for groups in unions, there is a group setter that just sets
the discriminator. This must be called before the group getter can be
used to set values. For example:
struct Foo {
union {
a :group {
v :Bool
}
b :group {
v :Bool
}
}
}
and in usage:
f.SetA() // Set that we are using group A
f.A().SetV(true) // then we can use the group A getter to set the inner values
Enums
capnpc-go generates enum values as constants. For example in the capnp file:
enum ElementSize {
empty @0;
bit @1;
byte @2;
twoBytes @3;
fourBytes @4;
eightBytes @5;
pointer @6;
inlineComposite @7;
}
In the generated capnp.go file:
type ElementSize uint16
const (
ElementSize_empty ElementSize = 0
ElementSize_bit ElementSize = 1
ElementSize_byte ElementSize = 2
ElementSize_twoBytes ElementSize = 3
ElementSize_fourBytes ElementSize = 4
ElementSize_eightBytes ElementSize = 5
ElementSize_pointer ElementSize = 6
ElementSize_inlineComposite ElementSize = 7
)
In addition an enum.String() function is generated that will convert the constants to a string
for debugging or logging purposes. By default, the enum name is used as the tag value,
but the tags can be customized with a $Go.tag or $Go.notag annotation.
For example:
enum ElementSize {
empty @0 $Go.tag("void");
bit @1 $Go.tag("1 bit");
byte @2 $Go.tag("8 bits");
inlineComposite @7 $Go.notag;
}
In the generated go file:
func (c ElementSize) String() string {
switch c {
case ElementSize_empty:
return "void"
case ElementSize_bit:
return "1 bit"
case ElementSize_byte:
return "8 bits"
default:
return ""
}
}
Interfaces
capnpc-go generates type-safe Client wrappers for interfaces. For parameter
lists and result lists, structs are generated as described above with the names
Interface_method_Params and Interface_method_Results, unless a single struct
type is used. For example, for this interface:
interface Calculator {
evaluate @0 (expression :Expression) -> (value :Value);
}
capnpc-go generates the following Go code (along with the structs
Calculator_evaluate_Params and Calculator_evaluate_Results):
// Calculator is a client to a Calculator interface.
type Calculator struct{ Client capnp.Client }
// Evaluate calls `evaluate` on the client. params is called on a newly
// allocated Calculator_evaluate_Params struct to fill in the parameters.
func (c Calculator) Evaluate(
ctx context.Context,
params func(Calculator_evaluate_Params) error,
opts ...capnp.CallOption) *Calculator_evaluate_Results_Promise
capnpc-go also generates code to implement the interface:
// A Calculator_Server implements the Calculator interface.
type Calculator_Server interface {
Evaluate(Calculator_evaluate_Call) error
}
// Calculator_evaluate_Call holds the arguments for a Calculator.evaluate server call.
type Calculator_evaluate_Call struct {
Ctx context.Context
Options capnp.CallOptions
Params Calculator_evaluate_Params
Results Calculator_evaluate_Results
}
// Calculator_ServerToClient is equivalent to calling:
// NewCalculator(capnp.NewServer(Calculator_Methods(nil, s), s))
// If s does not implement the Close method, then nil is used.
func Calculator_ServerToClient(s Calculator_Server) Calculator
// Calculator_Methods appends methods from Calculator that call to server and
// returns the methods. If methods is nil or the capacity of the underlying
// slice is too small, a new slice is returned.
func Calculator_Methods(methods []server.Method, s Calculator_Server) []server.Method
Since a single capability may want to implement many interfaces, you can
use multiple *_Methods functions to build a single slice to send to
NewServer.
An example of combining the client/server code to communicate with a locally
implemented Calculator:
var srv Calculator_Server
calc := Calculator_ServerToClient(srv)
result := calc.Evaluate(ctx, func(params Calculator_evaluate_Params) {
params.SetExpression(expr)
})
val := result.Value().Get()
A note about message ordering: when implementing a server method, you
are responsible for acknowledging delivery of a method call. Failure to
do so can cause deadlocks. See the server.Ack function for more details.
*/
package capnp // import "zombiezen.com/go/capnproto2"