Compare commits

...

13 Commits

Author SHA1 Message Date
1199c8ac07 moved documentation 2026-03-13 13:45:08 -04:00
5b877350c9 Updated documentation 2026-03-13 13:43:28 -04:00
0285bc402e Template based fsm. Initial commit. 2026-02-28 19:09:37 -05:00
f8681441e2 Added example desctiption. 2026-02-27 21:43:04 -05:00
3f4d5b30a5 Added example aoutput. 2026-02-27 21:42:17 -05:00
68a316354d Added example to main function 2026-02-27 21:35:15 -05:00
aaa77f7ea5 SPDX-License-Identifier 2026-02-27 21:21:23 -05:00
aa8edf76e5 Small changes to check commiter name 2026-02-27 20:56:45 -05:00
DH
f266f83729 Small changes to save credentials. 2026-02-27 20:41:05 -05:00
DH
cc0688f048 Final version with full refactoring. 2026-02-27 20:30:37 -05:00
DH
6d7cfc486e Smal refactoring. 2026-02-27 20:27:30 -05:00
DH
c9c2bc11ae So sort of small refactoring. 2026-02-27 19:53:55 -05:00
DH
fb7c83d04d Spell checking 2026-02-27 19:46:33 -05:00
8 changed files with 847 additions and 93 deletions

View File

@@ -1,3 +0,0 @@
# cpp-templates
generic actual cpp-templates

74
templatefsm/.gitignore vendored Normal file
View File

@@ -0,0 +1,74 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
CMakeLists.txt.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe

295
templatefsm/fsm.h Normal file
View File

