I have multiple macros at beginning of each GameObject child class header. This is intended, as the client might add any number of classes inheriting from GameObject, with any engine macro arguments.
However, this creates bloated and hard to read code:
SPAWNABLE(Dude); EDITORONLY(Dude); OPTIMIZE(Dude); RANDOMIZE(Dude); class Dude : public GameObject { // ... }; The easiest solution would be to have a macro that takes other macros as arguments:
ENGINE_CLASS(Dude, SPAWNABLE, EDITORONLY, OPTIMIZE, RANDOMIZE) : public GameObject { // ... }; How can a macro be placed inside of the ENGINE_CLASS macro?
Here is the GameObjectFactory, which has the SPAWNABLE macro, which allows the class to be spawned. It is an easy test for whether this "macro inside macro" implementation works:
// Allows GameObject to be spawnable through SpawnGameObject. No need to use this if the class is never spawned through the engine #define ENGINE_SPAWNABLE(CLASSNAME) \ class CLASSNAME; \ static bool CLASSNAME##Registered \ = (GameObjectFactory::GetInstance().GetGameObjectRegistry()[#CLASSNAME] = &GameObjectFactory::SpawnObject<CLASSNAME>, true) // Registers the class with the factory's registry, and connects that to a templated SpawnGameObject function // Spawns a class of selected name #define SpawnGameObject GameObjectFactory::GetInstance().SpawnGameObjectByName // 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::unique_ptr<Object> SpawnGameObjectByName(const std::string& Name); std::unordered_map<std::string, std::function<std::unique_ptr<Object>()>>& GetGameObjectRegistry(); // Returns the Registry of class names private: std::unordered_map<std::string, std::function<std::unique_ptr<Object>()>> Registry; // Registry that maps class names to factory functions }; The following compiles, however, results in Foo being unable to being spawned through the Factory, meaning this macro doesn't work.
#define ENGINE_CLASS(CLASSNAME, MACROS[]) \ #define MACROS MACRO[] #define ENGINE_CLASS(CLASSNAME, MACROS) \ class CLASSNAME ...
ENGINE_CLASS(Foo, { ENGINE_SPAWNABLE }) : public GameObject { public: Foo() { printf("hey!"); } };
#define EVAL(r, data, elem) data(elem)and thenBOOST_PP_SEQ_FOR_EACH(EVAL, CLASSNAME, CLASSES)and the class list looks like(SPAWNABLE)(EDITORONLY)(OPTIMIZE)(RANDOMIZE)- this would expand toSPAWNABLE(CLASSNAME) EDITORONLY(CLASSNAME)etc. It's a huge hack and the preprocessor simply isn't very good at this sort of thing.