zeusUnnamed repository; edit this file 'description' to name the repository. |
git clone Unknown |
Log | Files | Refs |
commit | 70a52be7e4bd76dfac7397ab7d33de06f08a7c83 |
parent | 99df392c5bab4cf9cd7cb54d381f0036593d0253 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Mon, 12 May 2025 19:20:54 +0200 |
Virtual factory example with runtime type id-ing
A | factory_virtual_ctor.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 files changed, 122 insertions(+), 0 deletions(-)
diff --git a/ factory_virtual_ctor.cpp b/ factory_virtual_ctor.cpp
@@ -0,0 +1,122 @@
#include <cstddef>
#include <functional>
#include <memory>
#include <vector>
class Unit {};
class Knight : public Unit {};
class Mage : public Unit {};
// allows the factory function to have one concrete signature
struct BuildingSpec {};
class Building {
// register type_id in runtime
static std::size_t type_count;
using BuildingFactory = std::function<Building *(const BuildingSpec &)>;
static std::vector<BuildingFactory> registry;
public:
virtual ~Building() = default;
static std::size_t Register(BuildingFactory factory) {
registry.emplace_back(std::move(factory));
return type_count++;
}
static auto Make(std::size_t type, const BuildingSpec &spec) {
return std::unique_ptr<Building>(registry[type](spec));
}
virtual Unit *MakeUnit() const = 0;
virtual std::unique_ptr<Building> Clone() const = 0;
};
std::vector<Building::BuildingFactory> Building::registry;
std::size_t Building::type_count = 0;
// CRTP to avoid rudundant Clone methods
template <typename Derived> class BuildingCloner : public Building {
public:
std::unique_ptr<Building> Clone() const override {
return std::unique_ptr<Building>(
new Derived(*static_cast<const Derived *>(this)));
}
};
// Builder for concrete BuildingSpec
struct CastleSpec : BuildingSpec {
bool with_pasture = false;
int number_of_stalls = 0;
CastleSpec() = default;
CastleSpec &SetPasture(bool with_pasture) {
this->with_pasture = with_pasture;
return *this;
}
CastleSpec &SetStalls(int number_of_stalls) {
this->number_of_stalls = number_of_stalls;
return *this;
}
};
// Concrete Building that needs a tag and a way to make units
class Castle : public BuildingCloner<Castle> {
public:
static const size_t type_tag;
explicit Castle(const CastleSpec &spec) { /* ... */ }
Knight *MakeUnit() const override { return new Knight; }
};
const size_t Castle::type_tag =
Building::Register([](const BuildingSpec &spec) -> Building * {
return new Castle(static_cast<const CastleSpec &>(spec));
});
struct TowerSpec : BuildingSpec {
bool with_magic = false;
int number_of_apprentices = 0;
TowerSpec() = default;
TowerSpec &SetMagic(bool with_magic) {
this->with_magic = with_magic;
return *this;
}
TowerSpec &SetApprentices(int number_of_apprentices) {
this->number_of_apprentices = number_of_apprentices;
return *this;
}
};
class Tower : public BuildingCloner<Tower> {
public:
static const size_t type_tag;
explicit Tower(const TowerSpec &spec) { /* ... */ }
Mage *MakeUnit() const override { return new Mage; }
};
const size_t Tower::type_tag =
Building::Register([](const BuildingSpec &spec) -> Building * {
return new Tower(static_cast<const TowerSpec &>(spec));
});
int main() {
const auto castle = Building::Make(
Castle::type_tag, CastleSpec{}.SetPasture(true).SetStalls(5));
const auto castle2 = castle->Clone();
const auto tower = Building::Make(
Tower::type_tag, TowerSpec{}.SetMagic(false).SetApprentices(2));
const auto tower2 = castle->Clone();
return 0;
}