1 | /* Copyright (C) 2017 Povilas Kanapickas <povilas@radix.lt> |
2 | |
3 | Distributed under the Boost Software License, Version 1.0. |
4 | (See accompanying file LICENSE_1_0.txt or copy at |
5 | http://www.boost.org/LICENSE_1_0.txt) |
6 | */ |
7 | |
8 | #ifndef LIBSIMDPP_DISPATCH_MAKE_DISPATCHER_H |
9 | #define LIBSIMDPP_DISPATCH_MAKE_DISPATCHER_H |
10 | |
11 | #include <simdpp/dispatch/macros_generated.h> |
12 | #include <simdpp/detail/preprocessor/punctuation/remove_parens.hpp> |
13 | #include <simdpp/detail/preprocessor/seq/for_each.hpp> |
14 | #include <simdpp/detail/preprocessor/variadic/to_seq.hpp> |
15 | |
16 | #if SIMDPP_EMIT_DISPATCHER |
17 | #include <simdpp/detail/preprocessor/punctuation/comma_if.hpp> |
18 | #include <simdpp/detail/preprocessor/seq/elem.hpp> |
19 | #include <simdpp/detail/preprocessor/seq/for_each_i.hpp> |
20 | #include <simdpp/detail/preprocessor/tuple/rem.hpp> |
21 | #include <simdpp/dispatch/collect_macros_generated.h> |
22 | |
23 | // When debugging this code, it's a good idea to familiarize yourself with |
24 | // advanced preprocessor techniques first. Several resources follow: |
25 | // http://jhnet.co.uk/articles/cpp_magic |
26 | // https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms |
27 | // https://stackoverflow.com/questions/11632219/c-preprocessor-macro-specialisation-based-on-an-argument |
28 | // https://stackoverflow.com/questions/44758329/c-or-macro-magic-to-generate-method-and-forward-arguments |
29 | // https://stackoverflow.com/questions/11031062/c-preprocessor-avoid-code-repetition-of-member-variable-list |
30 | // https://stackoverflow.com/questions/11729168/how-to-get-function-signature-via-preprocessor-define-written-before-it |
31 | // Some ideas have been taken from these pages. |
32 | |
33 | // The libsimdpp library includes a partial copy of boost.preprocessor library |
34 | // with BOOST_ prefix replaced with SIMDPP_ and variadics support enabled |
35 | // unconditionally. If implementing new features, feel free to add any headers |
36 | // that are needed but are missing. In case a header becomes no longer used, |
37 | // please remove it from the repository. |
38 | |
39 | // The implementation has been affected by the following MSVC preprocessor |
40 | // bugs / non-standard behavior: |
41 | // |
42 | // - MSVC treats expanded variadic arguments as single token in other function |
43 | // argument lists. SIMDPP_DETAIL_MSVC_DEFER_MACRO_ARGS has been used as a |
44 | // workaround in these cases. |
45 | // |
46 | // - MSVC does not accept empty token as valid argument in function macros. In |
47 | // cases when this matter, SIMDPP_PP_EMPTY() was passed after the real |
48 | // arguments. |
49 | // |
50 | // - MSVC is eager to expand function macros before its arguments are fully |
51 | // expanded. This has been worked around with placing one or more |
52 | // SIMDPP_PP_EMPTY between the function macro and its argument list to defer |
53 | // the expansion. |
54 | // |
55 | // - MSVC sometimes stops the expansion of macros even when the expansion should |
56 | // continue. This is worked around by wrapping the code in SIMDPP_PP_EXPAND |
57 | |
58 | #define SIMDPP_DETAIL_MSVC_DEFER_MACRO_ARGS(macro, args) macro args |
59 | |
60 | #if ~SIMDPP_PP_CONFIG_FLAGS() & SIMDPP_PP_CONFIG_MSVC() |
61 | #define SIMDPP_DETAIL_IGNORE_PARENS(x) SIMDPP_PP_EAT x |
62 | #else |
63 | #define SIMDPP_DETAIL_IGNORE_PARENS_DEFER(x) SIMDPP_PP_EAT x |
64 | #define SIMDPP_DETAIL_IGNORE_PARENS(x) SIMDPP_DETAIL_IGNORE_PARENS_DEFER(x) |
65 | #endif |
66 | |
67 | #define SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(x) \ |
68 | SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_STRIP_REST_DEFER(SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_ADD_COMMA x,) |
69 | |
70 | #if ~SIMDPP_PP_CONFIG_FLAGS() & SIMDPP_PP_CONFIG_MSVC() |
71 | #define SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_STRIP_REST_DEFER(...) \ |
72 | SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_STRIP_REST(__VA_ARGS__) |
73 | |
74 | #define SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_STRIP_REST(x, ...) SIMDPP_PP_REM x |
75 | #else |
76 | #define SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_STRIP_REST_DEFER(...) \ |
77 | SIMDPP_DETAIL_MSVC_DEFER_MACRO_ARGS(SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_STRIP_REST, (__VA_ARGS__)) |
78 | |
79 | #define SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_STRIP_REST(x, ...) \ |
80 | SIMDPP_DETAIL_MSVC_DEFER_MACRO_ARGS(SIMDPP_PP_REM, x) |
81 | #endif |
82 | |
83 | #define SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST_ADD_COMMA(...) (__VA_ARGS__), |
84 | |
85 | // We can't just use SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST or |
86 | // SIMDPP_DETAIL_EXTRACT_ARG_IMPL directly, because the argument list may be |
87 | // empty, so SIMDPP_DETAIL_EXTRACT_* may be invoked with the argument being |
88 | // empty token. The argument otherwise always starts with a parenthesis, so |
89 | // let's check that. |
90 | #define SIMDPP_DETAIL_EXTRACT_TYPE(T) \ |
91 | SIMDPP_PP_IIF(SIMDPP_PP_IS_BEGIN_PARENS(T), \ |
92 | SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST, \ |
93 | SIMDPP_PP_EAT \ |
94 | )(T) |
95 | |
96 | #define SIMDPP_DETAIL_EXTRACT_ARG_IMPL(T) SIMDPP_PP_REM T |
97 | #define SIMDPP_DETAIL_EXTRACT_ARG(T) \ |
98 | SIMDPP_PP_IIF(SIMDPP_PP_IS_BEGIN_PARENS(T), \ |
99 | SIMDPP_DETAIL_EXTRACT_ARG_IMPL, \ |
100 | SIMDPP_PP_EAT \ |
101 | )(T) |
102 | |
103 | #define SIMDPP_DETAIL_EXTRACT_FORWARD_IMPL(T) SIMDPP_PP_EAT T |
104 | #define SIMDPP_DETAIL_EXTRACT_FORWARD(T) \ |
105 | SIMDPP_PP_IIF(SIMDPP_PP_IS_BEGIN_PARENS(T), \ |
106 | SIMDPP_DETAIL_EXTRACT_FORWARD_IMPL, \ |
107 | SIMDPP_PP_EAT \ |
108 | )(T) |
109 | |
110 | #define SIMDPP_DETAIL_TYPES_EACH(r, data, i, x) \ |
111 | SIMDPP_PP_COMMA_IF(i) SIMDPP_DETAIL_EXTRACT_TYPE(x) |
112 | |
113 | #define SIMDPP_DETAIL_ARGS_EACH(r, data, i, x) \ |
114 | SIMDPP_PP_COMMA_IF(i) SIMDPP_DETAIL_EXTRACT_ARG(x) |
115 | |
116 | #define SIMDPP_DETAIL_FORWARD_EACH(r, data, i, x) \ |
117 | SIMDPP_PP_COMMA_IF(i) SIMDPP_DETAIL_EXTRACT_FORWARD(x) |
118 | |
119 | // The following 3 macros expand a given function argument list with the |
120 | // following format '(A) a, (B) b, (C) c' into different results. |
121 | |
122 | // Will expand to 'A, B, C' |
123 | #define SIMDPP_DETAIL_TYPES(args) \ |
124 | SIMDPP_PP_SEQ_FOR_EACH_I(SIMDPP_DETAIL_TYPES_EACH, data, SIMDPP_PP_VARIADIC_TO_SEQ args) |
125 | |
126 | // Will expand to 'A a, B b, C c' |
127 | #define SIMDPP_DETAIL_ARGS(args) \ |
128 | SIMDPP_PP_SEQ_FOR_EACH_I(SIMDPP_DETAIL_ARGS_EACH, data, SIMDPP_PP_VARIADIC_TO_SEQ args) |
129 | |
130 | // Will expand to 'a, b, c' |
131 | #define SIMDPP_DETAIL_FORWARD(args) \ |
132 | SIMDPP_PP_SEQ_FOR_EACH_I(SIMDPP_DETAIL_FORWARD_EACH, data, SIMDPP_PP_VARIADIC_TO_SEQ args) |
133 | |
134 | // Will expand to 1 if argument contains SIMDPP_PP_PROBE macro anywhere, 0 otherwise |
135 | #if ~SIMDPP_PP_CONFIG_FLAGS() & SIMDPP_PP_CONFIG_MSVC() |
136 | #define SIMDPP_PP_PROBE_TO_BOOL(...) \ |
137 | SIMDPP_PP_PROBE_TO_BOOL_I(__VA_ARGS__, 0) |
138 | |
139 | #else |
140 | // Note that we can't use SIMDPP_DETAIL_MSVC_DEFER_MACRO_ARGS because this |
141 | // would interfere with the expansion of that macro itself |
142 | |
143 | #define SIMDPP_PP_PROBE_TO_BOOL(...) \ |
144 | SIMDPP_PP_PROBE_TO_BOOL_MSVC_DEFER(SIMDPP_PP_PROBE_TO_BOOL_I, (__VA_ARGS__, 0)) |
145 | #define SIMDPP_PP_PROBE_TO_BOOL_MSVC_DEFER(macro, args) macro args |
146 | #endif |
147 | |
148 | #define SIMDPP_PP_PROBE_TO_BOOL_I(n1, n2, ...) n2 |
149 | #define SIMDPP_PP_PROBE() ~, 1, 0 |
150 | |
151 | // Given a single argument R, potentially consisting of multiple tokens, |
152 | // expands to SIMDPP_PP_PROBE() if R is empty, otherwise expands to nothing. |
153 | #if ~SIMDPP_PP_CONFIG_FLAGS() & SIMDPP_PP_CONFIG_MSVC() |
154 | #define SIMDPP_DETAIL_PROBE_IF_VOID_EMPTY(R) \ |
155 | SIMDPP_PP_IIF( \ |
156 | SIMDPP_PP_PROBE_TO_BOOL(SIMDPP_DETAIL_IS_EMPTY_PROBE R ()), \ |
157 | SIMDPP_PP_PROBE, \ |
158 | SIMDPP_PP_EMPTY \ |
159 | )() |
160 | #else |
161 | #define SIMDPP_DETAIL_PROBE_IF_VOID_EMPTY(R) \ |
162 | SIMDPP_PP_IIF SIMDPP_PP_EMPTY()( \ |
163 | SIMDPP_PP_PROBE_TO_BOOL(SIMDPP_DETAIL_IS_EMPTY_PROBE R ()), \ |
164 | SIMDPP_PP_PROBE, \ |
165 | SIMDPP_PP_EMPTY \ |
166 | )() |
167 | #endif |
168 | |
169 | #define SIMDPP_DETAIL_IS_EMPTY_PROBE(...) SIMDPP_PP_PROBE() |
170 | |
171 | // Given a single argument R, potentially consisting of multiple tokens or |
172 | // parenthesized token groups, expands to nothing if R starts with a parenthesis. |
173 | // Otherwise expands to SIMDPP_DETAIL_PROBE_IF_VOID_EMPTY with R |
174 | // passed as an argument. |
175 | #if ~SIMDPP_PP_CONFIG_FLAGS() & SIMDPP_PP_CONFIG_MSVC() |
176 | #define SIMDPP_DETAIL_PROBE_IF_VOID_1PAREN(R) \ |
177 | SIMDPP_PP_IIF(SIMDPP_PP_IS_BEGIN_PARENS(R), \ |
178 | SIMDPP_PP_EAT, \ |
179 | SIMDPP_DETAIL_PROBE_IF_VOID_EMPTY \ |
180 | )(R) |
181 | #else |
182 | // R might be empty token, thus workarounds are needed (see top of the file) |
183 | |
184 | #define SIMDPP_DETAIL_PROBE_IF_VOID_1PAREN(R) \ |
185 | SIMDPP_PP_IIF(SIMDPP_PP_IS_BEGIN_PARENS(R), \ |
186 | SIMDPP_PP_EAT, \ |
187 | SIMDPP_DETAIL_PROBE_IF_VOID_EMPTY \ |
188 | )(R SIMDPP_PP_EMPTY()) |
189 | #endif |
190 | |
191 | // Given a single argument R, potentially consisting of multiple tokens or |
192 | // parenthesized token groups, expands to nothing if R starts with a 'void' |
193 | // token. Otherwise expands to SIMDPP_DETAIL_PROBE_IF_VOID_1PAREN |
194 | // passing R with the first 'void' token removed as an argument. |
195 | |
196 | #if ~SIMDPP_PP_CONFIG_FLAGS() & SIMDPP_PP_CONFIG_MSVC() |
197 | #define SIMDPP_DETAIL_PROBE_IF_VOID_1PARAM(R) \ |
198 | SIMDPP_PP_IIF( \ |
199 | SIMDPP_PP_PROBE_TO_BOOL(SIMDPP_DETAIL_STARTS_WITH_VOID_PROBE(R)), \ |
200 | SIMDPP_DETAIL_PROBE_IF_VOID_1PAREN, \ |
201 | SIMDPP_PP_EAT \ |
202 | )(SIMDPP_PP_CAT(SIMDPP_DETAIL_STARTS_WITH_VOID_STRIP_, R)) |
203 | #else |
204 | // SIMDPP_PP_IIF is expanded one expansion too early and R might be empty |
205 | // token, thus workarounds are needed (see top of the file) |
206 | |
207 | #define SIMDPP_DETAIL_PROBE_IF_VOID_1PARAM(R) \ |
208 | SIMDPP_PP_IIF SIMDPP_PP_EMPTY()( \ |
209 | SIMDPP_PP_PROBE_TO_BOOL(SIMDPP_DETAIL_STARTS_WITH_VOID_PROBE(R SIMDPP_PP_EMPTY())), \ |
210 | SIMDPP_DETAIL_PROBE_IF_VOID_1PAREN, \ |
211 | SIMDPP_PP_EAT \ |
212 | )(SIMDPP_PP_CAT(SIMDPP_DETAIL_STARTS_WITH_VOID_STRIP_, R) SIMDPP_PP_EMPTY SIMDPP_PP_EMPTY()()) |
213 | #endif |
214 | |
215 | #define SIMDPP_DETAIL_STARTS_WITH_VOID_CHECK_void SIMDPP_PP_PROBE() |
216 | #define SIMDPP_DETAIL_STARTS_WITH_VOID_STRIP_void |
217 | #define SIMDPP_DETAIL_STARTS_WITH_VOID_PROBE(R) \ |
218 | SIMDPP_DETAIL_STARTS_WITH_VOID_PROBE_I(SIMDPP_DETAIL_STARTS_WITH_VOID_CHECK_ ## R) |
219 | #define SIMDPP_DETAIL_STARTS_WITH_VOID_PROBE_I(R) R |
220 | |
221 | // Given a parenthesized list of arguments R, expands to nothing if R contains |
222 | // more than one argument. Otherwise expands SIMDPP_DETAIL_PROBE_IF_VOID_1PARAM |
223 | // passing R with removed outer parentheses |
224 | #define SIMDPP_DETAIL_PROBE_IF_VOID(R) \ |
225 | SIMDPP_PP_IF(SIMDPP_PP_DEC(SIMDPP_PP_VARIADIC_SIZE(SIMDPP_PP_REM R)), \ |
226 | SIMDPP_PP_EAT, \ |
227 | SIMDPP_DETAIL_PROBE_IF_VOID_1PARAM \ |
228 | )(SIMDPP_PP_REMOVE_PARENS(R)) |
229 | |
230 | // Given a parenthesized list of arguments R, expands to nothing if R is |
231 | // parenthesized void token, otherwise expands to return token |
232 | #if ~SIMDPP_PP_CONFIG_FLAGS() & SIMDPP_PP_CONFIG_MSVC() |
233 | |
234 | #define SIMDPP_DETAIL_RETURN_IF_NOT_VOID(R) \ |
235 | SIMDPP_PP_IIF(SIMDPP_PP_PROBE_TO_BOOL(SIMDPP_DETAIL_PROBE_IF_VOID(R)), \ |
236 | SIMDPP_PP_EMPTY, \ |
237 | SIMDPP_DETAIL_RETURN_TOKEN \ |
238 | )() |
239 | #else |
240 | // The following problems have been worked around on MSVC (see top of the |
241 | // file): |
242 | // - SIMDPP_PP_IIF is expanded two expansions too early |
243 | // - the result of SIMDPP_DETAIL_PROBE_IF_VOID is not passed back to |
244 | // SIMDPP_PP_PROBE_TO_BOOL properly |
245 | // - the final SIMDPP_PP_IIF is not expanded for some reason. |
246 | |
247 | #define SIMDPP_DETAIL_RETURN_IF_NOT_VOID(R) \ |
248 | SIMDPP_PP_EXPAND( \ |
249 | SIMDPP_PP_IIF SIMDPP_PP_EMPTY SIMDPP_PP_EMPTY()()( \ |
250 | SIMDPP_DETAIL_MSVC_DEFER_MACRO_ARGS( \ |
251 | SIMDPP_PP_PROBE_TO_BOOL, (SIMDPP_DETAIL_PROBE_IF_VOID(R)) \ |
252 | ), \ |
253 | SIMDPP_PP_EMPTY, \ |
254 | SIMDPP_DETAIL_RETURN_TOKEN \ |
255 | )() \ |
256 | ) |
257 | #endif |
258 | |
259 | #define SIMDPP_DETAIL_RETURN_TOKEN() return |
260 | |
261 | #define SIMDPP_DETAIL_MAKE_DISPATCHER_IMPL(TEMPLATE_PREFIX, TEMPLATE_ARGS, R, NAME, ARGS) \ |
262 | \ |
263 | SIMDPP_DISPATCH_DECLARE_FUNCTIONS( \ |
264 | (SIMDPP_PP_REMOVE_PARENS(TEMPLATE_PREFIX) \ |
265 | SIMDPP_PP_REMOVE_PARENS(R) NAME (SIMDPP_DETAIL_TYPES(ARGS)))) \ |
266 | \ |
267 | SIMDPP_PP_REMOVE_PARENS(TEMPLATE_PREFIX) \ |
268 | SIMDPP_PP_REMOVE_PARENS(R) NAME(SIMDPP_DETAIL_ARGS(ARGS)) \ |
269 | { \ |
270 | using FunPtr = SIMDPP_PP_REMOVE_PARENS(R)(*)(SIMDPP_DETAIL_TYPES(ARGS)); \ |
271 | static FunPtr selected = nullptr; \ |
272 | if (selected == nullptr) { \ |
273 | ::simdpp::detail::FnVersion versions[SIMDPP_DISPATCH_MAX_ARCHS] = {}; \ |
274 | SIMDPP_DISPATCH_COLLECT_FUNCTIONS(versions, \ |
275 | (NAME SIMDPP_PP_REMOVE_PARENS(TEMPLATE_ARGS)), FunPtr) \ |
276 | ::simdpp::detail::FnVersion version = \ |
277 | ::simdpp::detail::select_version_any(versions, \ |
278 | SIMDPP_DISPATCH_MAX_ARCHS, SIMDPP_USER_ARCH_INFO); \ |
279 | selected = reinterpret_cast<FunPtr>(version.fun_ptr); \ |
280 | } \ |
281 | SIMDPP_DETAIL_RETURN_IF_NOT_VOID(R) selected(SIMDPP_DETAIL_FORWARD(ARGS)); \ |
282 | } |
283 | |
284 | #define SIMDPP_DETAIL_IGNORE_PARENS2(x) \ |
285 | SIMDPP_DETAIL_IGNORE_PARENS(SIMDPP_DETAIL_IGNORE_PARENS(x)) |
286 | |
287 | #define SIMDPP_DETAIL_IGNORE_PARENS3(x) \ |
288 | SIMDPP_DETAIL_IGNORE_PARENS(SIMDPP_DETAIL_IGNORE_PARENS( \ |
289 | SIMDPP_DETAIL_IGNORE_PARENS(x) \ |
290 | )) |
291 | |
292 | #define SIMDPP_DETAIL_IGNORE_PARENS4(x) \ |
293 | SIMDPP_DETAIL_IGNORE_PARENS(SIMDPP_DETAIL_IGNORE_PARENS( \ |
294 | SIMDPP_DETAIL_IGNORE_PARENS(SIMDPP_DETAIL_IGNORE_PARENS(x)) \ |
295 | )) |
296 | |
297 | // SIMDPP_PP_SEQ_ELEM does not work with sequence elements containing commas, |
298 | // so we use a workaround |
299 | #define SIMDPP_DETAIL_MAKE_DISPATCHER1(DESC) SIMDPP_ERROR_INCORRECT_NUMBER_OF_ARGUMENTS |
300 | #define SIMDPP_DETAIL_MAKE_DISPATCHER2(DESC) SIMDPP_ERROR_INCORRECT_NUMBER_OF_ARGUMENTS |
301 | #define SIMDPP_DETAIL_MAKE_DISPATCHER3(DESC) \ |
302 | SIMDPP_DETAIL_MAKE_DISPATCHER_IMPL( \ |
303 | (), \ |
304 | (), \ |
305 | (SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(DESC)), \ |
306 | SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(SIMDPP_DETAIL_IGNORE_PARENS(DESC)), \ |
307 | (SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(SIMDPP_DETAIL_IGNORE_PARENS2(DESC)))) |
308 | |
309 | #define SIMDPP_DETAIL_MAKE_DISPATCHER4(DESC) SIMDPP_ERROR_INCORRECT_NUMBER_OF_ARGUMENTS |
310 | #define SIMDPP_DETAIL_MAKE_DISPATCHER5(DESC) \ |
311 | SIMDPP_DETAIL_MAKE_DISPATCHER_IMPL( \ |
312 | (SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(DESC)), \ |
313 | (SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(SIMDPP_DETAIL_IGNORE_PARENS(DESC))), \ |
314 | (SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(SIMDPP_DETAIL_IGNORE_PARENS2(DESC))), \ |
315 | SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(SIMDPP_DETAIL_IGNORE_PARENS3(DESC)), \ |
316 | (SIMDPP_DETAIL_EXTRACT_PARENS_IGNORE_REST(SIMDPP_DETAIL_IGNORE_PARENS4(DESC)))) |
317 | |
318 | |
319 | #define SIMDPP_DETAIL_MAKE_DISPATCHER6(DESC) SIMDPP_ERROR_INCORRECT_NUMBER_OF_ARGUMENTS |
320 | #define SIMDPP_DETAIL_MAKE_DISPATCHER7(DESC) SIMDPP_ERROR_INCORRECT_NUMBER_OF_ARGUMENTS |
321 | |
322 | /** Builds a dispatcher for a specific non-member function. The same macro is |
323 | used for functions with or without return value, with different parameter |
324 | counts and for template functions. |
325 | |
326 | The function accepts a sequence of 3 or 5 parenthesized token groups. Each |
327 | group conveys the following information: |
328 | - (optional) the full template prefix, e.g. (template<class T>) |
329 | - (optional) the template argument list enclosed in brackets, e.g. (<T,U>) |
330 | - the return type, e.g. (void), or (float) |
331 | - the function name, e.g. (my_function) |
332 | - comma separated list of function arguments. Each argument is specified as |
333 | a parenthesized type name and argument name which follows immediately. |
334 | For example ((float) x, (int) y, (std::pair<int, int>) z). |
335 | |
336 | The following examples show several ways to invoke the |
337 | SIMDPP_MAKE_DISPATCHER macro: |
338 | |
339 | SIMDPP_MAKE_DISPATCHER((void)(my_function1)()) |
340 | SIMDPP_MAKE_DISPATCHER((void)(my_function1)((int) x, (float)z)) |
341 | SIMDPP_MAKE_DISPATCHER((int)(my_function1)((int) x, (float)z)) |
342 | SIMDPP_MAKE_DISPATCHER((template<class A, class B>)(<A,B>)(A)(my_function1)()) |
343 | SIMDPP_MAKE_DISPATCHER((template<class A, class B>)(<A,B>)(A)(my_function1)((B) x, (B)z)) |
344 | |
345 | SIMDPP_ARCH_NAMESPACE::NAME (where NAME refers to the name of the function |
346 | supplied to the SIMDPP_MAKE_DISPATCHER macro) must refer to the function to |
347 | be disptached relative to the namespace in which the SIMDPP_MAKE_DISPATCHER |
348 | macro is used in. That is, the macro must be used in a namespace one level |
349 | up than the dispatched function, and that namespace must be |
350 | SIMDPP_ARCH_NAMESPACE. |
351 | |
352 | The return and parameter types must be exactly the same as those of the |
353 | function to be dispatched. The dispatched function may be overloaded. |
354 | |
355 | When dispatching function templates, each used template must be explicitly |
356 | dispatched in all architecture-specific compilation units. For example, |
357 | when using simdpp_multiarch (see cmake/SimdppMultiarch.cmake), these |
358 | instantiations must be defined in SRC_FILE source file. |
359 | |
360 | The macro defines a function with the same signature as the dispatched |
361 | function in the namespace the macro is used. The body of that function |
362 | implements the dispatch mechanism. |
363 | |
364 | The dispatch functions check the enabled instruction set and select the |
365 | best function on first call. The initialization does not introduce race |
366 | conditions when done concurrently. |
367 | |
368 | The generated dispatching code links to all versions of the dispatched |
369 | function statically, so techniques to prevent linkers from stripping |
370 | unreferenced object files are not needed. |
371 | */ |
372 | #define SIMDPP_MAKE_DISPATCHER(DESC) \ |
373 | SIMDPP_PP_CAT(SIMDPP_DETAIL_MAKE_DISPATCHER, SIMDPP_PP_SEQ_SIZE(DESC))(DESC) |
374 | #else // #if SIMDPP_EMIT_DISPATCHER |
375 | #define SIMDPP_MAKE_DISPATCHER(DESC) |
376 | #endif |
377 | |
378 | #define SIMDPP_DETAIL_SIGNATURE_EACH(r, data, x) SIMDPP_PP_REMOVE_PARENS(x) ; |
379 | #define SIMDPP_DETAIL_SIGNATURES(signatures) \ |
380 | SIMDPP_PP_SEQ_FOR_EACH(SIMDPP_DETAIL_SIGNATURE_EACH, data, \ |
381 | SIMDPP_PP_VARIADIC_TO_SEQ signatures) |
382 | |
383 | #if SIMDPP_EMIT_DISPATCHER |
384 | /** Defines a one or more template instantiations for a dispatcher. Accepts one |
385 | or more parenthesized token groups separated by commas defining one or more |
386 | full template instantiations. For example: |
387 | SIMDPP_INSTANTIATE_DISPATCHER((template void foo<int>(int x)) |
388 | |
389 | or |
390 | |
391 | SIMDPP_INSTANTIATE_DISPATCHER( |
392 | (template void foo<int>(int x)), |
393 | (template void foo<char>(char x)) |
394 | ) |
395 | |
396 | The macro forces instantiation of the dispatch function defined by the |
397 | SIMDPP_MAKE_DISPATCHER macro and also of the functions referenced by the |
398 | dispatcher function. The latter is necessary because the dispatcher is |
399 | compiled into a only single object file out of the set of multiversioned |
400 | object files. The referenced functions will be instantiated in all of them. |
401 | */ |
402 | // Implementation note: we can't accept the signatures as a sequence, e.g. |
403 | // (a)(b)(c), because SIMDPP_PP_SEQ_FOR_EACH does not support sequence elements |
404 | // containing commas at the top level |
405 | #define SIMDPP_INSTANTIATE_DISPATCHER(...) \ |
406 | SIMDPP_DETAIL_SIGNATURES((__VA_ARGS__)) \ |
407 | namespace SIMDPP_ARCH_NAMESPACE { \ |
408 | SIMDPP_DETAIL_SIGNATURES((__VA_ARGS__)) \ |
409 | } |
410 | #else // SIMDPP_EMIT_DISPATCHER |
411 | #define SIMDPP_INSTANTIATE_DISPATCHER(...) \ |
412 | namespace SIMDPP_ARCH_NAMESPACE { \ |
413 | SIMDPP_DETAIL_SIGNATURES((__VA_ARGS__)) \ |
414 | } |
415 | #endif |
416 | |
417 | #endif // LIBSIMDPP_DISPATCH_MAKE_DISPATCHER_H |
418 | |