在C

2017-08-31 17 views
2

创建回调和结构为重复场的protobuf的消息中nanopb我有限定的原消息为:在C

message SimpleMessage { 
repeated int32 number = 1;} 

现在,编译后,场是pb_callback_t,我想编写功能。 (没有.options文件)

现在,在哪里以及该函数应包含哪些内容?数据本身存储在哪里以及如何访问数据/向其分配新数据?

*编辑*

根据@Groo的答案,这是我尝试的代码:

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

解码成功:17

后:常用3 [0] = 0

我该怎么办错了吗?

回答

1

您可以使用一些nanopb-specific proto flags强制nanopb生成静态分配数组的结构。

然而,nanopb的硫辛酸的默认行为是(对于整个列表一次)和解码(一旦为每个项目在列表)产生由nanopb编码期间调用的回调函数。这在低内存嵌入式系统中有时是首选,因为您不需要一次分配多个项目。

因此,对于您.proto文件:

message SimpleMessage { 
    repeated int32 number = 1; 
} 

你可能会得到这样的:

typedef struct _SimpleMessage { 
    pb_callback_t number; 
} SimpleMessage; 

意味着你必须创建自己的回调函数将被要求在连续的每个项目。

所以为了简单起见,假设你有一个简单的“变长”名单如下:

#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++; 
    } 
} 

显然,对于这样的一个例子,利用回调将没有任何意义,但它使例子简单。

编码回调必须遍历列表,并写出protobuf的标签,并在列表中的每个项的值:

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; 
} 

解码回调为每个项目调用一次,而“追加”到列表:

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; 
} 

有了这两个的地方,你一定要小心向右回调分配给正确的函数:

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对于每个具体的消息类型。如果需要,您的回调还可以将新的回调附加到嵌套的结构。

+0

它不起作用。将我的代码添加到原始问题中。 –

+0

@ItayPupko:你使用了错误的(解码)回调进行编码,应该有两个单独的回调(一个用于写入,一个用于读取)。我已经用编码和解码代码更新了我的答案。 – Groo

2

为了补充Groo的答案,这里是回答您的具体问题。

1。现在,这里又该功能包含哪些内容?

Groo提供的回调函数很好的解释。在nanopb库的network_server示例还使用回调和可以是有用的参考:network_server/server.cnetwork_server/client.c

2.哪里被存储数据本身?

只要你想! nanopb回调的重点在于它可以让您充分灵活地决定如何存储数据。在某些情况下,您可能想要即时处理数据,而不是将其存储在任何地方。

例如,上面的network_server示例从文件系统获取文件名,并将它们直接发送到网络 - 这样它可以处理任何数量的文件,而不需要太多内存。

3.如何访问/分配新的数据呢?

现在,这是回调的负面影响 - 你必须实现自己的接入和分配功能无论您使用的存储空间。这就是为什么最常见的情况下,静态分配(固定最大大小)或动态分配(需要内存量)更方便。

+0

酷,这是直接从开发人员。 +1 – Groo