@@ -0,0 +1,295 @@
#pragma once
#include <type_traits>
#include <utility>
#include <variant>
/** @brief Missing transition policy. */
enum class missing_transition_policy {
/** @brief Missing transition is considered required by validation. */
strict,
/** @brief Missing transition is allowed (event ignored). */
ignore
};
/** @brief Marker for undefined transition. */
struct no_transition {};
/** @brief Default guard: always allow. */
struct always_allow {
template <typename TContext, typename TFrom, typename TEvent>
bool operator() (TContext &, const TFrom &, const TEvent &) const noexcept {
return true;
}
};
/** @brief Default action: do nothing. */
struct do_nothing {
template <typename TContext, typename TFrom, typename TTo, typename TEvent>
void operator() (TContext &, const TFrom &, const TTo &, const TEvent &) const noexcept {
}
};
/**
* @brief Transition descriptor.
*
* @tparam TTo Destination state type.
* @tparam TGuard Guard functor type.
* @tparam TAction Action functor type.
*/
template <typename TTo, typename TGuard = always_allow, typename TAction = do_nothing>
struct transition_to {
using to_state = TTo;
using guard = TGuard;
using action = TAction;
};
/**
* @brief Transition table mapping: specialize to define transitions.
* Default: no_transition.
*/
template <typename TFrom, typename TEvent>
struct transition {
using type = no_transition;
};
/** @brief Optional mapping to names for logs. */
template <typename T>
struct name_of {
static constexpr const char *value = "?";
};
/**
* @brief Logger interface (default no-op).
*
* You can implement:
* - ignored_transition(from, event)
* - unhandled_transition(from, event) // policy=strict but missing transition at runtime
* - guard_blocked(from, event)
*/
struct null_logger {
void ignored_transition (const char *, const char *) noexcept {}
void unhandled_transition (const char *, const char *) noexcept {}
void guard_blocked (const char *, const char *) noexcept {}
};
/** @brief Hooks: state-only. */
template <typename TState>
struct on_exit_state {
template <typename TContext>
static void call (TContext &, const TState &) noexcept {}
};
template <typename TState>
struct on_entry_state {
template <typename TContext>
static void call (TContext &, const TState &) noexcept {}
};
/** @brief Hooks: event-specific. */
template <typename TState, typename TEvent>
struct on_exit_event {
template <typename TContext>
static void call (TContext &, const TState &, const TEvent &) noexcept {}
};
template <typename TState, typename TEvent>
struct on_entry_event {
template <typename TContext>
static void call (TContext &, const TState &, const TEvent &) noexcept {}
};
/**
* @brief Factory: construct destination state.
*
* IMPORTANT: Now includes TContext in template parameters to avoid templated make<TContext>().
*
* Default: To{}.
* Specialize state_factory<TContext, From, Event, To> as needed.
*/
template <typename TContext, typename TFrom, typename TEvent, typename TTo>
struct state_factory {
static TTo make (TContext &, const TFrom &, const TEvent &) {
return TTo{};
}
};
/**
* @brief Per (State,Event) missing transition policy.
*
* Default is ignore. Mark only required pairs strict.
*/
template <typename TFrom, typename TEvent>
struct missing_transition {
static constexpr missing_transition_policy policy = missing_transition_policy::ignore;
};
/** @brief Helper: is T one of Ts... */
template <typename T, typename... Ts>
struct is_one_of : std::disjunction<std::is_same<T, Ts>...> {};
/** @brief Helper: transition exists? */
template <typename TFrom, typename TEvent>
struct has_transition
: std::bool_constant < !std::is_same_v<typename transition<TFrom, TEvent>::type, no_transition >> {};
/** @brief Factory validation (call-form). */
template <typename TContext, typename TFrom, typename TEvent, typename TTo, typename = void>
struct is_factory_invocable : std::false_type {};
template <typename TContext, typename TFrom, typename TEvent, typename TTo>
struct is_factory_invocable <
TContext, TFrom, TEvent, TTo,
std::void_t<decltype (state_factory<TContext, TFrom, TEvent, TTo>::make (
std::declval<TContext &>(),
std::declval<const TFrom &>(),
std::declval<const TEvent &>())) >>
: std::bool_constant<std::is_same_v<
decltype (state_factory<TContext, TFrom, TEvent, TTo>::make (
std::declval<TContext &>(),
std::declval<const TFrom &>(),
std::declval<const TEvent &>())),
TTo>> {};
/**
* @brief FSM with runtime state storage and explicit compile-time validation.
*
* dispatch():
* - never static_asserts on missing transitions (avoids State×Event explosion with std::visit)
* - handles missing transitions via missing_transition<State,Event>::policy (ignore/strict)
*
* validate_events<Ev...>():
* - compile-time checks ONLY for pairs marked strict.
*/
template <typename TContext, typename TLogger, typename... TStates>
class fsm {
public:
using state_variant = std::variant<TStates...>;
template <typename TInitial,
typename = std::enable_if_t<is_one_of<TInitial, TStates...>::value>>
explicit fsm (TContext &ctx, TInitial initial, TLogger logger = TLogger{})
: m_ctx (ctx)
, m_logger (std::move (logger))
, m_state (std::move (initial)) {
std::visit (init_entry_visitor{*this}, m_state);
}
template <typename TEvent>
bool dispatch (const TEvent &ev) {
dispatch_visitor<TEvent> v{*this, ev};
return std::visit (v, m_state);
}
template <typename... TEvents>
static constexpr void validate_events() {
(validate_one_event<TEvents>(), ...);
}
const state_variant &state() const noexcept {
return m_state;
}
state_variant &state() noexcept {
return m_state;
}
private:
template <typename TEvent>
struct dispatch_visitor {
fsm &self;
const TEvent &ev;
template <typename TFrom>
bool operator() (TFrom &from) const {
return self.template dispatch_from<TFrom> (from, ev);
}
};
struct init_entry_visitor {
fsm &self;
template <typename TState>
void operator() (TState &st) const {
on_entry_state<TState>::call (self.m_ctx, st);
}
};
template <typename TEvent>
struct entry_after_commit_visitor {
fsm &self;
const TEvent &ev;
template <typename TState>
void operator() (TState &st) const {
on_entry_state<TState>::call (self.m_ctx, st);
on_entry_event<TState, TEvent>::call (self.m_ctx, st, ev);
}
};
template <typename TFrom, typename TEvent>
bool dispatch_from (TFrom &from, const TEvent &ev) {
using tr = typename transition<TFrom, TEvent>::type;
if constexpr (std::is_same_v<tr, no_transition>) {
if constexpr (missing_transition<TFrom, TEvent>::policy == missing_transition_policy::strict)
m_logger.unhandled_transition (name_of<TFrom>::value, name_of<TEvent>::value);
else
m_logger.ignored_transition (name_of<TFrom>::value, name_of<TEvent>::value);
return false;
} else {
using to_state = typename tr::to_state;
using guard_t = typename tr::guard;
using action_t = typename tr::action;
static_assert (is_one_of<to_state, TStates...>::value,
"FSM: transition target state not in FSM state list.");
static_assert (std::is_invocable_r_v<bool, guard_t, TContext &, const TFrom &, const TEvent &>,
"FSM: guard must be callable as bool(Context&, const From&, const Event&).");
static_assert (std::is_invocable_r_v<void, action_t, TContext &, const TFrom &, const to_state &, const TEvent &>,
"FSM: action must be callable as void(Context&, const From&, const To&, const Event&).");
static_assert (is_factory_invocable<TContext, TFrom, TEvent, to_state>::value,
"FSM: state_factory<Context,From,Event,To>::make must be callable as "
"To make(Context&, const From&, const Event&).");
guard_t guard{};
if (!guard (m_ctx, from, ev)) {
m_logger.guard_blocked (name_of<TFrom>::value, name_of<TEvent>::value);
return false;
}
on_exit_event<TFrom, TEvent>::call (m_ctx, from, ev);
on_exit_state<TFrom>::call (m_ctx, from);
to_state to = state_factory<TContext, TFrom, TEvent, to_state>::make (m_ctx, from, ev);
action_t action{};
action (m_ctx, from, to, ev);
m_state = std::move (to);
std::visit (entry_after_commit_visitor<TEvent> {*this, ev}, m_state);
return true;
}
}
template <typename TEvent>
static constexpr void validate_one_event() {
(validate_pair<TStates, TEvent>(), ...);
}
template <typename TState, typename TEvent>
static constexpr void validate_pair() {
if constexpr (missing_transition<TState, TEvent>::policy == missing_transition_policy::strict) {
static_assert (has_transition<TState, TEvent>::value,
"FSM validation: required (strict) transition is missing for (State, Event).");
}
}
private:
TContext &m_ctx;
TLogger m_logger;
state_variant m_state;
};

