I've been experimenting with the factory implementation described here:
http://www.nirfriedman.com/2018/04/29/unforgettable-factory/
The example uses "Animal" as a base and "Dog" and "Cat" as the derived classes that are registered with the factory. But now suppose I wanted to add a "Lion" class that derives from Cat... how can I derive from Cat while still registering with the Animal factory? Further, I would like to be able to create a "Cat" factory as well that would let me create object of type "Cat" instead of "Animal". Can this example be adapted to do this?
Here is some code based off the original post that shows what I'm trying to do.
#include <functional>
#include <memory>
#include <exception>
#include <iostream>
#include <string>
#include <map>
#include <unordered_map>
#include <cstdlib>
#include <cxxabi.h>
template<typename BaseT, typename ...Args>
class Factory
{
public:
friend BaseT;
template<class ...T>
static std::shared_ptr<BaseT> create(const std::string& name, T&&... args)
{
try
{
return types().at(name)(std::forward<T>(args)...);
}
catch(std::out_of_range e)
{
throw std::runtime_error("type \"" + name + "\" is not registered with the factory");
};
};
template<typename T>
struct Registrar : BaseT
{
public:
friend T;
static bool register_type()
{
auto demangle = [](const char* name) -> std::string
{
int status = -4;
std::unique_ptr<char, void (*)(void *)> res{abi::__cxa_demangle(name, NULL, NULL, &status), free};
return (status == 0) ? res.get() : name;
};
const auto name = demangle(typeid(T).name());
std::cout << "registering " << name << "\n";
Factory::types()[name] = [](Args... args) -> std::shared_ptr<BaseT>
{
return std::make_shared<T>(std::forward<Args>(args)...);
};
return true;
};
static inline bool registered = register_type();
private:
Registrar() : BaseT() { (void) registered; };
};
private:
using FunctionT = std::function< std::shared_ptr<BaseT>(Args...)>;
static auto& types()
{
static std::unordered_map<std::string, FunctionT> m_types;
return m_types;
};
};
struct Animal : Factory<Animal>
{
virtual void speak() = 0;
virtual ~Animal() = default;
};
// How can I create a Cat factory at this level in the hierarchy?
class Cat : public Animal::Registrar<Cat>
{
public:
Cat() {};
virtual ~Cat() = default;
virtual void speak() { std::cout << "Meow!" << "\n"; };
};
// I would like to register Lion with the Animal factory.
class Lion : public Cat
{
public:
Lion() {};
void speak() { std::cout << "Roar!" << "\n"; };
};
int main(int argc, char** argv)
{
auto a = Animal::create("Cat");
a->speak();
auto lion = Cat::create("Lion"); // this doesn't work
lion->speak();
return 0;
};