// SPDX-License-Identifier: MIT // Copyright (c) 2026 Dim Himro #ifndef TYPEFACTORY_H #define TYPEFACTORY_H #include #include #include #include // std::move /** * @brief Registry-based factory for creating objects by runtime identifier. * * Allows registering derived types and instantiating them using a ClassId key. * * Notes: * - Not thread-safe. Register types during initialization phase only. * - Re-registering the same id overwrites the previous entry ("last wins"). * - ClassId must be hashable (std::hash specialization must exist). * * @tparam ClassId -Unique class identifier type (e.g. std::string, int, enum). * @tparam BaseClass -Base type for all created objects. * @tparam Arg - Constructor arguments forwarded to registered derived types (by value). * * Usage example: * @code * #include * * struct Base { * virtual ~Base() = default; * virtual int get() = 0; * }; * * struct Derived1 : Base { * explicit Derived1(int start) : m_start(start) {} * int get() override { return m_start + 1; } * int m_start = 0; * }; * * struct Derived2 : Base { * explicit Derived2(int start) : m_start(start) {} * int get() override { return m_start + 2; } * int m_start = 0; * }; * * TypeFactory factory; * factory.registerType("one"); * factory.registerType("two"); * * auto a = factory.create("one", 10); * auto b = factory.creator("two")(10); // advanced API: get creator function * * (void)a->get(); * (void)b->get(); * @endcode */ template class TypeFactory { public: using BasePtr = std::shared_ptr; using CreatorFn = BasePtr (*)(Args...); TypeFactory() = default; /** * @brief Registers a Derived type under the given id. * * Requirements (checked at compile-time): * - Derived must inherit from BaseClass * - Derived must be constructible from Args... * * Re-registering the same id overwrites the previous entry ("last wins"). */ template void registerType(const ClassId& id) { static_assert(std::is_base_of_v, "Derived must inherit from BaseClass"); static_assert(std::is_constructible_v, "Derived must be constructible from Args..."); classes.insert_or_assign(id, &instantiate); } /** * @brief Returns creator function for the given id. * @throws std::out_of_range when id is not registered. */ [[nodiscard]] CreatorFn creator(const ClassId& id) const { return classes.at(id); } /** * @brief Creates an instance for the given id using provided arguments. * @throws std::out_of_range when id is not registered. */ [[nodiscard]] BasePtr create(const ClassId& id, Args... args) const { return creator(id)(std::move(args)...); } /** * @brief Checks whether an id is registered. */ [[nodiscard]] bool contains(const ClassId& id) const { return classes.find(id) != classes.end(); } protected: // Protected by design: derived factories may extend/inspect the registry. std::unordered_map classes; private: template static BasePtr instantiate(Args... args) { return std::make_shared(std::move(args)...); } }; #endif // TYPEFACTORY_H