A simple file, amessage.proto file following the language guide:
message AMessage { required int32 a=1; optional int32 b=2; }
Generate .h and .c files from the command-line in your current working directory:
protoc-c --c_out=. amessage.proto
Serialize/pack the AMessage as follows:
#include <stdio.h> #include <stdlib.h> #include "amessage.pb-c.h" int main (int argc, const char * argv[]) { AMessage msg = AMESSAGE__INIT; // AMessage void *buf; // Buffer to store serialized data unsigned len; // Length of serialized data if (argc != 2 && argc != 3) { // Allow one or two integers fprintf(stderr,"usage: amessage a [b]\n"); return 1; } msg.a = atoi(argv[1]); if (argc == 3) { msg.has_b = 1; msg.b = atoi(argv[2]); } len = amessage__get_packed_size(&msg); buf = malloc(len); amessage__pack(&msg,buf); fprintf(stderr,"Writing %d serialized bytes\n",len); // See the length of message fwrite(buf,len,1,stdout); // Write to stdout to allow direct command line piping free(buf); // Free the allocated serialized buffer return 0; }
I left most error handling out for brevity. Notice:
the use of
the A_MESSAGE__INIT macro to construct the message
the has_b member corresponds to the optional b field -- required fields do not have a has_ member.
amessage__get_packed_size returns the length of the packed data.
a_message__pack serializes the message.
On the other hand, to deserialize/unpack a message, try code like this:
#include <stdio.h> #include <stdlib.h> #include "amessage.pb-c.h" #define MAX_MSG_SIZE 1024 static size_t read_buffer (unsigned max_length, uint8_t *out) { size_t cur_len = 0; uint8_t c; while ((nread=fread(out + cur_len, 1, max_length - cur_len, stdin)) != 0) { cur_len += nread; if (cur_len == max_length) { fprintf(stderr, "max message length exceeded\n"); exit(1); } } return cur_len; }
int main (int argc, const char * argv[]) { AMessage *msg; // Read packed message from standard-input. uint8_t buf[MAX_MSG_SIZE]; size_t msg_len = read_buffer (MAX_MSG_SIZE, buf); // Unpack the message using protobuf-c. msg = amessage__unpack(NULL, msg_len, buf); if (msg == NULL) { fprintf(stderr, "error unpacking incoming message\n"); exit(1); } // display the message's fields. printf("Received: a=%d",msg->a); // required field if (msg->has_b) // handle optional field printf(" b=%d",msg->b); printf("\n"); // Free the unpacked message amessage__free_unpacked(msg, NULL); return 0; }
During linking each above program, make sure to include '-lprotobuf-c'
Test by piping one program into the next at command line:
./amessage_serialize 10 2 | ./amessage_deserialize Writing: 4 serialized bytes Received: a=10 b=2
Here is a simple file, cmessage.proto file:
message CMessage { repeated int32 c=1; }
Serialize/pack the CMessage as follows:
#include <stdio.h> #include <stdlib.h> #include "cmessage.pb-c.h" int main (int argc, const char * argv[]) { CMessage msg = CMESSAGE__INIT; // CMessage (repeated int32) void *buf; // Buffer to store serialized data unsigned len,i; // Length of serialized data msg.n_c = argc - 1; // Save number of repeated int32 msg.c = malloc (sizeof (int) * msg.n_c); // Allocate memory to store int32 for (i = 0; i < msg.n_c; i++) msg.c[i] = atoi (argv[i+1]); // Access msg.c[] as array len = cmessage__get_packed_size (&msg); // This is calculated packing length buf = malloc (len); // Allocate required serialized buffer length cmessage__pack (&msg, buf); // Pack the data fprintf(stderr,"Writing %d serialized bytes\n",len); // See the length of message fwrite (buf, len, 1, stdout); // Write to stdout to allow direct command line piping free (msg.c); // Free storage for repeated int32 free (buf); // Free serialized buffer return 0; }
I left most error handling out for brevity. Notice:
the use of the C_MESSAGE__INIT macro to construct the message
the n_XXX member is generated for a repeated field XXX.
On the other hand, if you want to deserialize/unpack a message, try code like this:
#include <stdio.h> #include "cmessage.pb-c.h" #define MAX_MSG_SIZE 4096 int main (int argc, const char * argv[]) { CMessage *msg; uint8_t buf[MAX_MSG_SIZE]; size_t msg_len = read_buffer (MAX_MSG_SIZE, buf); msg = cmessage__unpack (NULL, len, buf); // Deserialize the serialized input if (msg == NULL) { // Something failed fprintf(stderr, "error unpacking incoming message\n"); return 1; } for (i = 0; i < msg->n_c; i++) { // Iterate through all repeated int32 if (i > 0) printf (", "); printf ("%d", msg->c[i]); } printf ("\n"); cmessage__free_unpacked(msg,NULL); // Free the message from unpack() return 0; }
Test by piping one program into the next at command line:
./cmessage_serialize 12 3 4 | ./cmessage_deserialize Writing: 6 serialized bytes 12, 3, 4
Here is a simple file, dmessage.proto file:
message Submessage { required int32 value=1; } message DMessage { required Submessage a=1; optional Submessage b=2; }
Here DMessage consists of one or two integers (a is one int and required; b is one int and optional).
#include <stdio.h> #include <stdlib.h> #include "dmessage.pb-c.h" int main (int argc, const char * argv[]) { DMessage msg = DMESSAGE__INIT; // DMESSAGE Submessage sub1 = SUBMESSAGE__INIT; // SUBMESSAGE A Submessage sub2 = SUBMESSAGE__INIT; // SUBMESSAGE B void *buf; unsigned len; if (argc != 2 && argc != 3) { // Allow one or two integers fprintf(stderr,"usage: dmessage a [b]\n"); return 1; } sub1.value = atoi (argv[1]); msg.a = &sub1; // Point msg.a to sub1 data // NOTE: has_b is not required like amessage, therefore check for NULL on deserialze if (argc == 3) { sub2.value = atoi (argv[2]); msg.b = &sub2; } // Point msg.b to sub2 data len = dmessage__get_packed_size (&msg); // This is the calculated packing length buf = malloc (len); // Allocate memory dmessage__pack (&msg, buf); // Pack msg, including submessages fprintf(stderr,"Writing %d serialized bytes\n",len); // See the length of message fwrite (buf, len, 1, stdout); // Write to stdout to allow direct command line piping free(buf); // Free the allocated serialized buffer return 0; }
Notice:
there is no has_ flag for optional submessages -- if the pointer is non-NULL, then we assume that it is a value.
On the other hand, if you want to deserialize/unpack a message, try code like this:
#include <stdio.h> #include "dmessage.pb-c.h" #define MAX_MSG_SIZE 4096 int main (int argc, const char * argv[]) { DMessage *msg; // DMessage using submessages Submessage *sub1,*sub2;// Submessages char c; int i=0; // Data holders uint8_t buf[MAX_MSG_SIZE]; // Input data container for bytes while (fread(&c,1,1,stdin) != 0) { if (i >= MAX_MSG_SIZE) { fprintf(stderr,"message too long for allocated buffer\n"); return 1; } buf[i++] = c; } msg = dmessage__unpack(NULL,i,buf); // Deserialize the serialized input if (msg == NULL) { // Something failed fprintf(stderr,"error unpacking incoming message\n"); return 1; } sub1 = msg->a; sub2 = msg->b; printf("Received: a=%d",sub1->value); if (msg->b != NULL) printf(" b=%d",sub2->value); printf("\n"); dmessage__free_unpacked(msg,NULL); return 0; }
Test by piping one program into the next at command line:
./dmessage_serialize 4 5 | ./dmessage_deserialize Writing: 8 serialized bytes Received: a=4 b=5 ./dmessage_serialize 4 | ./dmessage_deserialize Writing: 4 serialized bytes Received: a=4
Here is a simple file, emessage.proto file:
message Submessage { required int32 value=1; } message EMessage { repeated Submessage a=1; }
Here DMessage consists of one or two integers (a is one int and required; b is one int and optional).
#include <stdio.h> #include <stdlib.h> #include "dmessage.pb-c.h" int main (int argc, const char * argv[]) { Submessage **subs; void *buf; unsigned len; subs = malloc (sizeof (Submessage*) * (argc-1)); for (i = 1; i < argc; i++) { subs[i-1] = malloc (sizeof (Submessage)); submessage__init (subs[i-1]); subs[i-1]->value = atoi(argv[i]); } msg.n_a = argc-1; msg.a = subs; len = emessage__get_packed_size (&msg); // This is the calculated packing length buf = malloc (len); // Allocate memory emessage__pack (&msg, buf); // Pack msg, including submessages fprintf(stderr,"Writing %d serialized bytes\n",len); // See the length of message fwrite (buf, len, 1, stdout); // Write to stdout to allow direct command line piping free(buf); // Free the allocated serialized buffer for (i = 1; i < argc; i++) free (subs[i]); free (subs); return 0; }
Notice that repeated fields create two fields, in this case n_a, the number of submessages, and a the submessages themselves. Also note that ais an array of pointers to messages.
On the other hand, if you want to deserialize/unpack a message, try code like this:
#include <stdio.h> #include "emessage.pb-c.h" #define MAX_MSG_SIZE 4096 int main (int argc, const char * argv[]) { EMessage *msg; char c; int i=0; uint8_t buf[MAX_MSG_SIZE]; // Input data container for bytes while (fread(&c,1,1,stdin) != 0) { if (i >= MAX_MSG_SIZE) { fprintf(stderr,"message too long for allocated buffer\n"); return 1; } buf[i++] = c; } msg = emessage__unpack(NULL,i,buf); // Deserialize the serialized input if (msg == NULL) { fprintf(stderr,"error unpacking incoming message\n"); return 1; } for (i = 0; i < msg->n_a; i++) printf ("%d\n", msg->a[i]->value); emessage__free_unpacked(msg,NULL); return 0; }
Test by piping one program into the next at command line:
./emessage_serialize 4 5 | ./emessage_deserialize Writing: 8 serialized bytes 4 5 ./emessage_serialize 4 5 8| ./emessage_deserialize 4 5 8