Added example on how to handle unions.
This commit is contained in:
17
example_unions/Makefile
Normal file
17
example_unions/Makefile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
CFLAGS=-ansi -Wall -Werror -I .. -g -O0
|
||||||
|
DEPS=../pb_decode.c ../pb_decode.h ../pb_encode.c ../pb_encode.h ../pb.h
|
||||||
|
|
||||||
|
all: encode decode
|
||||||
|
./encode 1 | ./decode
|
||||||
|
./encode 2 | ./decode
|
||||||
|
./encode 3 | ./decode
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f encode unionproto.pb.h unionproto.pb.c
|
||||||
|
|
||||||
|
%: %.c $(DEPS) unionproto.pb.h unionproto.pb.c
|
||||||
|
$(CC) $(CFLAGS) -o $@ $< ../pb_decode.c ../pb_encode.c unionproto.pb.c
|
||||||
|
|
||||||
|
unionproto.pb.h unionproto.pb.c: unionproto.proto ../generator/nanopb_generator.py
|
||||||
|
protoc -I. -I../generator -I/usr/include -ounionproto.pb $<
|
||||||
|
python ../generator/nanopb_generator.py unionproto.pb
|
||||||
92
example_unions/decode.c
Normal file
92
example_unions/decode.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/* This program reads a message from stdin, detects its type and decodes it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <pb_decode.h>
|
||||||
|
#include "unionproto.pb.h"
|
||||||
|
|
||||||
|
/* This function reads manually the first tag from the stream and finds the
|
||||||
|
* corresponding message type. It doesn't yet decode the actual message.
|
||||||
|
*
|
||||||
|
* Returns a pointer to the MsgType_fields array, as an identifier for the
|
||||||
|
* message type. Returns null if the tag is of unknown type or an error occurs.
|
||||||
|
*/
|
||||||
|
const pb_field_t* decode_unionmessage_type(pb_istream_t *stream)
|
||||||
|
{
|
||||||
|
pb_wire_type_t wire_type;
|
||||||
|
int tag;
|
||||||
|
bool eof;
|
||||||
|
|
||||||
|
while (pb_decode_tag(stream, &wire_type, &tag, &eof))
|
||||||
|
{
|
||||||
|
if (wire_type == PB_WT_STRING)
|
||||||
|
{
|
||||||
|
const pb_field_t *field;
|
||||||
|
for (field = UnionMessage_fields; field->tag != 0; field++)
|
||||||
|
{
|
||||||
|
if (field->tag == tag && (field->type & PB_LTYPE_SUBMESSAGE))
|
||||||
|
{
|
||||||
|
/* Found our field. */
|
||||||
|
return field->ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wasn't our field.. */
|
||||||
|
pb_skip_field(stream, wire_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decode_unionmessage_contents(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
|
||||||
|
{
|
||||||
|
pb_field_t field = {}; /* NB: Could get rid of this wrapper by fixing issue #2. */
|
||||||
|
field.ptr = fields;
|
||||||
|
|
||||||
|
return pb_dec_submessage(stream, &field, dest_struct);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
/* Read the data into buffer */
|
||||||
|
uint8_t buffer[512];
|
||||||
|
size_t count = fread(buffer, 1, sizeof(buffer), stdin);
|
||||||
|
pb_istream_t stream = pb_istream_from_buffer(buffer, count);
|
||||||
|
|
||||||
|
const pb_field_t *type = decode_unionmessage_type(&stream);
|
||||||
|
bool status = false;
|
||||||
|
|
||||||
|
if (type == MsgType1_fields)
|
||||||
|
{
|
||||||
|
MsgType1 msg = {};
|
||||||
|
status = decode_unionmessage_contents(&stream, MsgType1_fields, &msg);
|
||||||
|
printf("Got MsgType1: %d\n", msg.value);
|
||||||
|
}
|
||||||
|
else if (type == MsgType2_fields)
|
||||||
|
{
|
||||||
|
MsgType2 msg = {};
|
||||||
|
status = decode_unionmessage_contents(&stream, MsgType2_fields, &msg);
|
||||||
|
printf("Got MsgType2: %s\n", msg.value ? "true" : "false");
|
||||||
|
}
|
||||||
|
else if (type == MsgType3_fields)
|
||||||
|
{
|
||||||
|
MsgType3 msg = {};
|
||||||
|
status = decode_unionmessage_contents(&stream, MsgType3_fields, &msg);
|
||||||
|
printf("Got MsgType3: %d %d\n", msg.value1, msg.value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
printf("Decoding failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
85
example_unions/encode.c
Normal file
85
example_unions/encode.c
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/* This program takes a command line argument and encodes a message in
|
||||||
|
* one of MsgType1, MsgType2 or MsgType3.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <pb_encode.h>
|
||||||
|
#include "unionproto.pb.h"
|
||||||
|
|
||||||
|
/* This function is the core of the union encoding process. It handles
|
||||||
|
* the top-level pb_field_t array manually, in order to encode a correct
|
||||||
|
* field tag before the message. The pointer to MsgType_fields array is
|
||||||
|
* used as an unique identifier for the message type.
|
||||||
|
*/
|
||||||
|
bool encode_unionmessage(pb_ostream_t *stream, const pb_field_t messagetype[], const void *message)
|
||||||
|
{
|
||||||
|
const pb_field_t *field;
|
||||||
|
for (field = UnionMessage_fields; field->tag != 0; field++)
|
||||||
|
{
|
||||||
|
if (field->ptr == messagetype)
|
||||||
|
{
|
||||||
|
/* This is our field, encode the message using it. */
|
||||||
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return pb_encode_submessage(stream, messagetype, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Didn't find the field for messagetype */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s (1|2|3)\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t buffer[512];
|
||||||
|
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
bool status = false;
|
||||||
|
int msgtype = atoi(argv[1]);
|
||||||
|
if (msgtype == 1)
|
||||||
|
{
|
||||||
|
/* Send message of type 1 */
|
||||||
|
MsgType1 msg = {42};
|
||||||
|
status = encode_unionmessage(&stream, MsgType1_fields, &msg);
|
||||||
|
}
|
||||||
|
else if (msgtype == 2)
|
||||||
|
{
|
||||||
|
/* Send message of type 2 */
|
||||||
|
MsgType2 msg = {true};
|
||||||
|
status = encode_unionmessage(&stream, MsgType2_fields, &msg);
|
||||||
|
}
|
||||||
|
else if (msgtype == 3)
|
||||||
|
{
|
||||||
|
/* Send message of type 3 */
|
||||||
|
MsgType3 msg = {3, 1415};
|
||||||
|
status = encode_unionmessage(&stream, MsgType3_fields, &msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unknown message type: %d\n", msgtype);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Encoding failed!\n");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fwrite(buffer, 1, stream.bytes_written, stdout);
|
||||||
|
return 0; /* Success */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
30
example_unions/unionproto.proto
Normal file
30
example_unions/unionproto.proto
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// This is an example of how to handle 'union' style messages
|
||||||
|
// with nanopb, without allocating memory for all the message types.
|
||||||
|
//
|
||||||
|
// There is no official type in Protocol Buffers for describing unions,
|
||||||
|
// but they are commonly implemented by filling out exactly one of
|
||||||
|
// several optional fields.
|
||||||
|
|
||||||
|
message MsgType1
|
||||||
|
{
|
||||||
|
required int32 value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MsgType2
|
||||||
|
{
|
||||||
|
required bool value = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MsgType3
|
||||||
|
{
|
||||||
|
required int32 value1 = 1;
|
||||||
|
required int32 value2 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UnionMessage
|
||||||
|
{
|
||||||
|
optional MsgType1 msg1 = 1;
|
||||||
|
optional MsgType2 msg2 = 2;
|
||||||
|
optional MsgType3 msg3 = 3;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user