Skip to content

Instantly share code, notes, and snippets.

@jmal0
Last active October 8, 2017 01:44
Show Gist options
  • Save jmal0/7e48019d52156037477638ece2e76a1d to your computer and use it in GitHub Desktop.
Save jmal0/7e48019d52156037477638ece2e76a1d to your computer and use it in GitHub Desktop.
C++ static Abstract_factory as a CRTP
#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