簡體   English   中英

在C ++中,如何創建一個值是protobuf擴展標識符的映射

[英]In C++, how to create a map whose value is a protobuf extension indentifier

在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()函數使我們能夠使用擴展名的標識符獲取其擴展名。

如果我正確理解了該問題,則希望將構造的擴展對象的映射存儲在映射中,以便在解析消息后可以訪問。

在這種情況下,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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM