lamppp
Loading...
Searching...
No Matches
macros.hpp
1/*
2Copied from this blog post: https://sillycross.github.io/2022/04/01/2022-04-01/
3I have no idea how this works, but I need it for some explicit instatiations and I don't want to
4include the whole Boost library just for the boost/preprocessor stuff. All credit goes to sillycross
5*/
6
7// google linter hates this code :p
8// NOLINTBEGIN
9
11// Macro utility 'PP_FOR_EACH' and 'PP_FOR_EACH_CARTESIAN_PRODUCT'
12// Apply macro to all elements in a list, or all elements in the Cartesian product of multiple lists
13// Requires C++20.
14//
15// ----------------
16// PP_FOR_EACH(m, ...)
17// Expands to m(l) for all l in list
18// m(l) will expand further if 'm' is a macro
19//
20// Example: PP_FOR_EACH(m, 1, 2, 3) expands to m(1) m(2) m(3)
21//
22// ----------------
23// PP_FOR_EACH_CARTESIAN_PRODUCT(m, lists...)
24//
25// Expands to m(l) for all l in List1 * ... * ListN where * denotes Cartesian product.
26// m(l) will expand further if 'm' is a macro
27// The terms are enumerated in lexical order of the lists
28//
29// Example:
30// PP_FOR_EACH_CARTESIAN_PRODUCT(m, (1,2), (A,B), (x,y))
31// expands to
32// m(1,A,x) m(1,A,y) m(1,B,x) m(1,B,y) m(2,A,x) m(2,A,y) m(2,B,x) m(2,B,y)
33//
34// The implementation is inspired by the following articles:
35// https://www.scs.stanford.edu/~dm/blog/va-opt.html
36// https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
37// https://stackoverflow.com/questions/2308243/macro-returning-the-number-of-arguments-it-is-given-in-c
38//
39
40#define LMP_CAT(a, ...) LMP_PRIMITIVE_CAT(a, __VA_ARGS__)
41#define LMP_PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
42
43// LMP_IS_EXACTLY_TWO_ARGS(...)
44// Expands to 1 if exactly two parameters is passed in, otherwise expands to 0
45//
46#define LMP_IS_EXACTLY_TWO_ARGS(...) \
47 LMP_GET_FIRST_ARG(__VA_OPT__(LMP_IS_TWO_ARGS_IMPL1(__VA_ARGS__), ) 0)
48#define LMP_GET_FIRST_ARG(a, ...) a
49#define LMP_IS_TWO_ARGS_IMPL1(p1, ...) \
50 LMP_GET_FIRST_ARG(__VA_OPT__(LMP_IS_TWO_ARGS_IMPL2(__VA_ARGS__), ) 0)
51#define LMP_IS_TWO_ARGS_IMPL2(p1, ...) LMP_GET_FIRST_ARG(__VA_OPT__(0, ) 1)
52
53// LMP_IS_ZERO(x): Expands to 1 if x is 0, otherwise expands to 0
54//
55#define LMP_IS_ZERO(x) \
56 LMP_IS_EXACTLY_TWO_ARGS(LMP_PRIMITIVE_CAT(LMP_IS_ZERO_IMPL_, x))
57#define LMP_IS_ZERO_IMPL_0 0, 0
58
59// LMP_EXPAND_LIST((list)): Expands to list (i.e. the parenthesis is removed)
60//
61#define LMP_EXPAND_LIST_IMPL(...) __VA_ARGS__
62#define LMP_EXPAND_LIST(...) LMP_EXPAND_LIST_IMPL __VA_ARGS__
63#define LMP_ADD_COMMA_IF_NONEMPTY(...) __VA_OPT__(__VA_ARGS__, )
64#define LMP_EXPAND_LIST_TRAIL_COMMA(...) \
65 LMP_ADD_COMMA_IF_NONEMPTY(LMP_EXPAND_LIST_IMPL __VA_ARGS__)
66
67// LMP_IF_EQUAL_ZERO(cond)((true_br), (false_br))
68// Expands to true_br if cond is 0, otherwise expands to false_br
69//
70#define LMP_IF_EQUAL_ZERO(cond) \
71 LMP_CAT(LMP_IF_EQUAL_ZERO_IMPL_, LMP_IS_ZERO(cond))
72#define LMP_IF_EQUAL_ZERO_IMPL_1(truebr, falsebr) LMP_EXPAND_LIST(truebr)
73#define LMP_IF_EQUAL_ZERO_IMPL_0(truebr, falsebr) LMP_EXPAND_LIST(falsebr)
74
75// LMP_INC(x) increments x
76//
77#define LMP_INC(x) LMP_PRIMITIVE_CAT(LMP_INC_, x)
78#define LMP_INC_0 1
79#define LMP_INC_1 2
80#define LMP_INC_2 3
81#define LMP_INC_3 4
82#define LMP_INC_4 5
83#define LMP_INC_5 6
84#define LMP_INC_6 7
85#define LMP_INC_7 8
86#define LMP_INC_8 9
87#define LMP_INC_9 10
88#define LMP_INC_10 11
89#define LMP_INC_11 12
90#define LMP_INC_12 13
91#define LMP_INC_13 14
92#define LMP_INC_14 15
93#define LMP_INC_15 16
94#define LMP_INC_16 17
95#define LMP_INC_17 18
96#define LMP_INC_18 19
97#define LMP_INC_19 19
98
99// LMP_DEC(x) decrements x
100//
101#define LMP_DEC(x) LMP_PRIMITIVE_CAT(LMP_DEC_, x)
102#define LMP_DEC_0 0
103#define LMP_DEC_1 0
104#define LMP_DEC_2 1
105#define LMP_DEC_3 2
106#define LMP_DEC_4 3
107#define LMP_DEC_5 4
108#define LMP_DEC_6 5
109#define LMP_DEC_7 6
110#define LMP_DEC_8 7
111#define LMP_DEC_9 8
112#define LMP_DEC_10 9
113#define LMP_DEC_11 10
114#define LMP_DEC_12 11
115#define LMP_DEC_13 12
116#define LMP_DEC_14 13
117#define LMP_DEC_15 14
118#define LMP_DEC_16 15
119#define LMP_DEC_17 16
120#define LMP_DEC_18 17
121#define LMP_DEC_19 18
122
123// LMP_COUNT_ARGS(...): returns the total number of arguments
124// https://stackoverflow.com/questions/2308243/macro-returning-the-number-of-arguments-it-is-given-in-c
125//
126#define LMP_COUNT_ARGS(...) \
127 LMP_COUNT_ARGS_IMPL(__VA_ARGS__ __VA_OPT__(, ) LMP_COUNT_ARGS_IMPL_SEQ())
128#define LMP_COUNT_ARGS_IMPL(...) LMP_COUNT_ARGS_IMPL_GET_64TH_ARG(__VA_ARGS__)
129#define LMP_COUNT_ARGS_IMPL_GET_64TH_ARG( \
130 a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, \
131 a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, \
132 a32, a33, a34, a35, a36, a37, a38, a39, a40, a41, a42, a43, a44, a45, a46, \
133 a47, a48, a49, a50, a51, a52, a53, a54, a55, a56, a57, a58, a59, a60, a61, \
134 a62, a63, N, ...) \
135 N
136#define LMP_COUNT_ARGS_IMPL_SEQ() \
137 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, \
138 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, \
139 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, \
140 8, 7, 6, 5, 4, 3, 2, 1, 0
141
142// Takes "a list where the first element is also a list" and a value, perform a rotation like below:
143// (L1, v2, v3, v4), v5 => (v2, v3, v4, v5), expanded(L1)
144//
145#define LMP_LIST_ROTATION(a, b) LMP_LIST_ROTATION_IMPL1(LMP_EXPAND_LIST(a), b)
146#define LMP_LIST_ROTATION_IMPL1(...) LMP_LIST_ROTATION_IMPL2(__VA_ARGS__)
147#define LMP_LIST_ROTATION_IMPL2(a, ...) (__VA_ARGS__), LMP_EXPAND_LIST(a)
148
149#define LMP_PARENS ()
150
151#define LMP_CARTESIAN_IMPL_ENTRY(dimLeft, macro, vpack, ...) \
152 __VA_OPT__(LMP_CARTESIAN_IMPL_AGAIN_2 LMP_PARENS(dimLeft, macro, vpack, \
153 __VA_ARGS__))
154
155#define LMP_CARTESIAN_EMIT_ONE(macro, ...) macro(__VA_ARGS__)
156#define LMP_CARTESIAN_EMIT_ONE_PARAMS(vpack, vfirst) \
157 LMP_EXPAND_LIST_TRAIL_COMMA(vpack) vfirst
158
159#define LMP_CARTESIAN_IMPL(dimLeft, macro, vpack, vfirst, ...) \
160 LMP_IF_EQUAL_ZERO(dimLeft) \
161 ((LMP_CARTESIAN_EMIT_ONE(macro, \
162 LMP_CARTESIAN_EMIT_ONE_PARAMS(vpack, vfirst))), \
163 (LMP_CARTESIAN_IMPL_ENTRY_AGAIN_2 LMP_PARENS( \
164 LMP_DEC(dimLeft), macro, LMP_LIST_ROTATION(vpack, vfirst)))) \
165 __VA_OPT__(LMP_CARTESIAN_IMPL_AGAIN LMP_PARENS(dimLeft, macro, vpack, \
166 __VA_ARGS__))
167
168#define LMP_CARTESIAN_IMPL_AGAIN_2() LMP_CARTESIAN_IMPL_AGAIN LMP_PARENS
169#define LMP_CARTESIAN_IMPL_AGAIN() LMP_CARTESIAN_IMPL
170#define LMP_CARTESIAN_IMPL_ENTRY_AGAIN_2() \
171 LMP_CARTESIAN_IMPL_ENTRY_AGAIN LMP_PARENS
172#define LMP_CARTESIAN_IMPL_ENTRY_AGAIN() LMP_CARTESIAN_IMPL_ENTRY
173
174#define LMP_EXPAND(...) \
175 LMP_EXPAND4(LMP_EXPAND4(LMP_EXPAND4(LMP_EXPAND4(__VA_ARGS__))))
176#define LMP_EXPAND4(...) \
177 LMP_EXPAND3(LMP_EXPAND3(LMP_EXPAND3(LMP_EXPAND3(__VA_ARGS__))))
178#define LMP_EXPAND3(...) \
179 LMP_EXPAND2(LMP_EXPAND2(LMP_EXPAND2(LMP_EXPAND2(__VA_ARGS__))))
180#define LMP_EXPAND2(...) \
181 LMP_EXPAND1(LMP_EXPAND1(LMP_EXPAND1(LMP_EXPAND1(__VA_ARGS__))))
182#define LMP_EXPAND1(...) __VA_ARGS__
183
184// FOR_EACH implementation from https://www.scs.stanford.edu/~dm/blog/va-opt.html
185// See comment at beginning of this file
186//
187#define LMP_FOR_EACH(macro, ...) \
188 __VA_OPT__(LMP_EXPAND(LMP_FOR_EACH_HELPER(macro, __VA_ARGS__)))
189#define LMP_FOR_EACH_HELPER(macro, a1, ...) \
190 macro(a1) __VA_OPT__(LMP_FOR_EACH_AGAIN LMP_PARENS(macro, __VA_ARGS__))
191#define LMP_FOR_EACH_AGAIN() LMP_FOR_EACH_HELPER
192#define LMP_FOR_EACH_INDIRECTION(...) LMP_FOR_EACH(__VA_ARGS__)
193
194// FOR_EACH_CARTESIAN_PRODUCT(macro, lists...)
195// See comment at beginning of this file
196//
197#define LMP_FOR_EACH_CARTESIAN_PRODUCT(macro, list1, ...) \
198 LMP_EXPAND(LMP_CARTESIAN_IMPL_ENTRY(LMP_COUNT_ARGS(__VA_ARGS__), macro, \
199 (__VA_ARGS__), LMP_EXPAND_LIST(list1)))
200
202
203// NOLINTEND