[英]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();
}
更新: 版本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.