微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C++自注册工厂,多参数构造函数

如何解决C++自注册工厂,多参数构造函数

我阅读了 this 篇关于具有具体类的自注册功能的 C++ 工厂类的文章。真的很喜欢,尤其是用作注册类的键的破坏名称解决方案。

我想解决一个主要问题:我们如何修改工厂以支持具有不同构造函数参数的具体类

// Dog and Cat both derive from Animal,but have different constructor parameters
class Dog : public Animal::Registrar<Dog> {
public:
  Dog(int param1,std::string param2) : m_x(param1) {}

  void makeNoise() { std::cerr << "Dog: " << m_x << "\n"; }

private:
  int m_x;
};

class Cat : public Animal::Registrar<Cat> {
public:
  Cat(bool param1) : m_flag(param1) {}

  void makeNoise() { std::cerr << "Cat: " << m_x << "\n"; }

private:
  bool m_flag;
};

我想也许 template <class Base,class... Args> class Factory 的参数包应该移到 template <class T> struct Registrar,但我找不到合适的解决方案。

完整的原始代码如下

#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>

#include <cstdlib>
#include <cxxabi.h>

std::string demangle(const char *name) {

  int status = -4; // some arbitrary value to eliminate the compiler warning

  std::unique_ptr<char,void (*)(void *)> res{
      abi::__cxa_demangle(name,NULL,&status),std::free};

  return (status == 0) ? res.get() : name;
}

template <class Base,class... Args> class Factory {
public:

  template <class ... T>
  static std::unique_ptr<Base> make(const std::string &s,T&&... args) {
      return data().at(s)(std::forward<T>(args)...);
  }

  template <class T> struct Registrar : Base {
    friend T;

    static bool registerT() {
      const auto name = demangle(typeid(T).name());
      Factory::data()[name] = [](Args... args) -> std::unique_ptr<Base> {
        return std::make_unique<T>(std::forward<Args>(args)...);
      };
      return true;
    }
    static bool registered;

  private:
    Registrar() : Base(Key{}) { (void)registered; }
  };

  friend Base;

private:
  class Key {
    Key(){};
    template <class T> friend struct Registrar;
  };
  using FuncType = std::unique_ptr<Base> (*)(Args...);
  Factory() = default;

  static auto &data() {
    static std::unordered_map<std::string,FuncType> s;
    return s;
  }
};

template <class Base,class... Args>
template <class T>
bool Factory<Base,Args...>::Registrar<T>::registered =
    Factory<Base,Args...>::Registrar<T>::registerT();

struct Animal : Factory<Animal,int> {
  Animal(Key) {}
  virtual void makeNoise() = 0;
};

class Dog : public Animal::Registrar<Dog> {
public:
  Dog(int x) : m_x(x) {}

  void makeNoise() { std::cerr << "Dog: " << m_x << "\n"; }

private:
  int m_x;
};

class Cat : public Animal::Registrar<Cat> {
public:
  Cat(int x) : m_x(x) {}

  void makeNoise() { std::cerr << "Cat: " << m_x << "\n"; }

private:
  int m_x;
};

// Won't compile because of the private CRTP constructor
// class Spider : public Animal::Registrar<Cat> {
// public:
//     Spider(int x) : m_x(x) {}

//     void makeNoise() { std::cerr << "Spider: " << m_x << "\n"; }

// private:
//     int m_x;
// };

// Won't compile because of the pass key idiom
// class Zob : public Animal {
// public:
//     Zob(int x) : Animal({}),m_x(x) {}

//      void makeNoise() { std::cerr << "Zob: " << m_x << "\n"; }
//     std::unique_ptr<Animal> clone() const { return
//     std::make_unique<Zob>(*this); }

// private:
//      int m_x;

// };

// An example that shows that rvalues are handled correctly,and
// that this all works with move only types

struct Creature : Factory<Creature,std::unique_ptr<int>> {
    Creature(Key) {}
    virtual void makeNoise() = 0;
};

class Ghost : public Creature::Registrar<Ghost> {
public:
    Ghost(std::unique_ptr<int>&& x) : m_x(*x) {}

    void makeNoise() { std::cerr << "Ghost: " << m_x << "\n"; }

private:
    int m_x;
};

int main() {
  auto x = Animal::make("Dog",3);
  auto y = Animal::make("Cat",2);
  x->makeNoise();
  y->makeNoise();
  auto z = Creature::make("Ghost",std::make_unique<int>(4));
  z->makeNoise();
  return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。