Last active
October 8, 2017 01:44
-
-
Save jmal0/7e48019d52156037477638ece2e76a1d to your computer and use it in GitHub Desktop.
C++ static Abstract_factory as a CRTP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <initializer_list> | |
#include <iostream> | |
#include <memory> | |
#include <string> | |
// Example base type | |
class Base | |
{ | |
public: | |
Base(const double a, const double b) : | |
_a(a), | |
_b(b) | |
{ | |
} | |
virtual ~Base() {} | |
void print_ab() const | |
{ | |
std::cout << "a: " << _a << std::endl << "b: " << _b << std::endl; | |
} | |
protected: | |
double _a; | |
double _b; | |
}; | |
// Example type inheriting from Base | |
// For the factory create function to work, the static constant "name" is required | |
class Derived_1 : public Base | |
{ | |
public: | |
Derived_1(const double a, const double b) : | |
Base(a + 1, b + 1) | |
{ | |
} | |
~Derived_1() {} | |
static const std::string name; | |
}; | |
// Example type inheriting from Base | |
// For the factory create function to work, the static constant "name" is required | |
class Derived_2 : public Base | |
{ | |
public: | |
Derived_2(const double a, const double b) : | |
Base(a + 2, b + 2) | |
{ | |
} | |
~Derived_2() {} | |
static const std::string name; | |
}; | |
// Forward declare template Abstract_factory type so it can be a template of itself | |
// Concrete factories can call Abstract_factory::create with the type of object they create and any subclasses that they | |
// want to be able to create as template parameters. | |
template <class Created_type, typename... Sub_classes> | |
class Abstract_factory; | |
// Template instance that unpacks one Sub_class type from Subclasses | |
// create will check if type is equal to the static member, "name" from Sub_class | |
template <class Created_type, class Sub_class, typename... Sub_classes> | |
class Abstract_factory<Created_type, Sub_class, Sub_classes...> | |
{ | |
public: | |
template <typename... Args> | |
static std::unique_ptr<Created_type> create(const std::string& type, Args... args) | |
{ | |
if (type == Sub_class::name) | |
{ | |
// Found a valid type, call its constructor | |
std::cout << "Created object of type: " << Sub_class::name << std::endl; | |
// c++14: return std::move(std::make_unique<Sub_class>(a, b)); | |
return std::move(std::unique_ptr<Created_type>(new Sub_class(args...))); | |
} | |
// Check next subclass | |
return std::move(Abstract_factory<Created_type, Sub_classes...>::create(type, args...)); | |
} | |
}; | |
// Base case for recursive template of Abstract_factory. This will be reached if type could not be matched to one of the | |
// the subclasses specified in the original variadic template | |
template <class Created_type> | |
class Abstract_factory<Created_type> | |
{ | |
public: | |
template <typename... Args> | |
static std::unique_ptr<Created_type> create(const std::string& type, Args... args) | |
{ | |
std::cout << "Invalid type: " << type << std::endl; | |
return nullptr; | |
} | |
}; | |
// Concrete instance of an Abstract_factory. It can just call Abstract_factory::create with the appropriate template | |
// parameters instead of having to write its own chain of if statements | |
class Base_factory | |
{ | |
public: | |
template <typename... Args> | |
static std::unique_ptr<Base> create(const std::string& type, Args... args) | |
{ | |
return std::move(Abstract_factory<Base, Derived_1, Derived_2>::create(type, args...)); | |
} | |
}; | |
// Declare the concrete type identifiers. | |
const std::string Derived_1::name = "D1"; | |
const std::string Derived_2::name = "D2"; | |
// Call Base_factory and print stuff to show what's going on | |
void create_and_print_info(std::string name) | |
{ | |
std::cout << "Attempting to create object of type " << name << std::endl; | |
auto d = Base_factory::create(name, 0, 0); | |
if (d != nullptr) | |
{ | |
d->print_ab(); | |
} | |
} | |
// Compile with: g++ test_abstract_factory -o test -std=c++11 | |
// Usage: ./test_abstract_factory [type1, type2, ...] | |
int main(int argc, char* argv[]) | |
{ | |
// If arguments are provided, attempt to create a Base pointer from arguments. | |
if (argc > 1) | |
{ | |
for (size_t i = 1; i < argc; ++i) | |
{ | |
create_and_print_info(argv[i]); | |
} | |
} | |
// Otherwise create each example subclass and attempt to create from invalid type | |
else | |
{ | |
create_and_print_info("D1"); | |
create_and_print_info("D2"); | |
create_and_print_info(""); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment