为c中nanopb中的protobuf消息中的重复字段创建回调和结构 [英] creating callbacks and structs for repeated field in a protobuf message in nanopb in c

查看:534
本文介绍了为c中nanopb中的protobuf消息中的重复字段创建回调和结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个原始消息定义为:

I have a proto message defined as:

message SimpleMessage {
repeated int32 number = 1;}

现在,编译后,该字段是 pb_callback_t 并且我想编写该函数.(没有 .options 文件)

now, after compiling, the field is of pb_callback_t and I suppose to write that function. (without .options file)

现在,函数应该包含在哪里以及包含什么?数据本身存储在哪里以及如何访问它/为其分配新数据?

now, where and what should the function contain? where does the data itself being stored and how can I access it/ assign new data to it?

* 编辑 *

根据@Groo 的回答,这是我试过的代码:

according to @Groo 's answer, this is the code I tried:

typedef struct {
    int numbers_decoded;
} DecodingState;

bool read_single_number(pb_istream_t *istream, const pb_field_t *field, void **arg)
{
    // get the pointer to the custom state
    DecodingState *state = (DecodingState*)(*arg);

    int32_t value;
    if (!pb_decode_varint32(istream, &value))
    {
        const char * error = PB_GET_ERROR(istream);
        printf("Protobuf error: %s", error);
        return false;
    }

    printf("Decoded successfully: %d", value);
    state->numbers_decoded++;

    return true;
}

int main(void) {
    int32_t arr[3] = {10, 22, 342};
    uint8_t buffer[128];
    size_t message_length;
    bool status;
    SimpleMessage simple = SimpleMessage_init_zero;

    printf("\nbefore : arr[0] = %d\n",arr[0]);

    // set the argument and the callback fn
    simple.number.arg = &arr;
    simple.number.funcs.decode = read_single_number;

    pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
    status = pb_encode(&ostream, SimpleMessage_fields, &simple);

    message_length = ostream.bytes_written;
    SimpleMessage simple1 = SimpleMessage_init_zero;
    simple = simple1;
    arr[0] = 0;
    pb_istream_t istream = pb_istream_from_buffer(buffer, message_length);
    // this function will call read_single_number several times
    status = pb_decode(&istream, SimpleMessage_fields, &simple);
    printf("\nafter : arr[0] = %d\n",arr[0]);

    return EXIT_SUCCESS;
}

输出为:

之前:arr[0] = 10

before : arr[0] = 10

解码成功:17

之后:arr[0] = 0

after : arr[0] = 0

我做错了什么?

推荐答案

您可以使用一些 nanopb 特定的 proto 标志强制 nanopb 生成具有静态分配数组的结构.

You can use some nanopb-specific proto flags to force nanopb to generate structs with statically allocated arrays.

然而,nanopb 的protogen 的默认行为是生成一个回调函数,在编码(整个列表一次)和解码(对每个项目在列表).这在低内存嵌入式系统中有时是首选,因为您不需要一次分配多个项目.

However, the default behavior of nanopb's protogen is to generate a callback function which is called by nanopb during encoding (once for the entire list) and decoding (once for each item in the list). This is sometimes preferred in low-memory embedded systems, because you don't need to allocate more than one item at a time.

因此,对于您的 .proto 文件:

So, for your .proto file:

message SimpleMessage {
    repeated int32 number = 1;
}

你可能会得到类似的信息:

You might get something like:

typedef struct _SimpleMessage {
    pb_callback_t number;
} SimpleMessage;

这意味着您必须创建自己的回调函数,该函数将为每个项目连续调用.

Meaning you will have to create your own callback function which will be called for each item in succession.

为了简单起见,假设您有一个简单的可变长度"列表,如下所示:

So for simplicity, let's say you have a simple "variable length" list like this:

#define MAX_NUMBERS 32

typedef struct
{
    int32_t numbers[MAX_NUMBERS];
    int32_t numbers_count;
}
IntList;

// add a number to the int list
void IntList_add_number(IntList * list, int32_t number)
{
    if (list->numbers_count < MAX_NUMBERS)
    {
        list->numbers[list->numbers_count] = number;
        list->numbers_count++;
    }
}

