自动填充std :: map

时间:2015-04-27 14:03:12

标签: c++

我有一个描述动物的XML,每个动物都有不同的参数,看起来像这样:

<Animals>
 <Cat>
    </fur>
    </taillength>
 </Cat>
 <Elephant>
    </earsize>
 </Elephant>
</Animals>

我有继承自的课程(猫,大象):

IAnimal
{
public: virtual IAnimal* CreateAnimal(xml) = 0 ;
}

所以每个班级都可以创造自己,这很棒。

问题是,某处(在某些工厂)我必须拥有以下代码:

string name = xml->getname();
if(name.equals("cat")
{
  cat.CreateAnimal(xml);
} else if (name.equals("elephant"))
{
  elephant.CreateAnimal(xml);
}

我想通过创建一个从String(cat / elephant)映射到解析此类型的Class(Cat:IAnimal,Elephant:IAnimal)来避免该代码

然后执行以下操作:

map<string, IAnimal>
// populate map ... 
// ...
string name = xml->getname();
mymap[name]->CreateAnimal(xml);

问题是自动填充地图,因此每个类都会在运行时自动添加到地图(可以使用C#中的静态构造函数完成)。

我很乐意听取有关如何操作的建议,谢谢

4 个答案:

答案 0 :(得分:2)

这是否符合您的需求?您可以在全局范围内使用宏REGISTER_ANIMAL(Class, xmlName)来注册名为Class的{​​{1}}动物的工厂方法。

xmlName

输出:

#include <iostream>
#include <map>

struct IAnimal;

using AnimalFactory = IAnimal *(*)(std::string const&);
std::map<std::string, AnimalFactory> gFactories;

struct AnimalReg{
    AnimalReg(std::string xmlName, AnimalFactory factory) {
        gFactories.emplace(xmlName, factory);
    }
};

#define CAT_(x, y) x ## y
#define CAT(x, y) CAT_(x, y)
#define REGISTER_ANIMAL(Class, xmlName) \
    static AnimalReg const CAT(_animalReg, __COUNTER__) {xmlName, &Class::create}

struct IAnimal {};

///////////
// Usage //
///////////

struct Cat : IAnimal {    
    static IAnimal *create(std::string const &xml) {
        std::cout << "Cat\n"; return nullptr;
    }
};
REGISTER_ANIMAL(Cat, "cat");

struct Dog : IAnimal {
    static IAnimal *create(std::string const &xml) {
        std::cout << "Dog\n"; return nullptr;
    }
};
REGISTER_ANIMAL(Dog, "dog");

int main() {
    gFactories["cat"]("");
    gFactories["dog"]("");
}

答案 1 :(得分:2)

您可以通过使IAnimal的子类使用字符串标识符和创建函数将其自身添加到工厂映射中来创建工厂方法注册系统。像这样:

struct IAnimal;
//I made this a Singleton for simplicity
struct AnimalFactory
{
    //the type of a factory method
    using FactoryFunction = std::function<IAnimal*(const std::string&)>;
    //register a factory function
    bool RegisterFunction(const std::string &name, FactoryFunction f)
    {
        factoryMap.insert(std::make_pair(name,f));
        //do some error handling to see if the class is already registered, etc.
        return true;
    }

    //do the actual construction
    std::unique_ptr<IAnimal> CreateAnimal(const std::string &name, const std::string &xml)
    {
        //retrieve the factory method from the map and call it
        return std::unique_ptr<IAnimal>(factoryMap.at(name)(xml));
    }

    //singleton implementation
    static AnimalFactory &instance()
    {
        static AnimalFactory factory{};
        return factory;
    }

private:
    std::map<std::string, FactoryFunction> factoryMap;
};

您的子类然后按如下方式注册:

struct Cat : IAnimal
{
    Cat (const std::string &xml) {}
    static Cat* CreateAnimal(const std::string &xml) { return new Cat(xml); }
    static bool registered;
};
bool Cat::registered = 
   AnimalFactory::instance().RegisterFunction("cat", Cat::CreateAnimal);

struct Elephant : IAnimal
{
    Elephant (const std::string &xml) {}
    static Elephant* CreateAnimal(const std::string &xml) { return new Elephant(xml); }
    static bool registered;
};
bool Elephant::registered = 
   AnimalFactory::instance().RegisterFunction("elephant", Elephant::CreateAnimal);

然后你调用这样的工厂方法:

auto cat = AnimalFactory::instance().CreateAnimal("cat","hi");
auto elephant = AnimalFactory::instance().CreateAnimal("elephant","hi");

这种方法有许多不同的方面。我强烈建议阅读Andrei Alexandrescu的“现代C ++设计”中的第8节“对象工厂”,以获得对此的建议和讨论。

Demo

答案 2 :(得分:0)

当我们不知道他们在哪里时,我们应该如何给你任何建议来添加你的地图?

一个建议就是手动添加它们

myMap["cat"] = new Cat;
myMap["elephant"] = new Elephant;

或使其灵活,并从配置文件或数据库或文件系统中读取。

顺便说一句: 也许你应该改变你的工厂只需要一个xml字符串然后检查它是cat / elephant的类型然后返回该类型

if (name == "cat")
{
  return new Cat;
}
else if (name == "elephant")
{
  return new Elephant;
}

这就是我认为的战略模式。

答案 3 :(得分:0)

使用Factory method pattern,而不是创建地图。

解决具体的解决方案,无论如何,您将根据动物名称(或类型)在某处创建子类实例的决策点。

以下是Factory方法的示例:

#include <string>
#include <iostream>
#include <vector>

using std::string;
using std::vector;

class XmlReader {}; // Somewhere XmlReader defined

class IAnimal
{
public:
    static IAnimal* CreateAnimal(const XmlReader* xml);

public:
    virtual ~IAnimal() {}
    virtual string GetSound() const = 0;
};

class Cat: public IAnimal
{
    string GetSound() const { return "Miaow"; }
};

class Elephant: public IAnimal
{
    string GetSound() const { return "Rouw"; }
};

IAnimal* IAnimal::CreateAnimal(const XmlReader* xml)
{
    string name = xml->getname();
    if(name == "cat")
        return new Cat(xml);
    else if (name == "elephant")
        return new Elephant(xml);
    else
        return NULL;
}

int main()
{
    XmlReader xml("animal.xml");
    vector<IAnimal*> zoo;
    while(!xml.eof())
    {
        IAnimal* animal = IAnimal::CreateAnimal(&xml);
        std::cout << animal->GetSound() << std::endl;
        zoo.push_back(animal);
    }
}