2

在 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>?

不幸的是,这显然行不通。

我还将在此处复制粘贴写作范例: from 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() 函数可以让我们使用扩展的标识符来获取它的扩展。

4

1 回答 1

1

如果我正确理解了这个问题,您希望将构建的扩展对象的映射存储在映射中,以便在解析消息后访问。

在这种情况下, boost、poco 库等中有变体任何类型。您可以将键类型固定(即枚举类型或字符串)并将值类型设置为变体类型:

#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();
}

编译@coliru

更新:带有动物通用界面的版本 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();
}
于 2013-11-14T10:54:09.503 回答