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 returnsstd::unique_ptr<Object>, and yourRegistrymap stores functions that returnstd::unique_ptr<Object>, however yourGetGameObjectRegistry()method returns a map of functions that returnstd::shared_ptr<Object>, and yourSpawnGameObjectByName()method returnsstd::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:
Online Demo
That being said, note that your macro is declaring its
boolin global scope as astaticvariable. As such, every translation unit that you#includeFoo.hinto will get its own local copy of thebool. If you want to avoid that, then you should declare theboolas anexternvariable instead, and then move its definition (and thus the registration) intoFoo.cppinstead. Or else, you can make theboolbe astaticmember of theFooclass rather than a global variable.