|
|
@@ -0,0 +1,80 @@
|
|
|
+//
|
|
|
+// polymorphic_factory.hpp
|
|
|
+// resource_factory
|
|
|
+//
|
|
|
+// Created by Sam Jaffe on 8/14/16.
|
|
|
+//
|
|
|
+
|
|
|
+#pragma once
|
|
|
+
|
|
|
+#include <functional>
|
|
|
+#include <string>
|
|
|
+#include <unordered_map>
|
|
|
+
|
|
|
+/*
|
|
|
+ * An instance prototype factory factory.
|
|
|
+ * The purpose of this object is a prefab for situations where you do not
|
|
|
+ * know, necessarily, at compile time all posible types inheriting from the
|
|
|
+ * base class. An example of this is in a game, you may have a Mob class.
|
|
|
+ * Mob may be instantiated as an Orc, Goblin, Slime, or Knight, but you don't
|
|
|
+ * know the exhaustive list, and the overhead for implementing an override
|
|
|
+ * for every single type is wasteful. With this object, you can initialize
|
|
|
+ * implementations from a string ID, and some parameters required.
|
|
|
+ * For example:
|
|
|
+ *
|
|
|
+ * class Monster { ... };
|
|
|
+ * class MonsterPrototype : public Monster {
|
|
|
+ * public: Monster operator()(...) const { ... }
|
|
|
+ * };
|
|
|
+ * using MonsterGenerator = prototype_factory<Monster, ...>;
|
|
|
+ * MonsterGenerator::bind("Orc", MonsterPrototype("resources/orc.proto"));
|
|
|
+ *
|
|
|
+ * -----
|
|
|
+ *
|
|
|
+ * This can also be used as a generator for polymorphic types, optionally
|
|
|
+ * through a plugin system. This would allow a library consumer to define
|
|
|
+ * extensions to some polymorphic class, such as a Widget, and use built in
|
|
|
+ * features of the library to instantiate it. For example:
|
|
|
+ *
|
|
|
+ * class Widget { ... };
|
|
|
+ * class Button : public Widget { ... };
|
|
|
+ * using WidgetGenerator = objects::prototype::factory<std::unique_ptr<Widget>, ...>;
|
|
|
+ * WidgetGenerator::bind("Button",
|
|
|
+ * [](...) { return std::make_unique<Button>(...); });
|
|
|
+ */
|
|
|
+namespace objects { namespace prototype {
|
|
|
+ template <typename Base, typename... Args>
|
|
|
+ class factory {
|
|
|
+ public:
|
|
|
+ using rval_t = Base;
|
|
|
+ using producer = std::function<rval_t(Args &&...)>;
|
|
|
+
|
|
|
+ static factory & instance();
|
|
|
+
|
|
|
+ rval_t get(std::string const & type_id, Args &&... args) {
|
|
|
+ auto it = _factories.find(type_id);
|
|
|
+ if (it == _factories.end()) { return rval_t(); }
|
|
|
+ else { return (it->second)(std::forward<Args>(args)...); }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool bind(std::string const & type_id, producer p) {
|
|
|
+ return _factories.insert(std::make_pair(type_id, p)).second;
|
|
|
+ }
|
|
|
+ private:
|
|
|
+ factory() = default;
|
|
|
+ std::unordered_map<std::string, producer> _factories;
|
|
|
+ };
|
|
|
+} }
|
|
|
+
|
|
|
+#define INSTANTIATE_PROTOTYPE_FACTORY(...) \
|
|
|
+ template <> ::objects::prototype::factory<__VA_ARGS__> & \
|
|
|
+ ::objects::prototype::factory<__VA_ARGS__>::instance() { \
|
|
|
+ static ::objects::prototype::factory<__VA_ARGS__> _instance; \
|
|
|
+ return _instance; \
|
|
|
+ }
|
|
|
+
|
|
|
+#define INSTANTIATE_PROTOTYPE_FACTORY_2(Typedef) \
|
|
|
+ template <> Typedef & Typedef::instance() { \
|
|
|
+ static Typedef _instance; \
|
|
|
+ return _instance; \
|
|
|
+ }
|