Reflection in protobuf (C++/Java)
source link: https://forrestsu.github.io/posts/library/reflection-in-protobuf-cpp-java/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
最近工作中,需要做一些消息动态解析,因为使用的 protobuf,考虑使用protobuf的反射特性。
1 reflection in C++
在c++中使用protobuf 反射
package com.sunquan;
message Login
{
optional int64 userid = 1;
optional string username = 2; // name
optional string password = 3; // passwd
optional string email = 4;
optional string nickname = 5;
// etc ...
}
C++和Java 不同的是: c++有一个全局的pool,管理了所有定义在 proto 文件里的消息原型, 我们可以通过消息全称,查找到对应的单例的消息原型,然后通过原型构造可变的消息。
/**
* 通过消息名称, 获取构造该类型的默认(原型)。 然后你可以调用该消息的 New() 方法来构造这种类型的可变消息。
* @param FullMsgName eg: com.sunquan.Login
* @return if fail return NULL
*/
const google::protobuf::Message* ParseUtil::FindMsgProtoType(const std::string& FullMsgName)
{
google::protobuf::Message* pFactory = NULL;
auto pDesc = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(FullMsgName);
if (pDesc)
{
// Calling this method twice with the same Descriptor returns the same object.
pFactory = google::protobuf::MessageFactory::generated_factory()->GetPrototype(pDesc);
} else
std::cerr << "can't find new desc:" << FullMsgName << std::endl;
return pFactory;
}
当我们通过消息原型new一个可变的消息对象,就可以对消息进行动态赋值了,如下:
int ParseUtil::ReflectionTest() {
std::string fullname = "com.sunquan.Login";
const google::protobuf::Message* pMsgFactory = FindMsgProtoType(fullname);
if (pMsgFactory == NULL){
std::cerr << "CreateMessage() fail!" <<std::endl;
return 0;
}
//new mutable msg
google::protobuf::Message* pMsg = pMsgFactory->New();
const google::protobuf::Descriptor *pdesc = pMsgFactory->GetDescriptor();
const google::protobuf::Reflection *refl = pMsgFactory->GetReflection();
// 动态查找一个 FieldDesc
std::string fieldname = "username";
const google::protobuf::FieldDescriptor* pfield = pdesc->FindFieldByName(fieldname);
if (pfield) {
// 暂不支持 repeat 类型
if (pfield->is_repeated()) {
std::cerr << "repeat field is not support!" << fieldname << std::endl;
continue;
}
switch (pfield->cpp_type()) {
case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT32:
//if(oneRow[i].type() == QVariant::Date)
refl->SetInt32(pMsg, pfield, 100);
break;
case google::protobuf::FieldDescriptor::CppType::CPPTYPE_INT64:
refl->SetInt64(pMsg, pfield, 100LL);
break;
case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT32:
refl->SetUInt32(pMsg, pfield, 100);
break;
case google::protobuf::FieldDescriptor::CppType::CPPTYPE_UINT64:
refl->SetUInt64(pMsg, pfield, 100ULL);
break;
case google::protobuf::FieldDescriptor::CppType::CPPTYPE_DOUBLE:
refl->SetDouble(pMsg, pfield, 100.89);
break;
case google::protobuf::FieldDescriptor::CppType::CPPTYPE_FLOAT:
refl->SetFloat(pMsg, pfield, 100.98);
break;
case google::protobuf::FieldDescriptor::CppType::CPPTYPE_BOOL:
refl->SetBool(pMsg, pfield, true);
break;
case google::protobuf::FieldDescriptor::CppType::CPPTYPE_STRING:
refl->SetString(pMsg, pfield, "name");
break;
default:
//NoSupport CPPTYPE_ENUM/CPPTYPE_MESSAGE
std::cout << "UnSupport Type=" << pfield->type_name() << std::endl;
break;
}
}
//if no longer use, please remember delete "pMsg"
if (pMsg) delete pMsg;
return 0;
}
2 reflection in Java
在Java中使用的protobuf反射。 1 已知消息名称,查找到该消息类型的 Descriptor,然后根据序列化的字节码,动态构建对象。
public static void Reflect_Parse()
{
Login req = Login.newBuilder()
.setUserid(100)
.setUsername("Alice")
.setEmail("[email protected]")
.setNickname("爱丽丝")
.setPassword(Digest.getStringMD5("A123456"))
.build();
ByteString data = req.toByteString();
try {
//根据消息名称查找 Descriptor, DynamicMessage工厂 根据指定的 Descriptor 产生消息 object
String magname = "Login";
Descriptor desc = DemoProtos.getDescriptor().findMessageTypeByName(magname);
if (desc != null) {
// com.sunquan.zmqproto.Login
System.out.println(desc.getFullName());
DynamicMessage msg = DynamicMessage.parseFrom(desc, data);
System.out.println(msg.toString());
} else {
System.out.println("Can't find msg desc: " + magname);
}
//底层的实现: 每个消息自身都实现了 自己的 getDescriptor() 接口
Descriptor desc_direct = Login.getDescriptor();
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
}
2 预先准备好所有可能的消息,动态解析 把所有可能的消息的 Descriptor 加入到 Map,然后从Map中查找。
private Map<MsgType, Descriptor> typeMap = new TreeMap<MsgType, Descriptor>();
typeMap.put(MsgType.LOGINREP, LoginRep.getDescriptor());
typeMap.put(MsgType.INSERTORDERREP, InsertOrderRep.getDescriptor());
typeMap.put(MsgType.CANCELORDERREP, CancelOrderRep.getDescriptor());
3 动态修改消息字段值 wait
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK