Line data Source code
1 0 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : #pragma once
5 :
6 : #include <boost/parameter/name.hpp>
7 : #include <boost/preprocessor/arithmetic/inc.hpp>
8 : #include <boost/preprocessor/control/expr_iif.hpp>
9 : #include <boost/preprocessor/control/iif.hpp>
10 : #include <boost/preprocessor/control/while.hpp>
11 : #include <boost/preprocessor/list/adt.hpp>
12 : #include <boost/preprocessor/list/fold_left.hpp>
13 : #include <boost/preprocessor/list/fold_right.hpp>
14 : #include <boost/preprocessor/list/for_each_product.hpp>
15 : #include <boost/preprocessor/list/size.hpp>
16 : #include <boost/preprocessor/list/to_tuple.hpp>
17 : #include <boost/preprocessor/list/transform.hpp>
18 : #include <boost/preprocessor/logical/bitand.hpp>
19 : #include <boost/preprocessor/logical/bool.hpp>
20 : #include <boost/preprocessor/logical/compl.hpp>
21 : #include <boost/preprocessor/repetition/for.hpp>
22 : #include <boost/preprocessor/tuple/elem.hpp>
23 : #include <boost/preprocessor/tuple/reverse.hpp>
24 : #include <boost/preprocessor/tuple/size.hpp>
25 : #include <boost/preprocessor/tuple/to_list.hpp>
26 : #include <boost/preprocessor/variadic/elem.hpp>
27 : #include <boost/preprocessor/variadic/to_list.hpp>
28 :
29 : /// \cond
30 : #define GENERATE_INSTANTIATIONS_DO_PRODUCT(INSTANTIATION_MACRO, LIST_OF_LISTS) \
31 : BOOST_PP_LIST_FOR_EACH_PRODUCT(INSTANTIATION_MACRO, \
32 : BOOST_PP_LIST_SIZE(LIST_OF_LISTS), \
33 : BOOST_PP_LIST_TO_TUPLE(LIST_OF_LISTS))
34 :
35 : #define GENERATE_INSTANTIATION_TUPLES_TO_LISTS(d, _, elem) \
36 : BOOST_PP_TUPLE_TO_LIST(BOOST_PP_TUPLE_SIZE(elem), elem)
37 : /// \endcond
38 :
39 : /*!
40 : * \ingroup UtilitiesGroup
41 : * \brief Macro useful for generating many explicit instantiations of function
42 : * or class templates
43 : *
44 : * It is often necessary to generate explicit instantiations of function or
45 : * class templates. Since the total number of explicit instantiations scales as
46 : * the product of the number of possible number of parameter values of each
47 : * template parameter, this quickly becomes tedious. This macro allows you to
48 : * easily generate hundreds of explicit instantiations.
49 : *
50 : * The first argument to the macro is a macro that takes two arguments and is
51 : * described below. The remaining arguments are macro-tuples, e.g. `(1, 2, 3)`.
52 : * The Cartesian product of the macro-tuples is then computed and each term is
53 : * passed as a tuple as the second argument to the `INSTANTIATION_MACRO`. The
54 : * first argument to the `INSTANTIATION_MACRO` is a Boost.Preprocessor internal
55 : * variable so just make it `_`. The `INSTANTIATION(_, data)` macro below serves
56 : * as an example. A concrete example is generating explicit instantiations of
57 : * the class `Index<Dim>` for `Dim = 0,1,2,3`, which you would do as follows:
58 : *
59 : * \code
60 : * #define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)
61 : *
62 : * #define INSTANTIATION(_, data) \
63 : * template class Index<GET_DIM(data)>;
64 : *
65 : * GENERATE_INSTANTIATIONS(INSTANTIATION, (0, 1, 2, 3))
66 : *
67 : * #undef GET_DIM
68 : * #undef INSTANTIATION
69 : * \endcode
70 : *
71 : * This will generate:
72 : *
73 : * \code
74 : * template class Index<0>;
75 : * template class Index<1>;
76 : * template class Index<2>;
77 : * template class Index<3>;
78 : * \endcode
79 : *
80 : * It is also possible to generate explicit instantiations for multiple classes
81 : * or functions in a single call to `GENERATE_INSTANTIATIONS`. For example, the
82 : * (in)equivalence operators can be generated using:
83 : *
84 : * \code
85 : * #define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)
86 : * #define GEN_OP(op, dim) \
87 : * template bool operator op(const Index<dim>& lhs, \
88 : * const Index<dim>& rhs);
89 : * #define INSTANTIATION(_, data) \
90 : * template class Index<GET_DIM(data)>; \
91 : * GEN_OP(==, GET_DIM(data)) \
92 : * GEN_OP(!=, GET_DIM(data))
93 : *
94 : * GENERATE_INSTANTIATIONS(INSTANTIATION, (0, 1, 2, 3))
95 : *
96 : * #undef GET_DIM
97 : * #undef GEN_OP
98 : * #undef INSTANTIATION
99 : * \endcode
100 : *
101 : * which will result in the instantiations:
102 : *
103 : * \code
104 : * template class Index<0>;
105 : * template bool operator==(const Index<0>& lhs, const Index<0>& rhs);
106 : * template bool operator!=(const Index<0>& lhs, const Index<0>& rhs);
107 : * template class Index<1>;
108 : * template bool operator==(const Index<1>& lhs, const Index<1>& rhs);
109 : * template bool operator!=(const Index<1>& lhs, const Index<1>& rhs);
110 : * template class Index<2>;
111 : * template bool operator==(const Index<2>& lhs, const Index<2>& rhs);
112 : * template bool operator!=(const Index<2>& lhs, const Index<2>& rhs);
113 : * template class Index<3>;
114 : * template bool operator==(const Index<3>& lhs, const Index<3>& rhs);
115 : * template bool operator!=(const Index<3>& lhs, const Index<3>& rhs);
116 : * \endcode
117 : *
118 : * Now let's look at generating instantiations of member function templates of
119 : * class templates, which will be a common use case. In this example we generate
120 : * explicit instantiations of all the member function templates of the class
121 : * `ScalarWave::Solutions::PlaneWave`. In total, for `Dim = 1,2,3` and types
122 : * `double` and `DataVector` this is about 42 explicit instantiations, which
123 : * would be extremely annoying to write by hand. The macro code is surprisingly
124 : * simple:
125 : *
126 : * \code
127 : * #define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)
128 : * #define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)
129 : *
130 : * #define INSTANTIATE(_, data) \
131 : * template Scalar<DTYPE(data)> \
132 : * ScalarWave::Solutions::PlaneWave<DIM(data)>::psi( \
133 : * const tnsr::I<DTYPE(data), DIM(data)>& x, double t) const; \
134 : * template Scalar<DTYPE(data)> \
135 : * ScalarWave::Solutions::PlaneWave<DIM(data)>::dpsi_dt( \
136 : * const tnsr::I<DTYPE(data), DIM(data)>& x, double t) const;
137 : *
138 : * GENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector))
139 : *
140 : * #undef DIM
141 : * #undef DTYPE
142 : * #undef INSTANTIATE
143 : * \endcode
144 : *
145 : * We don't show the result from preprocessor since for all of the member
146 : * functions of `PlaneWave` the total output is approximately 150 lines, but you
147 : * can hopefully see the benefits of generating explicit instantiations using
148 : * the `GENERATE_INSTANTIATIONS` way.
149 : *
150 : * One thing that can be difficult is debugging metaprograms (be they template
151 : * or macro-based). To this end we provide a make target `DebugPreprocessor`
152 : * which prints the output of running the preprocessor on the file
153 : * `src/Executables/DebugPreprocessor/DebugPreprocessor.cpp`.
154 : * Note that the output of the `GENERATE_INSTANTIATIONS` macro will be on a
155 : * single line, so it often proves useful to copy-paste the output into an
156 : * editor and run clang-format over the code so it's easier to reason about.
157 : */
158 1 : #define GENERATE_INSTANTIATIONS(INSTANTIATION_MACRO, ...) \
159 : GENERATE_INSTANTIATIONS_DO_PRODUCT( \
160 : INSTANTIATION_MACRO, \
161 : BOOST_PP_LIST_TRANSFORM(GENERATE_INSTANTIATION_TUPLES_TO_LISTS, _, \
162 : BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__)))
|