tagval 0.2.0
Modern C++23 header-only library of tagged values (open/closed enumerations with metadata)
Loading...
Searching...
No Matches
macros.hpp
Go to the documentation of this file.
1#pragma once
2
14
15#include <tagval/entry.hpp>
18
19// `[[gnu::used]]` keeps the inline registrar variable in the final binary even
20// when nothing else in its translation unit is referenced — required for extern
21// entries built into a static archive. GCC and Clang (including Apple Clang)
22// honour the attribute; MSVC needs `/INCLUDE:<mangled>` on the linker line and
23// is not handled here (CI does not exercise MSVC for v0.1.0).
24#if defined(__GNUC__) || defined(__clang__)
25#define TAGVAL_DETAIL_USED [[gnu::used]]
26#else
27#define TAGVAL_DETAIL_USED
28#endif
29
30// In-class entry whose code is derived from the function name.
31//
32// TAGVAL_ENTRY(Status, Active, active);
33// TAGVAL_ENTRY(Status, Inactive, inactive, "Inactive", "mdi:off");
34#define TAGVAL_ENTRY(Owner, TypeName, FuncName, ...) \
35 struct TypeName : ::tagval::Entry<Owner, #FuncName __VA_OPT__(, ) __VA_ARGS__> {}; \
36 [[maybe_unused]] [[nodiscard]] static auto const& FuncName() { \
37 return base_t::template value<TypeName>(); \
38 }
39
40// In-class entry with explicit code:
41//
42// TAGVAL_ENTRY_AS(Status, Archived, is_archived, "archived", "Archived");
43#define TAGVAL_ENTRY_AS(Owner, TypeName, FuncName, Code, ...) \
44 struct TypeName : ::tagval::Entry<Owner, Code __VA_OPT__(, ) __VA_ARGS__> {}; \
45 [[maybe_unused]] [[nodiscard]] static auto const& FuncName() { \
46 return base_t::template value<TypeName>(); \
47 }
48
49// External entry: declared at namespace scope, always registers.
50#define TAGVAL_EXTERN_ENTRY(Owner, TypeName, FuncName, ...) \
51 struct TypeName : ::tagval::Entry<Owner, #FuncName __VA_OPT__(, ) __VA_ARGS__> {}; \
52 [[maybe_unused]] [[nodiscard]] inline auto const& FuncName() { \
53 return Owner::template value<TypeName>(); \
54 } \
55 /* NOLINTNEXTLINE(readability-identifier-naming) */ \
56 TAGVAL_DETAIL_USED [[maybe_unused]] inline const bool TypeName##_registered_ = [] { \
57 ::tagval::OpenEndedRegistry<Owner>::add(&::tagval::metadata_v<TypeName>); \
58 return true; \
59 }()
60
61#define TAGVAL_EXTERN_ENTRY_AS(Owner, TypeName, FuncName, Code, ...) \
62 struct TypeName : ::tagval::Entry<Owner, Code __VA_OPT__(, ) __VA_ARGS__> {}; \
63 [[maybe_unused]] [[nodiscard]] inline auto const& FuncName() { \
64 return Owner::template value<TypeName>(); \
65 } \
66 /* NOLINTNEXTLINE(readability-identifier-naming) */ \
67 TAGVAL_DETAIL_USED [[maybe_unused]] inline const bool TypeName##_registered_ = [] { \
68 ::tagval::OpenEndedRegistry<Owner>::add(&::tagval::metadata_v<TypeName>); \
69 return true; \
70 }()
71
72// Internal helpers for token pasting that survives one round of macro
73// expansion — needed so __LINE__ in TAGVAL_REGISTER_KIND actually expands
74// before the ##. Don't use directly.
75#define TAGVAL_DETAIL_PASTE2(a, b) a##b
76#define TAGVAL_DETAIL_PASTE(a, b) TAGVAL_DETAIL_PASTE2(a, b)
77
78// Register a kind in the program-wide tagval::KindRegistry. Place at
79// namespace scope (typically immediately after the kind's class definition,
80// in either a header or a .cpp). Opt-in: kinds that are never registered do
81// not appear in KindRegistry::all(). Idempotent on the kind's id; the same
82// macro placed in multiple TUs is safe.
83//
84// TAGVAL_REGISTER_KIND(Status);
85// TAGVAL_REGISTER_KIND(::vendor::PaymentMethod);
86//
87// The kind argument may be fully qualified — the registrar variable name is
88// generated from __LINE__ so namespace-qualified types work directly without
89// an extra alias. (`__COUNTER__` would also work but is flagged as a C2y
90// extension under -Wc2y-extensions.) Limitation: two TAGVAL_REGISTER_KIND
91// invocations on the same line of the same TU will collide — keep them on
92// separate lines.
93//
94// Inherits the static-archive caveat from TAGVAL_EXTERN_ENTRY: if the macro
95// lands in a TU that's only inside a .a and nothing else in that TU is
96// referenced from the consumer, the registrar may be dead-stripped. Same
97// workarounds apply (CMake OBJECT library, --whole-archive, -force_load).
98#define TAGVAL_REGISTER_KIND(KindType) \
99 /* NOLINTNEXTLINE(readability-identifier-naming) */ \
100 TAGVAL_DETAIL_USED [[maybe_unused]] inline const bool TAGVAL_DETAIL_PASTE( \
101 tagval_kind_registered_, __LINE__) = [] { \
102 ::tagval::KindRegistry::register_kind<KindType>(); \
103 return true; \
104 }()
Compile-time Entry NTTP and the runtime-view TagValMetadata struct.
Program-wide registry of every tag-value kind that opts in via the TAGVAL_REGISTER_KIND macro.
Per-Owner runtime registry used by OpenEnded kinds.