MNX Document Model
Loading...
Searching...
No Matches
BoilerplateMacros.h
1/*
2 * Copyright (C) 2025, Robert Patterson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22#pragma once
23
24#ifndef DOXYGEN_SHOULD_IGNORE_THIS
25// Preprocessor helpers to build child macro overloads and parameter lists.
26#define MNX_PP_CAT(a, b) MNX_PP_CAT_I(a, b)
27#define MNX_PP_CAT_I(a, b) a##b
28
29#define MNX_PP_NARG(...) MNX_PP_NARG_I(__VA_ARGS__, MNX_PP_RSEQ_N(), 0)
30#define MNX_PP_NARG_I(...) MNX_PP_ARG_N(__VA_ARGS__)
31#define MNX_PP_ARG_N( \
32 _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
33#define MNX_PP_RSEQ_N() 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
34
35#define MNX_FOR_EACH_COMMA(m, ...) MNX_PP_CAT(MNX_FOR_EACH_COMMA_, MNX_PP_NARG(__VA_ARGS__))(m, __VA_ARGS__)
36#define MNX_FOR_EACH_COMMA_1(m, x) m(x)
37#define MNX_FOR_EACH_COMMA_2(m, x, ...) m(x), MNX_FOR_EACH_COMMA_1(m, __VA_ARGS__)
38#define MNX_FOR_EACH_COMMA_3(m, x, ...) m(x), MNX_FOR_EACH_COMMA_2(m, __VA_ARGS__)
39#define MNX_FOR_EACH_COMMA_4(m, x, ...) m(x), MNX_FOR_EACH_COMMA_3(m, __VA_ARGS__)
40#define MNX_FOR_EACH_COMMA_5(m, x, ...) m(x), MNX_FOR_EACH_COMMA_4(m, __VA_ARGS__)
41#define MNX_FOR_EACH_COMMA_6(m, x, ...) m(x), MNX_FOR_EACH_COMMA_5(m, __VA_ARGS__)
42#define MNX_FOR_EACH_COMMA_7(m, x, ...) m(x), MNX_FOR_EACH_COMMA_6(m, __VA_ARGS__)
43#define MNX_FOR_EACH_COMMA_8(m, x, ...) m(x), MNX_FOR_EACH_COMMA_7(m, __VA_ARGS__)
44#define MNX_FOR_EACH_COMMA_9(m, x, ...) m(x), MNX_FOR_EACH_COMMA_8(m, __VA_ARGS__)
45#define MNX_FOR_EACH_COMMA_10(m, x, ...) m(x), MNX_FOR_EACH_COMMA_9(m, __VA_ARGS__)
46#define MNX_FOR_EACH_COMMA_11(m, x, ...) m(x), MNX_FOR_EACH_COMMA_10(m, __VA_ARGS__)
47#define MNX_FOR_EACH_COMMA_12(m, x, ...) m(x), MNX_FOR_EACH_COMMA_11(m, __VA_ARGS__)
48#define MNX_FOR_EACH_COMMA_13(m, x, ...) m(x), MNX_FOR_EACH_COMMA_12(m, __VA_ARGS__)
49#define MNX_FOR_EACH_COMMA_14(m, x, ...) m(x), MNX_FOR_EACH_COMMA_13(m, __VA_ARGS__)
50#define MNX_FOR_EACH_COMMA_15(m, x, ...) m(x), MNX_FOR_EACH_COMMA_14(m, __VA_ARGS__)
51#define MNX_FOR_EACH_COMMA_16(m, x, ...) m(x), MNX_FOR_EACH_COMMA_15(m, __VA_ARGS__)
52
53#define MNX_FIELD_TYPE(pair) MNX_FIELD_TYPE_I pair
54#define MNX_FIELD_TYPE_I(type, name) type
55
56#define MNX_FIELD_NAME(pair) MNX_FIELD_NAME_I pair
57#define MNX_FIELD_NAME_I(type, name) name
58
59#define MNX_FIELD_PARAM(pair) MNX_FIELD_TYPE(pair) MNX_FIELD_NAME(pair)
60#define MNX_FIELDS_PARAMS(...) MNX_FOR_EACH_COMMA(MNX_FIELD_PARAM, __VA_ARGS__)
61#define MNX_FIELDS_NAMES(...) MNX_FOR_EACH_COMMA(MNX_FIELD_NAME, __VA_ARGS__)
62#endif // DOXYGEN_SHOULD_IGNORE_THIS
63
75#define MNX_REQUIRED_PROPERTY(TYPE, NAME) \
76 [[nodiscard]] TYPE NAME() const { \
77 if (!ref().contains(#NAME)) { \
78 throw std::runtime_error("Missing required property: " #NAME); \
79 } \
80 return ref()[#NAME].get<TYPE>(); \
81 } \
82 void set_##NAME(const TYPE& value) { ref()[#NAME] = value; } \
83 static_assert(true, "") // require semicolon after macro
84
97#define MNX_ARRAY_ELEMENT_PROPERTY(TYPE, NAME, INDEX) \
98 static_assert(std::is_integral_v<decltype(INDEX)>, "array index must be an integer type"); \
99 [[nodiscard]] TYPE NAME() const { return (*this)[INDEX]; } \
100 void set_##NAME(const TYPE& value) { (*this)[INDEX] = value; } \
101 static_assert(true, "") // require semicolon after macro
102
119#define MNX_OPTIONAL_NAMED_PROPERTY(TYPE, NAME, KEY) \
120 [[nodiscard]] std::optional<TYPE> NAME() const { \
121 return ref().contains(KEY) ? std::optional<TYPE>(ref()[KEY].get<TYPE>()) : std::nullopt; \
122 } \
123 [[nodiscard]] TYPE NAME##_or(const TYPE& defaultVal) const { \
124 return ref().contains(KEY) ? ref()[KEY].get<TYPE>() : defaultVal; \
125 } \
126 void set_##NAME(const TYPE& value) { ref()[KEY] = value; } \
127 void clear_##NAME() { ref().erase(KEY); } \
128 static_assert(true, "") // require semicolon after macro
129
143#define MNX_OPTIONAL_PROPERTY(TYPE, NAME) MNX_OPTIONAL_NAMED_PROPERTY(TYPE, NAME, #NAME)
144
160#define MNX_OPTIONAL_PROPERTY_WITH_DEFAULT(TYPE, NAME, DEFAULT) \
161 [[nodiscard]] TYPE NAME() const { \
162 return ref().contains(#NAME) ? ref()[#NAME].get<TYPE>() : DEFAULT; \
163 } \
164 void set_##NAME(const TYPE& value) { ref()[#NAME] = value; } \
165 void clear_##NAME() { ref().erase(#NAME); } \
166 void set_or_clear_##NAME(const TYPE& value) { \
167 if (value == DEFAULT) clear_##NAME(); \
168 else set_##NAME(value); \
169 } \
170 static_assert(true, "") // require semicolon after macro
171
189#define MNX_REQUIRED_CHILD(...) \
190 MNX_PP_CAT(MNX_REQUIRED_CHILD_, MNX_PP_NARG(__VA_ARGS__))(__VA_ARGS__)
191
192#ifndef DOXYGEN_SHOULD_IGNORE_THIS
193
194#define MNX_REQUIRED_CHILD_CORE(TYPE, NAME, CREATE_BODY) \
195 [[nodiscard]] TYPE NAME() const { return getChild<TYPE>(#NAME); } \
196 CREATE_BODY \
197 static_assert(true, "") // require semicolon after macro
198
199#define MNX_REQUIRED_CHILD_2(TYPE, NAME) \
200 MNX_REQUIRED_CHILD_CORE(TYPE, NAME, \
201 TYPE create_##NAME() { return setChild(#NAME, TYPE(*this, #NAME)); } \
202 )
203
204#define MNX_REQUIRED_CHILD_3(TYPE, NAME, ...) \
205 MNX_REQUIRED_CHILD_CORE(TYPE, NAME, \
206 TYPE create_##NAME(MNX_FIELDS_PARAMS(__VA_ARGS__)) { \
207 return setChild(#NAME, TYPE(*this, #NAME, MNX_FIELDS_NAMES(__VA_ARGS__))); \
208 } \
209 )
210
211#define MNX_REQUIRED_CHILD_4(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
212#define MNX_REQUIRED_CHILD_5(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
213#define MNX_REQUIRED_CHILD_6(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
214#define MNX_REQUIRED_CHILD_7(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
215#define MNX_REQUIRED_CHILD_8(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
216#define MNX_REQUIRED_CHILD_9(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
217#define MNX_REQUIRED_CHILD_10(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
218#define MNX_REQUIRED_CHILD_11(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
219#define MNX_REQUIRED_CHILD_12(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
220#define MNX_REQUIRED_CHILD_13(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
221#define MNX_REQUIRED_CHILD_14(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
222#define MNX_REQUIRED_CHILD_15(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
223#define MNX_REQUIRED_CHILD_16(TYPE, NAME, ...) MNX_REQUIRED_CHILD_3(TYPE, NAME, __VA_ARGS__)
224
225#endif // DOXYGEN_SHOULD_IGNORE_THIS
226
245#define MNX_OPTIONAL_CHILD(...) \
246 MNX_PP_CAT(MNX_OPTIONAL_CHILD_, MNX_PP_NARG(__VA_ARGS__))(__VA_ARGS__)
247
248#ifndef DOXYGEN_SHOULD_IGNORE_THIS
249
250#define MNX_OPTIONAL_CHILD_CORE(TYPE, NAME, ENSURE_BODY) \
251 [[nodiscard]] std::optional<TYPE> NAME() const { return getOptionalChild<TYPE>(#NAME); } \
252 ENSURE_BODY \
253 void clear_##NAME() { ref().erase(#NAME); } \
254 static_assert(true, "") // require semicolon after macro
255
256#define MNX_OPTIONAL_CHILD_2(TYPE, NAME) \
257 MNX_OPTIONAL_CHILD_CORE(TYPE, NAME, \
258 TYPE ensure_##NAME() { \
259 if (auto child = getOptionalChild<TYPE>(#NAME)) return child.value(); \
260 return setChild(#NAME, TYPE(*this, #NAME)); \
261 } \
262 )
263
264#define MNX_OPTIONAL_CHILD_3(TYPE, NAME, ...) \
265 MNX_OPTIONAL_CHILD_CORE(TYPE, NAME, \
266 TYPE ensure_##NAME(MNX_FIELDS_PARAMS(__VA_ARGS__)) { \
267 if (auto child = getOptionalChild<TYPE>(#NAME)) return child.value(); \
268 return setChild(#NAME, TYPE(*this, #NAME, MNX_FIELDS_NAMES(__VA_ARGS__))); \
269 } \
270 )
271
272#define MNX_OPTIONAL_CHILD_4(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
273#define MNX_OPTIONAL_CHILD_5(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
274#define MNX_OPTIONAL_CHILD_6(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
275#define MNX_OPTIONAL_CHILD_7(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
276#define MNX_OPTIONAL_CHILD_8(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
277#define MNX_OPTIONAL_CHILD_9(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
278#define MNX_OPTIONAL_CHILD_10(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
279#define MNX_OPTIONAL_CHILD_11(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
280#define MNX_OPTIONAL_CHILD_12(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
281#define MNX_OPTIONAL_CHILD_13(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
282#define MNX_OPTIONAL_CHILD_14(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
283#define MNX_OPTIONAL_CHILD_15(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
284#define MNX_OPTIONAL_CHILD_16(TYPE, NAME, ...) MNX_OPTIONAL_CHILD_3(TYPE, NAME, __VA_ARGS__)
285
286#endif // DOXYGEN_SHOULD_IGNORE_THIS
287
288#define MNX_ASSERT_IF(TEST) \
289assert(!(TEST)); \
290if (TEST)