Summary
BRIDGE_REGISTER_MODEL / BRIDGE_REGISTER_ACTION build the name of their generated
static registrar by token-pasting the model and action types:
[[maybe_unused]] const bool bridge_action_reg_##M##_##A = ...
That works only when both arguments are bare identifiers. Passing a qualified type
pastes into an invalid preprocessor token and fails to compile.
Reproduction
namespace app::models {
struct Report {
struct Create {
static constexpr std::string_view Name = "Create";
struct Payload { int id = 0; };
struct Result { bool ok = false; };
using ReturnType = Result;
Payload payload;
};
Create::Result execute(Create const&);
};
} // namespace app::models
// Unqualified — fine:
// BRIDGE_REGISTER_MODEL(Report, "Report")
// Qualified — fails: the generated identifier contains ':' characters.
BRIDGE_REGISTER_MODEL(app::models::Report, "app.models.Report")
BRIDGE_REGISTER_ACTION(app::models::Report, app::models::Report::Create, "Create")
MSVC / clang-cl reject the pasted token; the error points at the macro expansion rather
than at the call site, which makes it slow to diagnose.
Why it matters
Any codebase that namespaces its models — or nests action types inside the model struct,
which the documented action shape encourages — cannot use the registration macros without
introducing a local alias purely to dodge the paste:
using ReportModel = app::models::Report; // only exists to satisfy the macro
BRIDGE_REGISTER_MODEL(ReportModel, "app.models.Report")
The existing tests and examples all register unqualified types, so this never shows up
in-tree.
Suggested fix
Key the generated names on __LINE__ instead of on the type spelling. There is exactly
one registration per line, so it is equally unique, and it is independent of how the type
is written:
#define BRIDGE_DETAIL_CAT_(a, b) a##b
#define BRIDGE_DETAIL_CAT(a, b) BRIDGE_DETAIL_CAT_(a, b)
[[maybe_unused]] const bool BRIDGE_DETAIL_CAT(bridge_action_reg_, __LINE__) = ...
Strictly more permissive: no behaviour change for existing callers, and unqualified
registrations keep compiling unchanged.
I'm happy to open a PR if the approach looks right.
Summary
BRIDGE_REGISTER_MODEL/BRIDGE_REGISTER_ACTIONbuild the name of their generatedstatic registrar by token-pasting the model and action types:
That works only when both arguments are bare identifiers. Passing a qualified type
pastes into an invalid preprocessor token and fails to compile.
Reproduction
MSVC / clang-cl reject the pasted token; the error points at the macro expansion rather
than at the call site, which makes it slow to diagnose.
Why it matters
Any codebase that namespaces its models — or nests action types inside the model struct,
which the documented action shape encourages — cannot use the registration macros without
introducing a local alias purely to dodge the paste:
The existing tests and examples all register unqualified types, so this never shows up
in-tree.
Suggested fix
Key the generated names on
__LINE__instead of on the type spelling. There is exactlyone registration per line, so it is equally unique, and it is independent of how the type
is written:
Strictly more permissive: no behaviour change for existing callers, and unqualified
registrations keep compiling unchanged.
I'm happy to open a PR if the approach looks right.