显然,对于这样的示例,使用回调没有任何意义,但它使示例变得简单.

Obviously, for such an example, using callbacks wouldn't make any sense, but it makes the example simple.

编码回调必须遍历列表,并为列表中的每一项写入protobuf标签和值:

Encoding callback must iterate through the list, and write the protobuf tag and the value for each item in the list:

bool SimpleMessage_encode_numbers(pb_ostream_t *ostream, const pb_field_t *field, void * const *arg)
{
    IntList * source = (IntList*)(*arg);

    // encode all numbers
    for (int i = 0; i < source->numbers_count; i++)
    {
        if (!pb_encode_tag_for_field(ostream, field))
        {
            const char * error = PB_GET_ERROR(ostream);
            printf("SimpleMessage_encode_numbers error: %s", error);
            return false;
        }

        if (!pb_encode_svarint(ostream, source->numbers[i]))
        {
            const char * error = PB_GET_ERROR(ostream);
            printf("SimpleMessage_encode_numbers error: %s", error);
            return false;
        }
    }

    return true;
}

解码回调为每个项目调用一次,并附加"到列表中:

Decoding callback is called once for each item, and "appends" to the list:

bool SimpleMessage_decode_single_number(pb_istream_t *istream, const pb_field_t *field, void **arg)
{
    IntList * dest = (IntList*)(*arg);

    // decode single number
    int64_t number;
    if (!pb_decode_svarint(istream, &number))
    {
        const char * error = PB_GET_ERROR(istream);
        printf("SimpleMessage_decode_single_number error: %s", error);
        return false;
    }

    // add to destination list
    IntList_add_number(dest, (int32_t)number);
    return true;
}

有了这两个,你必须小心地将正确的回调分配给正确的函数:

With these two in place, you must be careful to assign the right callback to the right function:

uint8_t buffer[128];
size_t total_bytes_encoded = 0;

// encoding
{
    // prepare the actual "variable" array
    IntList actualData = { 0 };
    IntList_add_number(&actualData, 123);
    IntList_add_number(&actualData, 456);
    IntList_add_number(&actualData, 789);

    // prepare the nanopb ENCODING callback
    SimpleMessage msg = SimpleMessage_init_zero;
    msg.number.arg = &actualData;
    msg.number.funcs.encode = SimpleMessage_encode_numbers;

    // call nanopb
    pb_ostream_t ostream = pb_ostream_from_buffer(buffer, sizeof(buffer));
    if (!pb_encode(&ostream, SimpleMessage_fields, &msg))
    {
        const char * error = PB_GET_ERROR(&ostream);
        printf("pb_encode error: %s", error);
        return;
    }

    total_bytes_encoded = ostream.bytes_written;
    printf("Encoded size: %d", total_bytes_encoded);
}

与解码类似:

// decoding
{
    // empty array for decoding
    IntList decodedData = { 0 };

    // prepare the nanopb DECODING callback
    SimpleMessage msg = SimpleMessage_init_zero;
    msg.number.arg = &decodedData;
    msg.number.funcs.decode = SimpleMessage_decode_single_number;

    // call nanopb
    pb_istream_t istream = pb_istream_from_buffer(buffer, total_bytes_encoded);
    if (!pb_decode(&istream, SimpleMessage_fields, &msg))
    {
        const char * error = PB_GET_ERROR(&istream);
        printf("pb_decode error: %s", error);
        return;
    }

    printf("Bytes decoded: %d", total_bytes_encoded - istream.bytes_left);
}

如果您的消息中有重复的结构,您的回调将不会使用nanopb 原始函数(如上面的 pb_decode_varint32),但对于每种具体的消息类型也是 pb_decode.如果需要,您的回调还可以将新的回调附加到这些嵌套结构.

If you have a repeated struct inside your message, your callback will not use nanopb primitive functions (like pb_decode_varint32 above), but again pb_decode for each concrete message type. Your callback can also attach new callbacks to those nested structs, if needed.

这篇关于为c中nanopb中的protobuf消息中的重复字段创建回调和结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