Design
A template class that will create a concrete instance of an interface class when given the name (as a string) of the concrete class.
This design is often seen in code, so the purpose of this class is to reduce the boilerplate code needed to write a factory class.
The template class will be used like this:
using MyFactory = factory<MyInterfaceType, MyType_1, MyType_2, MyType_3>;
int main() {
auto ptr = MyFactory::create("MyType_1");
ptr->doSomething();
}
There will be a static assert to check that all of the concrete types inherit from the interface type and the create method can accept an optional argument to describe the return type if no class matching the id is found.
Implementation
template<typename interface_type, typename... products>
struct factory {
template<typename Class>
struct LabledClass {
std::string_view label = to_string<Class>();
Class data;
};
using labled_classes = std::tuple<LabledClass<products>...>;
static std::unique_ptr<interface_type> create(const std::string_view& id, std::unique_ptr<interface_type> default_type = nullptr) {
std::unique_ptr<interface_type> result = std::move(default_type);
//checl all products inherit from interface_type
std::apply([](auto&&... tuple_elem) {
((static_check<decltype(tuple_elem.data)>()), ...);
}, labled_classes{} );
//if product matches id, return a new instance of that product
std::apply([&result, &id](auto&&... tuple_elem) {
(( tuple_elem.label == id ? result = std::make_unique<decltype(tuple_elem.data)>() : result ), ...);
}, labled_classes{} );
return result;
}
private:
template<typename product>
static void static_check() {
static_assert(std::is_base_of<interface_type, product>::value, "all products must inherit from interface_type");
}
};
This requires the function to_string<Class>()
which does not yet exist in the c++ standard, but can be implemented on GCC or Clang like so:
template<typename Class>
constexpr std::string_view to_string() {
std::string_view str = __PRETTY_FUNCTION__;
auto first = str.find("= ");
std::string_view str2 = str.substr(first + 2);
auto last = str2.find(";");
str2.remove_suffix(str2.length() - last);
return str2;
}
Example use
#include <iostream>
#include "factory.h"
struct Animal {
virtual void makeNoise() const = 0;
};
struct Dog : Animal {
virtual void makeNoise() const override{
std::cout << "Woof" << std::endl;
}
};
struct Cat : Animal {
virtual void makeNoise() const override{
std::cout << "Meow" << std::endl;
}
};
struct Duck : Animal {
virtual void makeNoise() const override{
std::cout << "Quack" << std::endl;
}
};
struct NullAnimal : Animal {
virtual void makeNoise() const override{
std::cout << "?" << std::endl;
}
};
using AnimalFactory = factory<Animal, Dog, Cat, Duck>;
int main() {
auto animal = AnimalFactory::create("Dog");
animal->makeNoise();
}
I have tested this with GCC 9.01 and it works