在C ++中,如何创建一个值为protobuf扩展标识符的映射

时间:2013-11-14 10:45:03

标签: c++ inheritance map key protocol-buffers

在protobuf中,我们有几种实现继承的选项。 “嵌套扩展”就是其中之一: http://www.indelible.org/ink/protobuf-polymorphism/

这里有趣的是如何读取序列化文件。我们必须创建一个Map以将Animal.type与其扩展标识符相对应,以便将动物投射到正确的Dog或Cat中。但是,在上面网站提供的示例中,使用的语言是Python。这意味着可以初始化Map而无需指定键的类型或值的类型。它运作良好:

# Unpack the serialized bytes.
animal = Animal()
animal.ParseFromString(bytes)

# Determine the appropriate extension type to use.
extension_map = { Animal.Cat: Cat.animal, Animal.Dog: Dog.animal }
extension = animal.Extensions[extension_map[animal.type]]

但是,为了在C ++中实现这样的映射,密钥类型和值类型是必需的。那么,我应该使用什么类型的值来将两个不同的扩展标识符存储到同一个地图中?

Map<Animal::Type, ::google::protobuf::internal::ExtensionIdentifier>

不幸的是,这显然不起作用。

我还要在这里复制粘贴写作范例:  来自animals_pb2 import *

# Construct the polymorphic base message type.
animal = Animal()
animal.type = Animal.Cat

# Create the subclass type by referencing the appropriate extension type.
# Note that this uses the self-referential field (Cat.animal) from within
# nested message extension.
cat = animal.Extensions[Cat.animal]
cat.declawed = True

# Serialize the complete message contents to a string.  It will end up
# looking roughly like this: [ type [ declawed ] ]
bytes = animal.SerializeToString()

Extensions()函数可以使我们使用扩展名的标识符来获取其扩展名。

1 个答案:

答案 0 :(得分:0)

如果我正确理解了问题,您希望在地图中存储构造的扩展对象的地图,以便在解析消息后有权访问。

在这种情况下,提升中有variantany类型,poco libraries等等。您可以将键类型固定(即枚举类型或字符串),将值类型设置为变体类型:

#include <boost/any.hpp>
#include <map>
#include <iostream>
#include <string>
#include <memory>

struct Animal {
    std::string type;
};

struct Dog : public Animal {
    constexpr static const char* TYPE = "Dog";
    void bark() const {
        std::cout<<"bow"<<std::endl;
    }
};

struct Cat : public Animal {
    constexpr static const char* TYPE = "Cat";
    void meow() const {
        std::cout<<"meow"<<std::endl;
    }
};

从消息中获取扩展名的工厂:

template <typename Animal,typename Message>
std::unique_ptr<Animal> get_extension(Message m) {
    try {
        Animal a( boost::any_cast<Animal>(m[Animal::TYPE]) );
        //for simplicity
        return std::unique_ptr<Animal>(new Animal(a));
    } catch (...) {
        return std::unique_ptr<Animal>();
    }    
}

和用法:

int main()
{
    // simulation of a protobuf message
    std::map<std::string,boost::any> m;
    m[Dog::TYPE] = Dog();
    m[Cat::TYPE] = Cat();

    // the generic interface to the message extensions
    auto dog = get_extension<Dog>(m);
    if (dog)
        dog->bark();
    auto cat = get_extension<Cat>(m);
    if (cat)
        cat->meow();
}

compile@coliru

使用动物的通用界面更新:version 2

实际上可能的任务是您有一条带扩展名的消息,并且您想要动态创建对象。在第二个版本中,您还可以通过相同的界面“与动物交谈”:

struct Animal {
    virtual void speak() const = 0;
    virtual ~Animal(){}
};

struct Dog : public Animal {
    constexpr static const char* TYPE = "Dog";
    virtual void speak() const {
        std::cout<<"bow"<<std::endl;
    }
};
// ... etc

一个简单的类型反序列化动物工厂:

struct AnimalRegistry {
    std::map<std::string , std::function<std::unique_ptr<Animal>()>> creators;

    template<typename A>
    void register_creator() {
        creators[A::TYPE] = []() { return std::unique_ptr<Animal>(new A); };
    }

    template<typename Message>
    std::unique_ptr<Animal> create(Message m) {
        return creators[m.animal_type]();
    }
};

,用法略有不同:

int main()
{
    AnimalRegistry registry;
    registry.register_creator<Dog>();
    registry.register_creator<Cat>();

    Message received_message { "Dog" /*this is a part of your protobuf*/ };

    auto dog = registry.create(received_message);
    if (dog)
        dog->speak();

    Message another_message { "Cat" };
    auto cat = registry.create(another_message);
    if (cat)
        cat->speak();
}