71 lines
3.3 KiB
C++
71 lines
3.3 KiB
C++
#pragma once
|
|
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
#include "external/fkyaml_node.hpp"
|
|
|
|
class Entity;
|
|
|
|
// Factory amb registre obert a extensió: cada `concrete/*.cpp` declara un
|
|
// registrador estàtic amb `REGISTER_ENTITY(name, Class)` que s'executa
|
|
// abans de `main`. Afegir un tipus nou d'entitat = nova classe + nova
|
|
// crida `REGISTER_ENTITY`, sense tocar el factory.
|
|
//
|
|
// El `Creator` rep el `fkyaml::node` de la plantilla de l'entitat (no el
|
|
// spawn). És el constructor de la classe concreta qui extreu els camps
|
|
// que li interessin (sprite.path, animations, stats, etc.).
|
|
class EntityFactory {
|
|
public:
|
|
using Creator = std::function<std::unique_ptr<Entity>(const fkyaml::node&)>;
|
|
|
|
static auto instance() -> EntityFactory&;
|
|
|
|
// Idempotent per nom: si s'invoca dues vegades amb el mateix nom,
|
|
// la segona sobreescriu (útil en tests que re-registren; no és
|
|
// especialment defensiu perquè els registradors són d'execució única).
|
|
void registerCreator(const std::string& name, Creator creator);
|
|
|
|
// Retorna nullptr si el nom no té registrada cap creator.
|
|
[[nodiscard]] auto create(const std::string& name, const fkyaml::node& yaml) const
|
|
-> std::unique_ptr<Entity>;
|
|
|
|
// Diagnòstic: llista de noms registrats (per ordre d'inserció no
|
|
// garantit). Útil al smoke per comprovar que tots els concrets
|
|
// s'han auto-registrat.
|
|
[[nodiscard]] auto registeredNames() const -> std::vector<std::string>;
|
|
|
|
private:
|
|
EntityFactory() = default;
|
|
std::unordered_map<std::string, Creator> creators_;
|
|
};
|
|
|
|
// La macro genera un objecte anònim dins un namespace anònim: el seu
|
|
// constructor s'executa abans de `main` i registra la factory. El nom
|
|
// del símbol es compon amb `TAG`, perquè així es poden registrar àlies
|
|
// per una mateixa classe (p.ex. dues claus diferents al YAML lligades a
|
|
// la mateixa classe C++) sense que els símbols xoquen. La forma curta
|
|
// `REGISTER_ENTITY(NAME, CLASS)` reusa `CLASS` com a tag; per àlies cal
|
|
// usar `REGISTER_ENTITY_AS` i passar un identificador únic.
|
|
//
|
|
// IMPORTANT: perquè el registrador es cridi, el `.cpp` que el conté ha
|
|
// d'estar enllaçat directament a l'executable (no dins una STATIC lib
|
|
// sense `--whole-archive`). Tots els `concrete/*.cpp` entren a APP_SOURCES.
|
|
#define REGISTER_ENTITY_AS(NAME, CLASS, TAG) \
|
|
namespace { \
|
|
struct TAG##_Registrar { \
|
|
TAG##_Registrar() { \
|
|
EntityFactory::instance().registerCreator( \
|
|
NAME, \
|
|
[](const fkyaml::node& yaml) -> std::unique_ptr<Entity> { \
|
|
return std::make_unique<CLASS>(yaml); \
|
|
}); \
|
|
} \
|
|
}; \
|
|
const TAG##_Registrar g_##TAG##_registrar; /* NOLINT(cert-err58-cpp) */ \
|
|
}
|
|
|
|
#define REGISTER_ENTITY(NAME, CLASS) REGISTER_ENTITY_AS(NAME, CLASS, CLASS)
|