Golang可以在C ++中做类似#define的事情吗?

时间:2018-07-12 15:02:57

标签: go macros polymorphism protocol-buffers

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

Message MsgType {
    string type; // indicate MsgA/ MsgB/ MsgC
};

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

|MsgType|MsgA/MsgB/MsgC|some end marker|

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

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)
    ...
}

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

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

这是问题:     由于每种情况都非常相似,所以我想做一些类似于C ++的事情

    <?php
    namespace App\DependencyInjection;

    use Symfony\Component\Config\FileLocator;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\DependencyInjection\Loader;
    use Symfony\Component\DependencyInjection\Extension\Extension;

    /**
     */
    class AppExtension extends Extension
    {
        /**
         * @see Symfony\Component\DependencyInjection\Extension.ExtensionInterface::load()
         */
        public function load(array $configs, ContainerBuilder $container)
        {
            $configuration = new Configuration();
            $config = $this->processConfiguration($configuration, $configs);

            $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../config'));
            $loader->load('services.yml');
            //$loader->load('parameters.yml');

            echo __DIR__.'/../Resources/DataFixtures';
            die();

            $fixture_loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/DataFixtures'));
            $fixture_loader->load('Patients.yml');

            $data_loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/datas'));
            //$data_loader->load('first_name.yml')
        }
    }

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

1 个答案:

答案 0 :(得分: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{}),
}

阅读消息时的常用代码:

typeToRead := registry[msgType.GetType()]

msg := reflect.New(typeToRead).Interface()
err := prto.Unmarshal(byteArrayforMsg, msg.(proto.Message))

注意: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) },
}

并使用它:

creator := registry[msgType.GetType()]

msg := creator()
err := prto.Unmarshal(byteArrayforMsg, msg)