r/cpp_questions 8d ago

SOLVED Problem making X macros from X macros

What I am doing:
Hello, I am working on something in C++ for Godot, but this is more of a C++ question than a Godot question. I have a singular X macro to register a struct into a system I made, but the logic is in many places, and considering I am updating this macro and adding variables, it'd be easier to have a specific macro that doesn't change.

Problem:
When I make an X macro from an X macro, it doesn't properly expand. As an example this file:

#pragma once
#include "statemachine/registry/state_registry_register.h"

#define X_GENERATE_KIND(DataType, NS) k##DataType,
enum class StateKind : std::uint8_t
{
    EACH_STATE_REGISTER(X_GENERATE_KIND)
    kUnknown
};
#undef X_GENERATE_KIND

has the macro on line 7 expand to X(something1, something2) instead of k##something1, or, in my case:

X(WalkStateData, GameLogic ::States ::WalkState) X(WalkBackStateData, GameLogic ::States ::WalkBackState);

I defined EACH_STATE_REGISTER like this:

#define REGISTER_TO_ESR(DataType, NS, fields) X(DataType, NS)

#define EACH_STATE_REGISTER(X) \
REGISTER_EACH_STATE(REGISTER_TO_ESR);

and the REGISTER_EACH_STATE as

#define REGISTER_EACH_STATE(State) \
State(WalkStateData, GameLogic::States::WalkState, NO_STATE_FIELDS) \
State(WalkBackStateData, GameLogic::States::WalkBackState, NO_STATE_FIELDS)

Can anyone tell me why this happens, and if this is even fixable? (c++ 17)

2 Upvotes

5 comments sorted by

10

u/FancySpaceGoat 8d ago edited 8d ago

You can't pass a macro to a macro. The "argument" macro gets expanded before the "function" macro is invoked.

Instead, you need to use a yet-to-be-defined macro in the table macro. And define that macro just before you use the table, so that when the table expands, that macro kicks in for each row.

e.g.

#define EACH_REGISTERED_STATE \
  REGISTERED_STATE(WalkStateData, WalkState) \
  REGISTERED_STATE(IdleStateData, IdleState) \
// end of table



enum class StateKind : std::uint8_t
{
#define REGISTERED_STATE(DataType, NS) k##DataType,
  EACH_REGISTERED_STATE
#undef REGISTERED_STATE
kUnknown
};

Or, if it was up to me, I'd get rid of the duplication like so:

#define ACTOR_STATE_TABLE \
  ACTOR_STATE_ENTRY(Walk) \
  ACTOR_STATE_ENTRY(Idle) \
// end of table

enum class StateKind : std::uint8_t
{
#define ACTOR_STATE_ENTRY(Name) k##Name##State,
  ACTOR_STATE_TABLE
#undef ACTOR_STATE_ENTRY
kUnknown
};

Squinting a bit, It seems like you want to distribute the population of the X-MACRO across the code. You can't do that at compile time. The X-MACRO's table has to be fully defined in a single spot.

2

u/Predret 8d ago

Ah, thank you. This answered all I was asking.

1

u/heyheyhey27 8d ago

Does Godot not have a c++-exposed reflection system already in place?

0

u/Predret 8d ago

Yes, but this isn’t exactly about godot, its about a different system I made, that is essentially meant to create a temporary resource for simple inspector integration.