2015-04-27 4 views
1

У меня есть XML, который описывает животное, каждое животное имеет различные параметры, выглядит примерно так:Автоматически заполнять зЬй :: Карты

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

И у меня есть классы (Cat, слон), унаследованные из:

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: IAnimal, слон: IAnimal)

А затем делает следующее:

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

проблема заключается в том, чтобы автоматически заполнить карту, так что каждый класс будет dd сам во время выполнения автоматически на карту (что-то, что можно сделать с помощью статического конструктора в C#).

Я был бы рад услышать предложения в том, как это сделать, спасибо

+1

Ваш 'метод CreateAnimal' не может быть статическим, если это виртуальный, и не может быть чисто виртуальным (' = 0 '), если это не виртуальная. – Quentin

+0

Вы правы, Редактировал вопрос – OopsUser

+0

Статические конструкторы на самом деле не существуют в C++: http://stackoverflow.com/questions/1197106/static-constructors-in-c-need-to-initialize-private-static-objects – dwcanillas

ответ

0

Как мы должны дать вам какие-либо suggentions, чтобы добавить детали к вашей карте, когда горе не знаю, откуда они?

Одно предложение было бы просто добавить их вручную

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

или делает его гибким и прочитать, что из конфигурационного файла или базы данных или из файловой системы.

КСТАТИ: Может быть, вы должны изменить свой завод, чтобы взять только строку XML, то проверить, какой тип это кошка/слон, а затем вернуть этот тип

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

Это будет более шаблон стратегии я думаю.

2

Это соответствует вашим потребностям? Вы используете макрос REGISTER_ANIMAL(Class, xmlName) в глобальной области действия для регистрации фабричного метода для животного с именем 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"](""); 
} 

Выходов:

Cat 
Dog 
+0

Возможно, это можно сделать без макросов, где 'REGISTER_ANIMAL' был фактически методом шаблона:' register_animal (const string & name) '. – tadman

+0

@tadman Я так не думаю. Вы не можете вызвать метод из глобальной области, не так ли? Возможно, как часть инициализатора, но тогда вам все равно нужно объявить инициализированную переменную. – Quentin

+0

Вам нужно много танцевать с помощью умных конструкторов. То, как вы были здесь, существует уже несколько десятилетий, напоминает мне о [Borland OWL] (http://en.wikipedia.org/wiki/Object_Windows_Library), мне просто интересно, есть ли еще C++ 14 путь делать это. – tadman

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"); 

Есть целый ряд различных граней для этот подход. Я бы настоятельно рекомендовал прочитать раздел 8 «Объектные заводы» от «Современного дизайна C++» Андрея Александреску для предложений и обсуждения этого вопроса.

Demo

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); 
    } 
} 
Смежные вопросы