google的protobuf简单介绍

google的protobuf是一种轻便高效的结构化数据存储格式,在通信协议和数据存储等领域中使用比较多。protobuf对于结构中的每个成员,会提供set系列函数和get系列函数。

但是,对于使用来说,需要根据传入的参数考虑需要调用的函数名,在使用这个比较多的情况,还是会让人觉得有些麻烦。而且,对于有些使用,例如之后打算不再使用protobuf,改为直接将数据压入内存段(The raw in-memory data structures sent/saved in binary form),或者直接压入内存段的方式,改为使用protobuf,那么,如果两者都是通过传入函数的方式来进行数据设置,或者数据解析,那么改动内容会比较少,而且出错几率也会比较低。那么如何实现呢?

先给出proto文件:

syntax = "proto2";

package student;

message Student
{
    required string name = 1;
    required int32 id = 2;
    optional int32 age = 3;
    optional string phoneNumber = 4;
}

下面给出通过函数重载方式,来处理各种参数类型的方式:

#include <google\protobuf\message.h>

namespace goo_proto = ::google::protobuf;

template <typename Param>
struct ProtoFunc
{
    static constexpr void* SetValueFunc = nullptr;
};

template <>
struct ProtoFunc<goo_proto::int32>
{
    static constexpr auto SetValueFunc = &(goo_proto::Reflection::SetInt32);
};

template <>
struct ProtoFunc<goo_proto::int64>
{
    static constexpr auto SetValueFunc = &(goo_proto::Reflection::SetInt64);
};

template <>
struct ProtoFunc<std::string>
{
    static constexpr auto SetValueFunc = &(goo_proto::Reflection::SetString);
};

template <>
struct ProtoFunc<const char*>
{
    static constexpr auto SetValueFunc = &(goo_proto::Reflection::SetString);
};

template <typename ValueType>
void SetFieldValue(goo_proto::Message* msg, const goo_proto::Reflection* reflection,
    const goo_proto::FieldDescriptor* field, ValueType&& value)
{
    (reflection->*(ProtoFunc<std::remove_cv_t<std::decay_t<ValueType>>>::SetValueFunc))
        (msg, field, std::forward<ValueType>(value));
}

通过上述的模板,就可以调用SetFieldValue来处理int32, int64, std::string和const char*了,这个是实现泛型函数的前期准备。

下面给出一次设置多个参数的方式:

template <typename ...Args>
void SetFieldAllValues(goo_proto::Message* msg, Args&&... args)
{
    auto descriptor = msg->GetDescriptor();
    auto reflection = msg->GetReflection();
    SetFieldImpl<0>(msg, descriptor, reflection, std::forward<Args>(args)...);
}

template <size_t idx, typename T, typename ...Args>
void SetFieldImpl(goo_proto::Message* msg, const goo_proto::Descriptor* descriptor, const goo_proto::Reflection* reflection,
    T&& value, Args&&... args)
{
    auto field = descriptor->field(idx);
    SetFieldValue(msg, reflection, field, std::forward<T>(value));
    SetFieldImpl<idx + 1>(msg, descriptor, reflection, std::forward<Args>(args)...);
}

template <size_t idx>
void SetFieldImpl(goo_proto::Message* msg, const goo_proto::Descriptor* descriptor, const goo_proto::Reflection* reflection)
{
    // empty
}

上面就是实现,设置所有proto结构体中元素的方式。多谢protobuf中提供了descriptor::field函数,通过这个函数,我才有办法比较简单的实现通过传入所有的参数(这里没有考虑设置repeat成员),来一次性设置好整个proto结构体对象。下面看一下使用上述函数的一个实例:

#include <iostream>
#include <string>
#include "studentinfo.h"
#include "studentinfo.pb.h"

int main(int argc, char* argv[])
{
    student::Student s;
    SetFieldAllValues(&s, "Jack", 10, 20, "11122233345");
    std::cout << s.name() << std::endl;
    std::cout << s.id() << std::endl;
    std::cout << s.age() << std::endl;
    std::cout << s.phonenumber() << std::endl;
    return 0;
}

这里只是提供了设置的函数(Set函数),没有提供Get函数,不过根据类似的方式,实现Get函数应该不是很困难,这里就不给出代码了。

猜你喜欢

转载自www.cnblogs.com/albizzia/p/9153252.html