如何解决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 举报,一经查实,本站将立刻删除。