自动构建从字符串到模板化工厂函数的映射

时间:2017-01-10 19:52:29

标签: c++ templates factory instantiation command-line-parsing

我希望能够根据选定的命令行参数实例化一组模板化 C ++对象。如果要实例化的类型(即Character类)没有模板化,下面的解决方案(由Is there a way to instantiate objects from a string holding their class name? 建议)将起作用。

同样重要的是:下面的代码允许Character的每个子类自行注册。这意味着用户可以添加Character的其他子类,而无需触及库中的任何代码或显式管理所有可能的子类型的列表。 问题:有没有办法为模板化类提供相同的功能?

main中解析命令行时,库的用户选择模板参数的值。这意味着CharacterDescription::make,因此,CharacterDescription类也必须模板化。此时,我无法弄清楚如何让每个Character子类“注册”自己而不维护所有可能子类的中心列表。下面makeCharacters的模板化版本几乎可以实现我的想法 - 我只想设置代码,以便makeCharacters不维护所有子类的列表。

(我怀疑答案需要一些#define魔法;但是,我没有看到怎么做。)

Character.hpp

// Base Class
class Character {

};

// Descriptor for subclasses that may be instantiated.
// (i.e., contains the parameters used for the option parser 
// as well as a pointer to a factory function)
class CharacterDescription {

  typedef Character* (*CharacterFactory)(const char* params);

public:
  const char* optionName;
  const CharacterFactory factory;

    CharacterDescription(const char* pOpt, const CharacterFactory pFactory) :
        optionName(pOpt), factory(pFactory) {
      characterList.push_back(*this);
    }

  template<typename T>
  static Character *make(const char *params) {
    return new T(params);
  }

  static vector<CharacterDescription> characterList;

};

Character.cpp

vector<CharacterDescription>  CharacterDescription::characterList;

Animal.hpp

// Example subclass that may be requested on the command line.
class Animal : public Character {

public:
  Animal(const char* /*params*/) {}

public:
  static CharacterDescription description;
};

Animal.cpp

CharacterDescription Animal::description("animal", CharacterDescription::make<Animal>);

的main.cpp

    // Yes, this is gross, but it gets the point across
    vector<Character*> makeCharacters(int argc, const char* argv[]){

      vector<Character*> characters;
      for (int i = 1;i < argc; i++) {
        const char* current = argv[i];
    auto foundItem = std::find_if(CharacterDescription::characterList.begin(),
    CharacterDescription::characterList.end(),
    [current](const CharacterDescription& item) {return strcmp(item.optionName,current)==0;});
    if (foundItem != CharacterDescription::characterList.end() ) {
      characters.push_back((*foundItem).factory(argv[i+1]));
    }
  }
  return characters;
}

makeCharacters的模板版

template<typename T>
vector<Character<T>*> makeCharacters(int argc, const char* argv[]){

  vector<CharacterDescription<T>> characterList;

  //
  // **Key question:**  Is there a way to distribute these lines of code 
  // to the .hpp or .cpp file for each subclass?
  //
  characterList.push_back({"animal", CharacterDescription<T>::template make<Animal<T>>});
  characterList.push_back({"tree", CharacterDescription<T>::template make<Tree<T>>});
  characterList.push_back({"rock", CharacterDescription<T>::template make<Rock<T>>});

  vector<Character<T>*> characters;
  for (int i = 1;i < argc; i++) {
    const char* current = argv[i];
    auto foundItem = std::find_if(characterList.begin(),
                                  characterList.end(),
                                  [current](const CharacterDescription<T>& item) {return strcmp
                                                                                           (item.optionName,current)==0;});
    if (foundItem != characterList.end() ) {
      characters.push_back((*foundItem).factory(argv[i+1]));
    }
  }
  return characters;
}

0 个答案:

没有答案