C Guide¶
This document will introduce how to use bitproto with C language.
Compile bitproto for C¶
Firstly, run the bitproto compiler to generate code for C:
$ bitproto c pen.bitproto
Where the pen.bitproto is introduced in earlier section An example bitproto.
We will find that bitproto generates us two files in current directory:
pen_bp.h: Contains the declarations of structs, macros and api functions etc.pen_bp.c: Contains the function implementations.
It’s recommended to open this two generated files to have a look. In the generated file pen_bp.h:
The
enum Colorin bitproto is mapped to atypedefstatement in C, and the enum values are mapped to macros:typedef uint8_t Color; // 3bit #define COLOR_UNKNOWN 0 #define COLOR_RED 1 #define COLOR_BLUE 2 #define COLOR_GREEN 3
The
Timestamptype in bitproto is mapped to atypedefin C:typedef int64_t Timestamp; // 64bit
The message
Penin bitproto is mapped to astructin C:struct Pen { Color color; // 3bit Timestamp produced_at; // 64bit };
The compiler also generates three functions, they are the encoder, decoder and json formatter.
int EncodePen(struct Pen *m, unsigned char *s); int DecodePen(struct Pen *m, unsigned char *s); int JsonPen(struct Pen *m, char *s);
Download bitproto C library¶
Bitproto serialization requires a language-specific library to work, the generated encoder and decoder depends on the bitproto C library underlying.
Download the bitproto library for C language from
this github link,
and put them (the bitproto.c and bitproto.h) to current working directory.
Run the code¶
Now, we create a file named main.c and put the following code in it:
#include "pen_bp.h"
#include <stdio.h> // for `printf`
int main() {
struct Pen p = {COLOR_RED, 1611515729966};
unsigned char s[BYTES_LENGTH_PEN] = {0};
// Encode p to buffer s.
EncodePen(&p, s);
// Decode buffer s to p1.
struct Pen p1 = {};
DecodePen(&p1, s);
// Format p1 to buffer buf.
char buf[64] = {0};
JsonPen(&p1, buf);
printf("%s", buf);
return 0;
}
In the code above, we firstly create a p of type struct Pen with data initilization,
then call a function EncodePen to encode p into buffer s. The length of buffer s
is generated by compiler as a macro defined as BYTES_LENGTH_PEN.
In the decoding part, we construct another p1 instance of type struct Pen with zero
initilization, then call a function DecodePen to decode bytes from buffer s into p1.
Finally, use a function JsonPen generated by the compiler to format the structure p1
to json string to checkout if the decoding works ok.
Let’s compile it with the C library bitproto.c and generated pen_bp.c, and run:
$ cc main.c bitproto.c pen_bp.c -o main
$ ./main
{"color":1,"produced_at":1611515729966}
The encoder and decoder copy bits between the structure’s memory and buffer s byte-to-byte,
the whole call allocates memory on the stack without any dynamic allocations.
There’s another larger example source code on the github.
Naming Prefix¶
As we know, there’s no namespace mechanism to scope definition names across including header files in C.
Bitproto provides an option to add a name prefix to all generated types. To use it, define an option
at the global scope of the bitproto file:
option c.name_prefix = "my_prefix_"
Run the bitproto compiler again, we will that names in pen_bp.h are changed:
The
enum Coloris now mapped toMyPrefixColor.The
Timestampis now mapped toMyPrefixTimestamp.The
message Penis now mapped tostruct MyPrefixPen.