• 使用反射处理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 <googleprotobufmessage.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函数应该不是很困难,这里就不给出代码了。

  • 相关阅读:
    【转】js 获取浏览器高度和宽度值(多浏览器)
    Css相册
    微信公众号开发笔记2-自定义菜单
    微信公众号开发笔记1-获取Access Token
    【转】CSS选择器笔记
    【转】CSS浮动(float,clear)通俗讲解
    高云的jQuery源码分析笔记
    经典闭包例子详解
    执行控制——节流模式
    图片上下左右的无缝滚动的实现
  • 原文地址:https://www.cnblogs.com/albizzia/p/9153252.html
Copyright © 2020-2023  润新知