148
templatefsm/main.cpp Normal file
View File

@@ -0,0 +1,148 @@
#include "fsm.h"
#include <cstdint>
/** States */
struct Idle { };
struct Active {
std::uint32_t session_id = 0;
};
/** Events */
struct EvStart {
std::uint32_t requested_session = 0;
};
struct EvStop { };
/** Context */
struct Context {
std::uint32_t starts = 0;
};
/** Names (optional) */
template <> struct name_of<Idle> {
static constexpr const char *value = "Idle";
};
template <> struct name_of<Active> {
static constexpr const char *value = "Active";
};
template <> struct name_of<EvStart> {
static constexpr const char *value = "EvStart";
};
template <> struct name_of<EvStop> {
static constexpr const char *value = "EvStop";
};
/** Logger */
struct MyLogger {
void ignored_transition (const char *from_state, const char *event) noexcept {
(void)from_state;
(void)event;
// printf("FSM ignored: %s + %s\n", from_state, event);
}
void unhandled_transition (const char *from_state, const char *event) noexcept {
(void)from_state;
(void)event;
// printf("FSM UNHANDLED: %s + %s\n", from_state, event);
}
void guard_blocked (const char *from_state, const char *event) noexcept {
(void)from_state;
(void)event;
// printf("FSM guard blocked: %s + %s\n", from_state, event);
}
};
/** Guard */
struct GuardStartAllowed {
bool operator() (Context &ctx, const Idle &, const EvStart &) const noexcept {
return ctx.starts < 10U;
}
};
/** Action */
struct ActionOnStart {
void operator() (Context &ctx, const Idle &, const Active &, const EvStart &) const noexcept {
++ctx.starts;
}
};
/** Factory specialization (NOTE: now includes Context in template parameters) */
template <>
struct state_factory<Context, Idle, EvStart, Active> {
static Active make (Context &, const Idle &, const EvStart &ev) {
Active a{};
a.session_id = ev.requested_session;
return a;
}
};
/** Hooks (optional) */
template <>
struct on_entry_state<Active> {
static void call (Context &, const Active &) noexcept {
// start timers, etc.
}
};
template <>
struct on_exit_state<Active> {
static void call (Context &, const Active &) noexcept {
// stop timers, etc.
}
};
template <>
struct on_entry_event<Active, EvStart> {
static void call (Context &, const Active &, const EvStart &) noexcept {
// entered Active due to EvStart
}
};
/** Transitions */
template <>
struct transition<Idle, EvStart> {
using type = transition_to<Active, GuardStartAllowed, ActionOnStart>;
};
template <>
struct transition<Active, EvStop> {
using type = transition_to<Idle>;
};
/**
* "Adult strictness": mark only required pairs strict.
* Everything else is ignore by default.
*/
template <>
struct missing_transition<Idle, EvStart> {
static constexpr missing_transition_policy policy = missing_transition_policy::strict;
};
template <>
struct missing_transition<Active, EvStop> {
static constexpr missing_transition_policy policy = missing_transition_policy::strict;
};
/** FSM type */
using MyFsm = fsm<Context, MyLogger, Idle, Active>;
static constexpr void validate_fsm_contract() {
MyFsm::validate_events<EvStart, EvStop>();
}
int main() {
validate_fsm_contract();
Context ctx{};
MyFsm f (ctx, Idle{}, MyLogger{});
f.dispatch (EvStart{42}); // Idle -> Active
f.dispatch (EvStop{}); // Active -> Idle
// Not required by strict contract => default ignore (logged via ignored_transition)
f.dispatch (EvStop{}); // Idle + EvStop => ignored
f.dispatch (EvStart{7}); // (if currently Active) Active + EvStart => ignored
return 0;
}

