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