Reputation: 141
I tried to know what package I got in my tcp socket, so I use protobuf.
But when I SerializeToString
my first protobuf class, the ParseFromString
method of an other protobuf class returns true.
The two classes are differents
Here are my .proto
syntax = "proto3";
package protobuf;
message Message
{
string content = 1;
}
message Player
{
int32 id = 1;
string name = 2;
}
Here is my c++ code
auto messageProto = new protobuf::Message;
messageProto->set_content("Hello");
std::string data;
messageProto->SerializeToString(&data);
protobuf::Player player;
if (player.ParseFromString(data))
{
qDebug() << "parse player";
}
protobuf::Message message2;
if (message2.ParseFromString(data))
{
qDebug() << "parse message";
}
Output :
parse player
parse message
Why ?
Upvotes: 1
Views: 1504
Reputation: 141
EDIT : So, now, is it the best approach ?
syntax = "proto3";
message Header
{
oneof payload
{
Message message = 1;
Player player = 2;
}
}
message Message
{
string content = 1;
}
message Player
{
int32 id = 1;
string name = 2;
}
When I write :
void SocketManager::sendData(Player& player)
{
Header header;
header.set_allocated_player(&player);
write(header);
}
void SocketManager::sendData(Message& message)
{
Header header;
header.set_allocated_message(&message);
write(header);
}
// etc... for each kind of message
When I read :
void read(const std::string& data)
{
protobuf::Header header;
header.ParseFromString(data);
switch (header.payload_case())
{
case protobuf::Header::kMessage:
emit messageProtoReceived(header.message());
break;
case protobuf::Header::kPlayer:
emit playerProtoReceived(header.player());
break;
case protobuf::Header::PAYLOAD_NOT_SET:
qDebug() << "Error, the payload isn't set, please create a header with a payload";
break;
}
}
Upvotes: 0
Reputation: 1063338
My recommended solution to the problem of multiple different payloads:
syntax = "proto3";
package protobuf;
message RenameMe // the outer payload wrapper
{
oneof payload
{
Foo foo = 1;
Bar bar = 2;
}
}
message Foo // one type of content
{
string content = 1;
}
message Bar // another type of content
{
int32 id = 1;
string name = 2;
}
Now you can just deserialize everything as a RenameMe
(naming is hard!) and check the payload
discriminated union enum to see how you should interpret the data. Then just access foo
or bar
respectively.
This approach is clear, obvious, and readily and effectively extensible into additional message types. The testing can be implemented with switch
in many programming languages. This style also works well with polymorphism in some environments - for example, in C# with protobuf-net that could be serialized/deserialized with:
[ProtoContract, ProtoInclude(1, typeof(Foo)), ProtoInclude(2, typeof(Bar))]
abstract class RenameMe {}
[ProtoContract]
class Foo : RenameMe {
[ProtoMember(1)] public string Content {get;set;}
}
[ProtoContract]
class Bar : RenameMe {
[ProtoMember(1)] public int Id {get;set;}
[ProtoMember(2)] public string Name {get;set;}
}
Upvotes: 1