Currently, the Factory registry macro im using is at the bottom of the header file:
#pragma once
#include "GameObject.h"
class Foo : public GameObject
{
//...
};
ENGINE_SPAWNABLE(Foo);
This macro is functional, but is very hard to find in bigger headers and gets lost in headers with multiple classes. The macro would be best declared at the top of the class like so:
#pragma once
#include "GameObject.h"
ENGINE_SPAWNABLE();
class Foo : public GameObject
{
//...
};
But how would I go about doing this? Currently the macro only knows of the class because it's defined beforehand.
The current registry implementation is as so, only the macro part is important:
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include "Object.h"
// Registers a GameObject class with the factory's registry, and connecting that to a templated SpawnGameObject function. No need to use this if the class is never spawned through the engine
#define ENGINE_SPAWNABLE(CLASSNAME) static bool CLASSNAME##Registered \
= (GameObjectFactory::GetInstance().GetGameObjectRegistry()[#CLASSNAME] = &GameObjectFactory::SpawnObject<CLASSNAME>, true)
// Singleton game object factory
class GameObjectFactory
{
public:
// Gets instance of the simpleton
static GameObjectFactory& GetInstance()
{
static GameObjectFactory Instance;
return Instance;
}
// A templated function to spawn any registered GameObject
template <typename TObject>
static std::unique_ptr<Object> SpawnObject()
{
return std::make_unique<TObject>();
}
// A factory function that spawns an object of the specified class name
std::shared_ptr<Object> SpawnGameObjectByName(const std::string& Name);
std::unordered_map<std::string, std::function<std::shared_ptr<Object>()>>& GetGameObjectRegistry(); // Returns the Registry
private:
std::unordered_map<std::string, std::function<std::unique_ptr<Object>()>> Registry; // Registry that maps class names to factory functions
};
Any help or nudges towards any relevant resources would be appreciated.
First off, your SpawnObject()
method returns std::unique_ptr<Object>
, and your Registry
map stores functions that return std::unique_ptr<Object>
, however your GetGameObjectRegistry()
method returns a map of functions that return std::shared_ptr<Object>
, and your SpawnGameObjectByName()
method returns std::shared_ptr<Object>
. So, you should fix this discrepancy. Either you want your objects to use unique ownership or shared ownership, don't mix them.
That being said, if you want to move the macro above each class being registered, you still have to pass the class type into the macro as a parameter. But, you can have the macro forward-declare the class, eg:
#define ENGINE_SPAWNABLE(CLASSNAME) \
class CLASSNAME; \
static bool CLASSNAME##Registered = (GameObjectFactory::GetInstance().GetGameObjectRegistry()[#CLASSNAME] = &GameObjectFactory::SpawnObject<CLASSNAME>, true)
ENGINE_SPAWNABLE(Foo);
...
class Foo : public GameObject
{
//...
};
That being said, note that your macro is declaring its bool
in global scope as a static
variable. As such, every translation unit that you #include
Foo.h
into will get its own local copy of the bool
. If you want to avoid that, then you should declare the bool
as an extern
variable instead, and then move its definition (and thus the registration) into Foo.cpp
instead. Or else, you can make the bool
be a static
member of the Foo
class rather than a global variable.