The C Code Generator
Design The overall goal is to keep the code-generator as simple as possible. Hopefully performance isn't sacrificed to that end! Anyways, we generate very little code: we mostly generate structure definitions (for example enums and structures for messages) and some metadata which is basically reflection-type data. The serializing and deserializing is implemented in a library, called libprotobuf-c rather than generated code.
The Generated Code For each enum, we generate a C enum. For each message, we generate a C structure which can be cast to a ProtobufCMessage. For each enum and message, we generate a descriptor object that allows us to implement a kind of reflection on the structures.
Naming Conventions First, some naming conventions: The name of the type for enums and messages and services is camel case (meaning WordsAreCrammedTogether) except that double-underscores are used to delimit scopes. For example: would generate a C type Foo__Bar__BazBah. Functions and globals are all lowercase, with camel-case words separated by single underscores; namespaces are separated with double-underscores. For example: Enums values are all uppercase. Stuff we dd to your symbol names will also be separated by a double-underscore. For example, the unpack method above.
Generated Descriptors We also generate descriptor objects for messages and enums. These are declared in the .h files:
Message Methods The message structures all begin with ProtobufCMessage, so they may be cast to that type. We generate some functions for each message: unpack(). Unpack data for a particular message-format: Note that allocator may be NULL. free_unpacked(). Free a message that you obtained with the unpack method: get_packed_size(). Find how long the serialized representation of the data will be: message-format: pack(). Pack message into buffer; assumes that buffer is long enough (use get_packed_size first!). pack_to_buffer(). Pack message into virtualize buffer.
Services Services are collections of methods each having an input and output type. Unlike messages where we generate a structure that corresponds to the actual message object, for services we generate a function that creates a ProtobufCService from a collection of user-defined methods. We also define simple functions that invoke each method of a service. These functions work if the service is created by the create_service generated function or if the service is instantiated by an RPC system. Suppose we have a .proto file: We will get generated code: val, wr->radix, buf); rv.descriptor = &b__descriptor; rv.str = buf; closure (&rv, closure_data); } /* convert string to int: use strtoul */ static void radix_atoi (Convert_Service *service, const B *input, A__Closure closure, void *closure_data) { Convert_WithRadix *wr = (Convert_WithRadix *) service; A rv; rv.val = strtoul (input->val, NULL, wr->radix); rv.descriptor = &a__descriptor; closure (&rv, closure_data); } /* create a new convert service by radix */ ProtobufCService * create_convert_service_from_radix (unsigned radix) { Convert_WithRadix *wr = malloc (sizeof (Convert_WithRadix)); convert__init (wr, (Convert__ServiceDestroy) free); wr->base.itoa = radix_itoa; wr->base.atoi = radix_atoi; wr->radix = radix; return (ProtobufCService *) wr; } ]]> Just like with messages, you may cast from Convert_Service to ProtobufCService, at least as long as you have run the __init function. Conversely, we generate functions to help you invoke service methods on generic ProtobufCService objects. These go through the invoke() of service and they work on both services created with create_service as well as factory-provided services like those provided by RPC systems. For example:
The protobuf-c Library This library is used by the generated code; it includes common structures and enums, as well as functions that most users of the generated code will want. There are three main components: the Descriptor structures helper structures and objects packing and unpacking code
protobuf-c: the Descriptor structures For example, enums are described in terms of structures: Likewise, messages are described by: And finally services are described by:
protobuf-c: helper structures and typedefs We defined typedefs for a few types which are used in .proto files but do not have obvious standard C equivalents: a boolean type (protobuf_c_boolean) a binary-data (bytes) type (ProtobufCBinaryData) the various int types (int32_t, uint32_t, int64_t, uint64_t) are obtained by including inttypes.h We also define a simple allocator object, ProtobufCAllocator that let's you control how allocations are done. This is predominately used for parsing. There is a virtual buffer facility that only has to implement a method to append binary-data to the buffer. This can be used to serialize messages to different targets (instead of a flat slab of data). We define a base-type for all messages, for code that handles messages generically. All it has is the descriptor object.
Buffers One important helper type is the ProtobufCBuffer which allows you to abstract the target of serialization. The only thing that a buffer has is an append method: ProtobufCBuffer subclasses are often defined on the stack. For example, to write to a FILE you could make: fp); // XXX: no error handling! } ]]> To use this new type of Buffer, you would do something like: A commonly builtin subtype is the BufferSimple which is declared on the stack and uses a scratch buffer provided by the user for its initial allocation. It does exponential resizing. To create a BufferSimple, use code like: You can access the data as buf.len and buf.data. For example, To finish up, use:
protobuf-c: packing and unpacking messages To pack messages one first computes their packed size, then provide a buffer to pack into. Or you can use the "streaming" approach: where ProtobufCBuffer is a base object with an append metod. See . To unpack messages, you should simple call If you pass NULL for allocator, then the default allocator will be used. You can cast the result to the type that matches the descriptor. The result of unpacking should be freed with protobuf_c_message_free().
Author Dave Benson.