How can I have macros as arguments inside macros?

161 views Asked by At

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!"); }
};
1

There are 1 answers

2
dalfaB On

If the topic is just replacing several macros by only one, like:

ALL_MACROS( Dude, SPAWNABLE, EDITORONLY, OPTIMIZE )

It can be done, but defining ALL_MACROS requires several substeps:

#define PARENS   ()                    // to stop prepro-interpretation
#define ALL_MACROS_HLP_REM_PARENS() ALL_MACROS_HLP
#define ALL_MACROS_HLP(prm, mac1, ...)                  \
    mac1( prm );                                        \
    __VA_OPT__( ALL_MACROS_HLP_REM_PARENS PARENS (prm, __VA_ARGS__) )
#define DEEP_SOLVE(...)  DEEP_SOLVE1(DEEP_SOLVE1(DEEP_SOLVE1(DEEP_SOLVE1(__VA_ARGS__))))
#define DEEP_SOLVE1(...) DEEP_SOLVE2(DEEP_SOLVE2(DEEP_SOLVE2(DEEP_SOLVE2(__VA_ARGS__))))
#define DEEP_SOLVE2(...) DEEP_SOLVE3(DEEP_SOLVE3(DEEP_SOLVE3(DEEP_SOLVE3(__VA_ARGS__))))
#define DEEP_SOLVE3(...) IDEM(IDEM(IDEM(IDEM(IDEM(IDEM(IDEM(__VA_ARGS__)))))))
#define IDEM(...)       __VA_ARGS__   // to force prepro-interpretation
#define ALL_MACROS(prm, mac1, ...)                      \
     DEEP_SOLVE( ALL_MACROS_HLP(prm, mac1, __VA_ARGS__) )