Reputation: 653
I defined 3 Message types using protobuf. (MsgA, MsgB, MsgC)
Message MsgA {
string content;
int64 A;
};
Message MsgB {
string content;
char B;
};
Message MsgC {
string content;
double C;
};
and I defined a MsgType to indicate the message is MsgA/MsgB/MsgC
Message MsgType {
string type; // indicate MsgA/ MsgB/ MsgC
};
Then, I generated some messages and stored in a memory map file in this format:
|MsgType|MsgA/MsgB/MsgC|some end marker|
when I read from the buffer, I want to do something similar to this:
msgType := &MsgType{}
err := proto.Unmarshal(byteArrayforMsgType, msgType)
...
switch msgType.GetType() {
case "MsgA":
a := &MsgA{}
err := prto.Unmarshal(byteArrayforMsg, a)
...
case "MsgB":
b := &MsgB{}
err := prto.Unmarshal(byteArrayforMsg, b)
...
case "MsgC":
c := &MsgC{}
err := prto.Unmarshal(byteArrayforMsg, c)
...
}
Here is the question: Since each case is quite similar, I want to do something similar to C++
#define CASE(MsgType)\
case #MsgType:\
msg := createObject<msgType>();\
...
switch type {
CASE(MsgA);
CASE(MsgB);
CASE(MsgC);
}
Actually there are many message types, not just A,B,C. There will be repeating codes in each case section. Is there any method in Go to do the similar thing as C++?
Upvotes: 5
Views: 5696
Reputation: 418317
You could use a map where you store type descriptors mapped from type name (e.g. MsgType.type
field). The type descriptor may be reflect.Type
.
So you can get the type descriptor with a simple map lookup, and you can use reflect.New()
to get a pointer to a new, zeroed value of this type.
For example:
var registry = map[string]reflect.Type{
"MsgA" : reflect.TypeOf(MsgA{}),
"MsgB" : reflect.TypeOf(MsgB{}),
"MsgC" : reflect.TypeOf(MsgC{}),
}
And the common code when reading a message:
typeToRead := registry[msgType.GetType()]
msg := reflect.New(typeToRead).Interface()
err := prto.Unmarshal(byteArrayforMsg, msg.(proto.Message))
Note: msg
will be of static type interface{}
, and it wraps a pointer to one of your message types, e.g. the type of the concrete value stored it in may be *MsgA
, exactly what you have to pass to proto.Unmarshal()
.
Another way to get a new message value could be to use constructor functions, so no reflection will be needed.
This is how it would look like:
var registry = map[string]func() proto.Message{
"MsgA" : func() proto.Message { return new(MsgA) },
"MsgB" : func() proto.Message { return new(MsgB) },
"MsgC" : func() proto.Message { return new(MsgC) },
}
And using it:
creator := registry[msgType.GetType()]
msg := creator()
err := prto.Unmarshal(byteArrayforMsg, msg)
Upvotes: 4