最近工作中,需要做一些消息动态解析,因为使用的 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("Alice123@gmail.com")
.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
网友评论