View File

@@ -0,0 +1,10 @@
TEMPLATE = app
CONFIG += console c++17
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += \
main.cpp
HEADERS += \
fsm.h

190
typefactory/README.md Normal file
View File

@@ -0,0 +1,190 @@
# TypeFactory
A lightweight registry-based factory for creating objects by runtime identifier.
`TypeFactory` is a small header-only C++ utility for situations where the exact object type
must be selected dynamically at runtime using an ID.
Typical use cases include:
- protocol command dispatch
- vehicle type identification
- runtime parser or handler selection
- plugin-like object creation
- configuration-based object instantiation
The implementation is intentionally simple and follows the KISS principle.
## Features
- header-only implementation
- C++17 compatible
- compile-time validation of registered types
- fast lookup using `std::unordered_map`
- optional access to creator function for advanced use
- predictable and compact API
- suitable for both commercial and open-source projects
## Requirements
- C++17 or newer
- `ClassId` must be hashable (`std::hash<ClassId>` must be available)
## Basic Idea
The factory stores a mapping:
- **runtime ID** -> **creator function**
A derived type is registered once, then can be created later by its ID.
## Example
```cpp
#include "typefactory.h"
#include <string>
#include <memory>
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;
}
private:
int m_start = 0;
};
struct Derived2 : Base {
explicit Derived2(int start) : m_start(start) {}
int get() override {
return m_start + 2;
}
private:
int m_start = 0;
};
int main() {
TypeFactory<std::string, Base, int> factory;
factory.registerType<Derived1>("one");
factory.registerType<Derived2>("two");
auto d1 = factory.create("one", 10);
auto d2 = factory.create("two", 10);
return d1->get() + d2->get();
}
```
## Advanced Usage
The factory also provides direct access to the creator function:
```cpp
auto fn = factory.creator("one");
auto obj = fn(10);
```
This can be useful when integrating with dispatch tables or higher-level runtime logic.
## API Overview
### `registerType`
Registers a derived type for a given runtime ID.
```cpp
factory.registerType<Derived>("id");
```
Compile-time checks ensure that:
- `Derived` inherits from `BaseClass`
- `Derived` is constructible from `Args...`
If the same ID is registered again, the previous registration is overwritten.
This is intentional: **last registration wins**.
### `creator`
Returns the creator function associated with the given ID.
```cpp
auto fn = factory.creator("id");
```
Throws `std::out_of_range` if the ID is not registered.
### `create`
Creates an object directly by ID.
```cpp
auto obj = factory.create("id", args...);
```
Throws `std::out_of_range` if the ID is not registered.
### `contains`
Checks whether an ID is registered.
```cpp
if (factory.contains("id")) {
// safe to create
}
```
## Design Notes
- `TypeFactory` is not thread-safe during registration.
- Types should normally be registered during initialization.
- Lookup and creation are lightweight and predictable.
- The class is designed to be reusable as a low-level infrastructure utility.
- The registry is stored in a protected `std::unordered_map`, so derived factories can extend the design if needed.
## Why Not Abstract Factory?
This utility is not a classic *Abstract Factory* pattern.
A classic Abstract Factory typically creates **families of related objects**.
`TypeFactory` is closer to a:
- registry-based factory
- type factory
- runtime creator map
It focuses on selecting one concrete type by runtime ID.
## Example Use Cases
### Vehicle Identification
A controller returns a vehicle ID.
The system uses that ID to create the matching parameter or settings object.
### Protocol Command Handling
A command ID is received over the network.
The system selects and creates the correct handler object for that command.
### Runtime Format Selection
A parser or converter is selected dynamically depending on file type, message type, or configuration.
## License
This project is licensed under the MIT License.
For source files, it is recommended to use SPDX headers such as:
```cpp
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Dim Himro
```

