191 lines
4.2 KiB
Markdown
191 lines
4.2 KiB
Markdown
# 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
|
|
```
|