nanopb 的使用
这个文档写的还可以.
https://jpa.kapsi.fi/nanopb/docs/concepts.html#compiling-proto-files-for-nanopb
c 用法
定义一个 repeated 字段, 怎么进行 encode /decode
参考这个的 demo 进行修改
https://blog.csdn.net/fjlhlonng/article/details/131181948
我们添加一个 repeated 字段
syntax = "proto2";
message TestMessage
{
required string name = 1;
required uint32 age = 2;
optional fixed32 grade = 3;
repeated float data = 4; // add
}
不用 options 文件指定长度…
进行编译
可以看到编译出来的是 pb_callback_t 类型
/* Struct definitions /
typedef struct _TestMessage {
char name[32];
uint32_t age;
bool has_grade;
uint32_t grade;
pb_callback_t data;
/ @@protoc_insertion_point(struct:TestMessage) */
} TestMessage;
struct pb_callback_s {
/* New function signature, which allows modifying arg contents in callback. */
union {
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
} funcs;
/* Free arg for use by callback */
void *arg;
};
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "pb_decode.h"
#include "pb_encode.h"
#include "test.pb.h"
typedef unsigned char uint8_t;
/**
* Structure to be used with pb_encode_float_arr_cb & pb_decode_float_arr_cb
*/
typedef struct pb_float_arr_arg
{
/* Where to place the decoded float array */
float *arr;
/* Length of the float array */
uint8_t arr_len;
/* Array index used for decoding.
Must be initialized to 0 prior to calling pb_decode_float_arr_cb*/
uint8_t *arr_index;
} pb_float_arr_arg;
bool
pb_encode_float_arr_cb(pb_ostream_t *stream, const pb_field_t *field,
void *const *arg)
{
pb_float_arr_arg *arr = (pb_float_arr_arg*)*arg;
float *f_data = (float*)arr->arr;
if(!pb_encode_tag(stream, PB_WT_STRING, field->tag))
return false;
if(!pb_encode_varint(stream, arr->arr_len * 4))
return false;
for(int i = 0; i < arr->arr_len; i++)
{
if(!pb_encode_fixed32(stream, &f_data[i]))
return false;
}
return true;
}
bool
pb_decode_float_arr_cb(pb_istream_t *stream, const pb_field_t *field,
void **arg)
{
pb_float_arr_arg const *arr = (pb_float_arr_arg const*)*arg;
float *f_arr = arr->arr;
while(0 != stream->bytes_left)
{
if(*arr->arr_index >= arr->arr_len)
{
pb_skip_field(stream, PB_WT_32BIT);
}
else if(!pb_decode_fixed32(stream, &(f_arr[*arr->arr_index])))
return false;
(*arr->arr_index)++;
}
return true;
}
int main (int argc, char *argv[])
{
uint8_t buf[64];
size_t len;
TestMessage o = TestMessage_init_default;
TestMessage n = TestMessage_init_default;
pb_ostream_t o_stream = {0};
pb_istream_t i_stream = {0};
strcat(o.name, "Mike jordan");
o.age = 17;
// encode
float temp[4] ={1.5,2.8,3.7,4.5};
uint8_t arr_index = 0;
pb_float_arr_arg encode_arg = {.arr = (float *)temp, .arr_len = 4,
.arr_index = &arr_index};
o.data.funcs.encode = &pb_encode_float_arr_cb;
o.data.arg = &encode_arg;
o_stream = pb_ostream_from_buffer(buf, sizeof(buf));
pb_encode(&o_stream, TestMessage_fields, &o);
len = o_stream.bytes_written;
printf("len: %ld\n", len);
// decode
i_stream = pb_istream_from_buffer(buf, len);
float decode_args[4] = { 0 };
arr_index = 0;
pb_float_arr_arg arg = {.arr = decode_args, .arr_len = 4, .arr_index = &arr_index};
n.data = (struct pb_callback_s)
{.funcs.decode = &pb_decode_float_arr_cb, .arg = &arg};
pb_decode(&i_stream, TestMessage_fields, &n);
printf("name: %s\n", n.name);
printf("age: %u\n", n.age);
for(int i=0;i<4;i++)
printf("%f\n",decode_args[i]);
return 0;
}
运行下 结果如下:
$./start
len: 33
name: Mike jordan
age: 17
1.500000
2.800000
3.700000
4.500000
运行成功.
如果类型是 pb_callback_t 类型的话,就是这样操作。
如果不是pb_callback_t 类型的话, 直接赋值即可,比较简单
string 类型的解析
在 proto 文件添加类型
required string string_data = 4 ;
编译
/* Struct definitions /
typedef struct _TestMessage {
char name[32];
uint32_t age;
bool has_grade;
uint32_t grade;
pb_callback_t string_data;
/ @@protoc_insertion_point(struct:TestMessage) */
} TestMessage;
看到还是一个 pb_callback_t 类型.
要自己写个 encode/deconde 的函数
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "pb_decode.h"
#include "pb_encode.h"
#include "test.pb.h"
typedef struct pb_buffer_arg
{
/* Buffer to be written, or reference to read buffer */
void const *buf;
/* Length of buf */
size_t buf_len;
} pb_buffer_arg;
bool
pb_encode_string_cb(pb_ostream_t *stream, const pb_field_t *field,
void *const *arg)
{
pb_buffer_arg *data = (pb_buffer_arg*)*arg;
if(pb_encode_tag_for_field(stream, field) &&
pb_encode_string(stream, data->buf, data->buf_len))
return true;
return false;
}
bool
pb_decode_string_cb(pb_istream_t *stream, const pb_field_t *field,
void **arg)
{
pb_buffer_arg *data = (pb_buffer_arg*)*arg;
data->buf_len = stream->bytes_left;
data->buf = stream->state;
return pb_read(stream, (pb_byte_t*)NULL, stream->bytes_left);
}
int main (int argc, char *argv[])
{
uint8_t buf[64];
size_t len;
TestMessage o = TestMessage_init_default;
TestMessage n = TestMessage_init_default;
pb_ostream_t o_stream = {0};
pb_istream_t i_stream = {0};
strcat(o.name, "Mike jordan");
o.age = 17;
// encode
char const ecnode_string[] = "test string";
o.string_data.funcs.encode = pb_encode_string_cb;
o.string_data.arg = & ((pb_buffer_arg) {.buf = ecnode_string, .buf_len = sizeof (ecnode_string) });
o_stream = pb_ostream_from_buffer(buf, sizeof(buf));
pb_encode(&o_stream, TestMessage_fields, &o);
len = o_stream.bytes_written;
printf("len: %ld\n", len);
i_stream = pb_istream_from_buffer(buf, len);
//decode
pb_buffer_arg decode_string = {.buf = NULL, .buf_len = 0 };
n.string_data.arg = &decode_string;
n.string_data.funcs.decode = pb_decode_string_cb;
pb_decode(&i_stream, TestMessage_fields, &n);
printf("name: %s\n", n.name);
printf("age: %u\n", n.age);
printf("decode_string:%s , buf_len:%zu\n",decode_string.buf, decode_string.buf_len);
return 0;
}
运行结果
$./start
len: 29
name: Mike jordan
age: 17
decode_string:test string , buf_len:12
符合预期. 完成
这里有个困惑,decode_string 中的 buf 传进去的时候是为 NULL 的。
是在哪里分配了空间吗? 用 gdb 跑一下看下
gdb ./start --tui (加上 tui 参数,有图形界面,好看一点)
buf 地址确实是为空
跑完 decode 后,就有地址了。 说明分配空间了.
int 类型
像 int 这种基础的类似,操作比较简单,直接赋值即可。这里不在写
c++ 使用
先使用 protobuf 实验:
还是使用
syntax = "proto2";
message TestMessage
{
required string name = 1;
required uint32 age = 2;
optional fixed32 grade = 3;
}
//TestMessage.name max_size:32
先编译 test.proto
/home/xxx/protobuf-3.6.1/protobuf/bin/protoc -I=./ --cpp_out=./ test.proto
测试文件:
#include <iostream>
#include <string>
#include "test.pb.h"
using namespace std;
int main(void) {
GOOGLE_PROTOBUF_VERIFY_VERSION;
TestMessage t1;
t1.set_age(10);
t1.set_name("bob");
string serializedStr;
t1.SerializeToString(&serializedStr);
TestMessage t2;
if(!t2.ParseFromString(serializedStr)){
cout << "Failed to parse student." << endl;
return -1;
}
cout << t2.age() << endl;
cout << t2.name() << endl;
return 0;
}
编译测试文件
g++ -I /home/xxx/protobuf-3.6.1/protobuf/include test.pb.cc main.cpp -std=c++11 -L /home/xxx/protobuf-3.6.1/protobuf/lib -lprotobuf
导入环境变量:
export LD_LIBRARY_PATH=/home/xxx/protobuf-3.6.1/protobuf/lib
执行:
$./a.out
10
bob
正确…
SerializeToString : 将对象 序列化成二进制数据,保存到字符串中
如果要发送的话,则是发送这个 比如:
static void
send_config_req(ssc_connection *conn, sensor_uid const *suid)
{
string pb_req_msg_encoded;
string config_encoded;
sns_client_request_msg pb_req_msg;
sns_std_sensor_config config;
config.set_sample_rate(SAMPLE_RATE);
config.SerializeToString(&config_encoded);
pb_req_msg.set_msg_id(SNS_STD_SENSOR_MSGID_SNS_STD_SENSOR_CONFIG);
pb_req_msg.mutable_request()->set_payload(config_encoded);
pb_req_msg.mutable_suid()->set_suid_high(suid->high);
pb_req_msg.mutable_suid()->set_suid_low(suid->low);
pb_req_msg.mutable_susp_config()->
set_delivery_type(SNS_CLIENT_DELIVERY_WAKEUP);
pb_req_msg.mutable_susp_config()->
set_client_proc_type(SNS_STD_CLIENT_PROCESSOR_APSS);
pb_req_msg.mutable_request()->mutable_batching()->
set_batch_period(BATCH_PERIOD);
pb_req_msg.SerializeToString(&pb_req_msg_encoded);
conn->send_request(pb_req_msg_encoded);
}
贴上一个链接地址,写的还挺好的,挺详细的.
https://blog.csdn.net/lizhichao410/category_11936113.html