在C

2017-08-31 116 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