View File

@@ -1,8 +1,42 @@
#include <iostream> #include <iostream>
#include "typefactory.h"
#include <string>
using namespace std; using namespace std;
int main() { int main() {
cout << "Hello World!" << endl; cout << "Type factory simple example" << endl;
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<std::string, Base, int> factory;
factory.registerType<Derived1> ("one");
factory.registerType<Derived2> ("two");
auto a = factory.create ("one", 10);
auto b = factory.creator ("two") (10); // advanced API: get creator function
std::cout << a->get() << std::endl;
std::cout << b->get() << std::endl;
return 0; return 0;
} }

View File

@@ -1,112 +1,118 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Dim Himro
#ifndef TYPEFACTORY_H #ifndef TYPEFACTORY_H
#define TYPEFACTORY_H #define TYPEFACTORY_H
#include <unordered_map>
#include <memory> #include <memory>
#include <type_traits>
#include <unordered_map>
#include <utility> // std::move
/** /**
* @brief Registry-based factory for creating objects by runtime identifier. * @brief Registry-based factory for creating objects by runtime identifier.
* *
* Allows registering derived types and instantiating them * Allows registering derived types and instantiating them using a ClassId key.
* using a ClassId key.
* *
* @param ClassId - unique class identification class. For example string, or integer id. * Notes:
* @param BaseClass - all objects should have one base class. For example vehicle or parameter * - Not thread-safe. Register types during initialization phase only.
* @param Args - constructor arguments * - Re-registering the same id overwrites the previous entry ("last wins").
* - ClassId must be hashable (std::hash<ClassId> 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: * Usage example:
* @code * @code
* class Base { * #include <string>
* public:
* virtual int get() = 0;
* }
* *
* class Derived1 : public Base { * struct Base {
* public: * virtual ~Base() = default;
* Derived1 (int start) : m_start(start){} * virtual int get() = 0;
* int get() override { * };
* return m_start + 1;
* }
* private:
* int m_start = 0;
* }
* *
* class Derived2 : public Base { * struct Derived1 : Base {
* public: * explicit Derived1(int start) : m_start(start) {}
* Derived1 (int start) : m_start(start){} * int get() override { return m_start + 1; }
* int get() override { * int m_start = 0;
* return m_start + 2; * };
* }
* private:
* int m_start = 0;
* }
* .
* .
* .
* TypeFsactory <std::string, Base, int> typefactory;
* typefactory.registerType<Derived1>("one");
* typefactory.registerType<Derived2>("2");
* *
* auto d1 = typefactory.createInstance ("one")(10); * struct Derived2 : Base {
* auto d2 = typefactory.createInstance ("2")(10); * explicit Derived2(int start) : m_start(start) {}
* int get() override { return m_start + 2; }
* int m_start = 0;
* };
* *
* d1->get(); * TypeFactory<std::string, Base, int> factory;
* d2->get(); * factory.registerType<Derived1>("one");
* factory.registerType<Derived2>("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 * @endcode
*/ */
template<class ClassId, class BaseClass, class ... Args> template <class ClassId, class BaseClass, class... Args>
class TypeFsactory { class TypeFactory {
public: public:
typedef std::shared_ptr<BaseClass> BaseClass_SP; using BasePtr = std::shared_ptr<BaseClass>;
/** using CreatorFn = BasePtr (*)(Args...);
* @brief typefactoryFunction - typefactory function pointer type. Generally speaking it is pointer to constructor.
*/
typedef BaseClass_SP (*typefactoryFunction) (Args ...);
TypeFsactory() = default; TypeFactory() = default;
/** /**
* @brief registerType - template function to register class in factory * @brief Registers a Derived type under the given id.
* @param Derived - registering type *
* @param id - unique class identification * Requirements (checked at compile-time):
* * - Derived must inherit from BaseClass
* Usage example: * - Derived must be constructible from Args...
* @code *
* typefactory.registerType<Derived1>("one"); * Re-registering the same id overwrites the previous entry ("last wins").
* typefactory.registerType<Derived2>("2"); */
* @endcode template <class Derived>
*/ void registerType(const ClassId& id) {
template<class Derived> static_assert(std::is_base_of_v<BaseClass, Derived>,
void registerType (const ClassId& id) { "Derived must inherit from BaseClass");
/// Register Derived type by storing its constructor wrapper static_assert(std::is_constructible_v<Derived, Args...>,
classes[id] = &typefactory<Derived>; "Derived must be constructible from Args...");
}
/** classes.insert_or_assign(id, &instantiate<Derived>);
* @brief createInstance - createInstance class by unique id. Class should be registered before }
* @param id - unique class identification
* @return shared pointer to new class.
* @throws std::out_of_range when id not found in map
*/
typefactoryFunction createInstance (const ClassId& id) const {
return classes.at (id);
}
protected:
/**
* @brief classes - Not thread-safe. Register types during initialization phase only.
*/
std::unordered_map<ClassId, typefactoryFunction> classes;
private: /**
/** * @brief Returns creator function for the given id.
* @brief typefactory - main functionality, createInstance registered object * @throws std::out_of_range when id is not registered.
* @param Derived - class type */
* @param args - constructor arguments [[nodiscard]] CreatorFn creator(const ClassId& id) const {
* @return shared pointer to register Derived class return classes.at(id);
*/ }
template<class Derived>
static BaseClass_SP typefactory (Args ... args) { /**
return std::make_shared<Derived> (args ...); * @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<ClassId, CreatorFn> classes;
private:
template <class Derived>
static BasePtr instantiate(Args... args) {
return std::make_shared<Derived>(std::move(args)...);
}
}; };
#endif // TYPEFACTORY_H #endif // TYPEFACTORY_H