golang 可以像 C++ 中的 #define 那样做类似的事情吗?

Cub*_*Mew 5 polymorphism macros go protocol-buffers

我使用 protobuf 定义了 3 种消息类型。(MsgA, MsgB, MsgC)

Message MsgA {
    string content;
    int64 A;
};
Message MsgB {
    string content;
    char B;
};
Message MsgC {
    string content;
    double C;
};
Run Code Online (Sandbox Code Playgroud)

我定义了一个 MsgType 来指示消息是 MsgA/MsgB/MsgC

Message MsgType {
    string type; // indicate MsgA/ MsgB/ MsgC
};
Run Code Online (Sandbox Code Playgroud)

然后,我生成了一些消息并以这种格式存储在内存映射文件中:

|MsgType|MsgA/MsgB/MsgC|some end marker|
Run Code Online (Sandbox Code Playgroud)

当我从缓冲区读取时,我想做类似的事情:

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)
    ...
}
Run Code Online (Sandbox Code Playgroud)

问题来了:既然每个案例都非常相似,我想做一些类似于C++的事情

#define CASE(MsgType)\
    case #MsgType:\
    msg := createObject<msgType>();\
    ...
switch type {
    CASE(MsgA);
    CASE(MsgB);
    CASE(MsgC);
}
Run Code Online (Sandbox Code Playgroud)

实际上有很多消息类型,不仅仅是A,B,C。每个案例部分都会有重复的代码。Go 中有什么方法可以做与 C++ 类似的事情吗?

icz*_*cza 4

有反思

您可以使用映射来存储从类型名称(例如MsgType.type字段)映射的类型描述符。类型描述符可以是reflect.Type

因此,您可以通过简单的映射查找来获取类型描述符,并且可以使用它reflect.New()来获取指向该类型的新的归零值的指针。

例如:

var registry = map[string]reflect.Type{
    "MsgA" : reflect.TypeOf(MsgA{}),
    "MsgB" : reflect.TypeOf(MsgB{}),
    "MsgC" : reflect.TypeOf(MsgC{}),
}
Run Code Online (Sandbox Code Playgroud)

以及读取消息时的通用代码:

typeToRead := registry[msgType.GetType()]

msg := reflect.New(typeToRead).Interface()
err := prto.Unmarshal(byteArrayforMsg, msg.(proto.Message))
Run Code Online (Sandbox Code Playgroud)

注意:msg将是静态类型interface{},并且它包装了一个指向您的消息类型之一的指针,例如,存储它的具体值的类型可能是*MsgA,正是您必须传递给的proto.Unmarshal()

带有构造函数

获取新消息值的另一种方法是使用构造函数,因此不需要反射。

它看起来是这样的:

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) },
}
Run Code Online (Sandbox Code Playgroud)

并使用它:

creator := registry[msgType.GetType()]

msg := creator()
err := prto.Unmarshal(byteArrayforMsg, msg)
Run Code Online (Sandbox Code Playgroud)

  • 也许使用构造函数而不是反射可以更清楚。 (2认同)