主要是nanopb中,optional可选、required必选、repeated重复字段的使用中,发现每个字段都需要在代码中手动处理一些东西,比如: 1.需要手动检查required字段是否有值,必选字段未赋值也不会报错; 2.给optional字段赋值后,需要手动给存在性检查变量has_fie赋值为true; 3.repeated重复字段在赋值后,需要手动赋值令field_count变量==实际数据个数;
syntax = "proto2"; import"nanopb.proto"; message SimpleMessage { required string name = 1 [(nanopb).max_size = 128]; optional int32 number = 2 [default = 2]; repeated int32 repeatID = 3 [(nanopb).max_count = 5]; }
typedef struct simpleMessage{ char name[128l; bool has_number; // 存在性检查变量 int32_t number; pb_size_t repeatID_count; // 数组实际数据个数 int32_t repeatID[5]; }SimpleMessage;
#include <stdio.h> #include <pb_encode.h> #include <pb_decode.h> #include "simple.pb.h" int main() { uint8_t buffer[128]; size_t message_length; bool status; /* Encode message */ { SimpleMessage message = SimpleMessage_init_zero; /* Create a stream that will write to our buffer. */ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); /* Fill in the message */ /*strncpy(message.name, "Lisa", sizeof(message.name)); message.number = 520;*/ message.repeatID[0] = 111; message.repeatID[1] = 222; message.repeatID[2] = 333; printf("send: Your name was %s!\n", message.name); printf("send: Your number was %d!\n", (int)message.number); printf("send: Your repeatID[1] was %d!\n\n", (int)message.repeatID[1]); /* Encode the message! */ status = pb_encode(&stream, SimpleMessage_fields, &message); message_length = stream.bytes_written; if (!status) { printf("Encoding failed: %s\n", PB_GET_ERROR(&stream)); return 1; } } /* But because we are lazy, we will just decode it immediately. */ { SimpleMessage message = SimpleMessage_init_zero; pb_istream_t stream = pb_istream_from_buffer(buffer, message_length); /* Decode the message. */ status = pb_decode(&stream, SimpleMessage_fields, &message); /* Check for errors */ if (!status) { printf("Decoding failed: %s\n", PB_GET_ERROR(&stream)); return 1; } printf("Recv: Your name was %s!\n", message.name); printf("Recv: Your number was %d!\n", (int)message.number); printf("Recv: Your repeatID[1] was %d!\n", (int)message.repeatID[1]); } return 0; }
Send: Your name was ! Send: Your number was 520! Send: Your repeatID[1] was 222! Recv: Your name was ! Recv: Your muber was 2! Recv: Your repeatID[1] was 0!
1、 required字段name并未赋值,编码和解码都未报错。 GDB调试中,在对应的encode库函数检测这个字段不是空,所以没报错”missing required field”。 原因是不同类型的required字段都有初始默认值,string默认为空“ ”、int32为0、bool默认为false。
在simple.proto中手动设置repeatID字段最大为5, 消息结构体中,该字段定义如下: typedef struct _SimpleMessage { pb_size_t repeatID_count; int32 repeatID[5]; } SimpleMessage; 其中,irepeatID_count表示repeated字段实际存了几个值。
在代码中给 repeatID[5]数组赋值后,如果没有手动给repeatID_count赋值,它默认为0,解码后该repeated字段全部为0。
3.消息结构体中,optional可选字段对应有has_fieldname变量需要手动赋值: 结构体中的存在性检查变量has_number表示可选字段number是否有值,代码中如果设值number= 123,但没有手动赋值“has_number = true”,解码后默认未设置过该可选字段,即用默认值0显示。
**感觉在nanopb中,“存储重复字段的数组实际大小”、“必选字段是否已经赋值”、“可选字段是否已经设值”这些情况,都需要手动去赋值或判断,和protobuf相比这样操作太麻烦了!** **不知道是由于nanopb是轻量化的,设计如此,还是我使用有误?
在使用 nanopb 时,确实需要手动处理一些细节,包括必选字段的检查、可选字段的存在性标志以及重复字段的计数。这些操作虽然繁琐,但都是为了保证数据在轻量级环境中的正确性和有效性。以下是详细的解答和改进建议:
required
在 nanopb 中,必选字段没有值时不会自动报错,需要手动检查。例如,你需要在编码前确保所有 required 字段都有正确的值。
optional
可选字段在赋值后需要手动设置存在性标志 (has_fieldname)。例如,赋值 number 字段后,需要手动设置 has_number:
has_fieldname
number
has_number
message.number = 520; message.has_number = true;
repeated
在赋值 repeated 字段后,需要手动设置计数器 field_count:
field_count
message.repeatID[0] = 111; message.repeatID[1] = 222; message.repeatID[2] = 333; message.repeatID_count = 3;
根据以上几点,对你的代码进行相应的修改:
#include <stdio.h> #include <pb_encode.h> #include <pb_decode.h> #include "simple.pb.h" int main() { uint8_t buffer[128]; size_t message_length; bool status; /* Encode message */ { SimpleMessage message = SimpleMessage_init_zero; /* Create a stream that will write to our buffer. */ pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); /* Fill in the message */ strncpy(message.name, "Lisa", sizeof(message.name) - 1); // Ensure null-termination message.name[sizeof(message.name) - 1] = '\0'; // Null-terminate message.number = 520; message.has_number = true; message.repeatID[0] = 111; message.repeatID[1] = 222; message.repeatID[2] = 333; message.repeatID_count = 3; // Set the count printf("send: Your name was %s!\n", message.name); printf("send: Your number was %d!\n", (int)message.number); printf("send: Your repeatID[1] was %d!\n\n", (int)message.repeatID[1]); /* Encode the message! */ status = pb_encode(&stream, SimpleMessage_fields, &message); message_length = stream.bytes_written; if (!status) { printf("Encoding failed: %s\n", PB_GET_ERROR(&stream)); return 1; } } /* Decode the message */ { SimpleMessage message = SimpleMessage_init_zero; pb_istream_t stream = pb_istream_from_buffer(buffer, message_length); /* Decode the message. */ status = pb_decode(&stream, SimpleMessage_fields, &message); /* Check for errors */ if (!status) { printf("Decoding failed: %s\n", PB_GET_ERROR(&stream)); return 1; } printf("Recv: Your name was %s!\n", message.name); printf("Recv: Your number was %d!\n", (int)message.number); printf("Recv: Your repeatID[1] was %d!\n", (int)message.repeatID[1]); } return 0; }
nanopb 的设计确实是为了在嵌入式环境中提供轻量级的 protobuf 支持,这意味着需要开发者在使用时进行更多的手动操作。这些手动操作虽然繁琐,但可以保证数据的正确性和内存的高效使用。在编码和解码过程中,务必注意必选字段的检查、可选字段的存在性标志以及重复字段的计数,确保所有数据都被正确处理。
希望这些改进能帮助你更好地使用 nanopb。如果还有其他问题,欢迎继续提问!