.. _quickstart-c-guide: 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: .. sourcecode:: bash $ bitproto c pen.bitproto Where the ``pen.bitproto`` is introduced in earlier section :ref:`quickstart-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 Color`` in bitproto is mapped to a ``typedef`` statement in C, and the enum values are mapped to macros: .. sourcecode:: c typedef uint8_t Color; // 3bit #define COLOR_UNKNOWN 0 #define COLOR_RED 1 #define COLOR_BLUE 2 #define COLOR_GREEN 3 * The ``Timestamp`` type in bitproto is mapped to a ``typedef`` in C: .. sourcecode:: c typedef int64_t Timestamp; // 64bit * The message ``Pen`` in bitproto is mapped to a ``struct`` in C: .. sourcecode:: c struct Pen { Color color; // 3bit Timestamp produced_at; // 64bit }; * The compiler also generates three functions, they are the encoder, decoder and json formatter. .. sourcecode:: c 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: .. sourcecode:: c #include "pen_bp.h" #include // 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: .. sourcecode:: bash $ 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: .. sourcecode:: bitproto option c.name_prefix = "my_prefix_" Run the bitproto compiler again, we will that names in ``pen_bp.h`` are changed: * The ``enum Color`` is now mapped to ``MyPrefixColor``. * The ``Timestamp`` is now mapped to ``MyPrefixTimestamp``. * The ``message Pen`` is now mapped to ``struct MyPrefixPen``.