3f1b392fab5ca03c9419346b56d562cb85f502af
[alexxy/gromacs.git] / api / nblib / ppmap.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2020, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*
36  * Copyright (C) 2012 William Swanson
37  *
38  * Permission is hereby granted, free of charge, to any person
39  * obtaining a copy of this software and associated documentation
40  * files (the "Software"), to deal in the Software without
41  * restriction, including without limitation the rights to use, copy,
42  * modify, merge, publish, distribute, sublicense, and/or sell copies
43  * of the Software, and to permit persons to whom the Software is
44  * furnished to do so, subject to the following conditions:
45  *
46  * The above copyright notice and this permission notice shall be
47  * included in all copies or substantial portions of the Software.
48  *
49  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
50  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
51  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
52  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
53  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
54  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
55  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
56  *
57  * Except as contained in this notice, the names of the authors or
58  * their institutions shall not be used in advertising or otherwise to
59  * promote the sale, use or other dealings in this Software without
60  * prior written authorization from the authors.
61  */
62
63 /*! \inpublicapi \file
64  *  \brief
65  *  Provides MAP and MAP_LIST to apply a macro to a variadic argument list
66  *
67  *  The MAP and MAP_LIST macros implement calling a supplied macro with
68  *  all of the subsequent arguments. For example:
69  *  MAP(macro, x, y, z) expands to macro(x) macro(y) macro(z)  while
70  *  MAP_LIST(macro, x, y, z) expands to macro(x), macro(y), macro(z)
71  *
72  *  Due to the limitations of the preprocessor, it is unfortunately not
73  *  possible to implement this functionality in a more straight-forward way.
74  *  Since this use-case is not too uncommon, Boost for example implements
75  *  BOOST_PP_SEQ_FOR_EACH which provides equivalent functionality implemented
76  *  with the same technique, but is more comprehensive in scope,
77  *  beyond what's required here.
78  *
79  *  A discussion of how and why this macro works can be found here:
80  *  https://stackoverflow.com/questions/27765387/distributing-an-argument-in-a-variadic-macro
81  *  and the original repository of this implementation is this one:
82  *  https://github.com/swansontec/map-macro
83  *  It also contains some useful explanations of how it works.
84  */
85
86 #ifndef NBLIB_PPMAP_H
87 #define NBLIB_PPMAP_H
88
89 #define EVAL0(...) __VA_ARGS__
90 #define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
91 #define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
92 #define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
93 #define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
94 #define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
95
96 #define MAP_END(...)
97 #define MAP_OUT
98 #define MAP_COMMA ,
99
100 #define MAP_GET_END2() 0, MAP_END
101 #define MAP_GET_END1(...) MAP_GET_END2
102 #define MAP_GET_END(...) MAP_GET_END1
103 #define MAP_NEXT0(test, next, ...) next MAP_OUT
104 #define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
105 #define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next)
106
107 #define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
108 #define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
109
110 #define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
111 #define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
112
113 #define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
114 #define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
115
116 /**
117  * Applies the function macro `f` to each of the remaining parameters.
118  */
119 #define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
120
121 /**
122  * Applies the function macro `f` to each of the remaining parameters and
123  * inserts commas between the results.
124  */
125 #define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
126
127
128 /** The PP_NARG macro returns the number of arguments that have been
129  *  passed to it.
130  */
131 #define PP_NARG(...) PP_NARG_(__VA_ARGS__, PP_RSEQ_N())
132 #define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
133 #define PP_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, \
134                  _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34,  \
135                  _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50,  \
136                  _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, N, ...)         \
137     N
138 #define PP_RSEQ_N()                                                                             \
139     63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \
140             40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
141             19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
142
143 /** MAP_ENUMERATE macro:
144  * MAP_ENUMERATE(action, args...)
145  * like MAP, calls action with each argument, but also forwards the index of the argument to action
146  */
147 #define FE_0(WHAT)
148 #define FE_1(WHAT, N, X) WHAT(X, N - 1) // NOLINT bugprone-macro-parentheses
149 #define FE_2(WHAT, N, X, ...) WHAT(X, N - 2) FE_1(WHAT, N, __VA_ARGS__)
150 #define FE_3(WHAT, N, X, ...) WHAT(X, N - 3) FE_2(WHAT, N, __VA_ARGS__)
151 #define FE_4(WHAT, N, X, ...) WHAT(X, N - 4) FE_3(WHAT, N, __VA_ARGS__)
152 #define FE_5(WHAT, N, X, ...) WHAT(X, N - 5) FE_4(WHAT, N, __VA_ARGS__)
153
154 #define GET_MACRO(_0, _1, _2, _3, _4, _5, NAME, ...) NAME
155 #define MAP_ENUMERATE(action, ...)                                   \
156     GET_MACRO(_0, __VA_ARGS__, FE_5, FE_4, FE_3, FE_2, FE_1, FE_0, ) \
157     (action, PP_NARG(__VA_ARGS__), __VA_ARGS__)
158
159 #endif // NBLIB_PPMAP_H