Various minor nblib updates
[alexxy/gromacs.git] / api / nblib / util / traits.hpp
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 /*! \inpublicapi \file
36  * \brief
37  * Implements general purpose STL-like type traits
38  *
39  * \author Victor Holanda <victor.holanda@cscs.ch>
40  * \author Joe Jordan <ejjordan@kth.se>
41  * \author Prashanth Kanduri <kanduri@cscs.ch>
42  * \author Sebastian Keller <keller@cscs.ch>
43  * \author Artem Zhmurov <zhmurov@gmail.com>
44  */
45
46 #ifndef NBLIB_UTIL_TRAITS_HPP
47 #define NBLIB_UTIL_TRAITS_HPP
48
49 #include <cassert>
50
51 #include <sstream>
52 #include <string>
53 #include <tuple>
54 #include <type_traits>
55 #include <vector>
56
57
58 namespace nblib
59 {
60
61 //! \brief Base template for a holder of entries of different data types
62 template<class... Ts>
63 struct TypeList
64 {
65 };
66
67 namespace detail
68 {
69 //! \brief unimplemented base template
70 template<template<class...> class P, class L>
71 struct [[maybe_unused]] Map_;
72
73 /*! \brief Implementation of Map_
74  *
75  * This is a specialization of the Map_ base template
76  * for the case that the L template parameter itself has template parameters
77  * in this case, the template parameters of L are caught in Ts...
78  *
79  */
80 template<template<class...> class P, template<class...> class L, class... Ts>
81 struct Map_<P, L<Ts...>>
82 {
83     // resulting type is a TypeList of the P-template instantiated
84     // with all template parameters of L
85     typedef TypeList<P<Ts>...> type;
86 };
87
88 //! \brief unimplemented base template
89 template<template<class...> class P, class L>
90 struct [[maybe_unused]] Reduce_;
91
92 //! \brief Implementation of Reduce_
93 template<template<class...> class P, template<class...> class L, class... Ts>
94 struct Reduce_<P, L<Ts...>>
95 {
96     typedef P<Ts...> type;
97 };
98
99 //! \brief unimplemented base template
100 template<class L1, class L2>
101 struct [[maybe_unused]] FuseTwo_;
102
103 //! \brief implementation of FuseTwo_
104 template<template<class...> class L1, template<class...> class L2, class... Ts1, class... Ts2>
105 struct FuseTwo_<L1<Ts1...>, L2<Ts2...>>
106 {
107     typedef TypeList<Ts1..., Ts2...> type;
108 };
109
110 //! \brief unimplemented base template
111 template<class... Ls>
112 struct [[maybe_unused]] Fuse_;
113
114 //! \brief recursion endpoint
115 template<class L>
116 struct Fuse_<L>
117 {
118     typedef L type;
119 };
120
121 //! \brief recurse until only one type is left
122 template<class L1, class L2, class... Ls>
123 struct Fuse_<L1, L2, Ls...>
124 {
125     typedef typename Fuse_<typename FuseTwo_<L1, L2>::type, Ls...>::type type;
126 };
127
128
129 //! \brief keep adding the template parameter pack to the type list
130 template<class L, int N, class... Ts>
131 struct RepeatHelper_
132 {
133     typedef typename RepeatHelper_<typename FuseTwo_<L, TypeList<Ts...>>::type, N - 1, Ts...>::type type;
134 };
135
136 //! \brief stop recurision
137 template<class L, class... Ts>
138 struct RepeatHelper_<L, 1, Ts...>
139 {
140     typedef L type;
141 };
142
143 //! \brief base case
144 template<class L, int N, class = void>
145 struct Repeat_
146 {
147 };
148
149 //! \brief capture original template parameter pack, protect against N < 1
150 template<template<class...> class L, int N, class... Ts>
151 struct Repeat_<L<Ts...>, N, std::enable_if_t<N >= 1>>
152 {
153     typedef typename RepeatHelper_<L<Ts...>, N, Ts...>::type type;
154 };
155
156
157 // Like std::void_t but for values
158 template<auto...>
159 using void_value_t = void;
160
161 template<class T, class = void>
162 struct HasValueMember : std::false_type
163 {
164 };
165
166 template<class T>
167 struct HasValueMember<T, void_value_t<T::value>> : std::true_type
168 {
169 };
170
171 template<class T, class = void>
172 struct AccessTypeMemberIfPresent
173 {
174     typedef T type;
175 };
176
177 template<class T>
178 struct AccessTypeMemberIfPresent<T, typename std::void_t<typename T::type>>
179 {
180     typedef typename T::type type;
181 };
182
183 template<class T>
184 using AccessTypeMemberIfPresent_t = typename AccessTypeMemberIfPresent<T>::type;
185
186 /*! \brief Comparison meta function that compares T to Tuple[N]
187  *
188  * This trait evaluates to std::true_type if T is the same as Tuple[N]
189  * OR if T is the same as the type member of Tuple[N]
190  */
191 template<int N, typename T, typename Tuple>
192 struct MatchTypeOrTypeMember :
193         std::disjunction<std::is_same<T, std::tuple_element_t<N, Tuple>>,
194                 std::is_same<T, AccessTypeMemberIfPresent_t<std::tuple_element_t<N, Tuple>>>>
195 {
196 };
197
198 //! \brief Recursion to check the next field N+1
199 template<int N, class T, class Tuple, template<int, class, class> class Comparison, class Match = void>
200 struct MatchField_ : std::integral_constant<size_t, MatchField_<N + 1, T, Tuple, Comparison>{}>
201 {
202 };
203
204 //! \brief recursion stop when Comparison<N, T, Tuple>::value is true
205 template<int N, class T, class Tuple, template<int, class, class> class Comparison>
206 struct MatchField_<N, T, Tuple, Comparison, std::enable_if_t<Comparison<N, T, Tuple>{}>> :
207         std::integral_constant<size_t, N>
208 {
209 };
210
211 } // namespace detail
212
213 /*! \brief Create a TypeList of P instantiated with each template parameter of L
214  *
215  * returns TypeList<P<Ts>...>, with Ts... = template parameters of L
216  * does not compile if L has no template parameters
217  */
218 template<template<class...> class P, class L>
219 using Map = typename detail::Map_<P, L>::type;
220
221 /*! \brief Base template for expressing a datatype P templated with all the entries in type list L
222  *
223  * The result is P instantiated with all the template parameters of L
224  */
225 template<template<class...> class P, class L>
226 using Reduce = typename detail::Reduce_<P, L>::type;
227
228 //! \brief Concatenates template parameters of two variadic templates into a TypeList
229 template<class... Ls>
230 using FuseTwo = typename detail::FuseTwo_<Ls...>::type;
231
232 /*! \brief This traits concatenates an arbitrary number of variadic templates into a single TypeList
233  *
234  * For clarity reasons, the fuse operation to fuse two lists into one has been decoupled
235  * into a separate trait from the handling of the recursion over the variadic arguments.
236  */
237 template<class... Ls>
238 using Fuse = typename detail::Fuse_<Ls...>::type;
239
240 /*! \brief Repeat the template parameters of L N times
241  *
242  * L must have template parameters
243  * N must be bigger than 0
244  * Repeated types are put in a TypeList
245  */
246 template<class L, int N>
247 using Repeat = typename detail::Repeat_<L, N>::type;
248
249 /*! \brief Meta function to return the first index in Tuple whose type matches T
250  *
251  *  If there are more than one, the first occurrence will be returned.
252  *  If there is no such type, the size of Tuple will be returned.
253  *  Note that the default comparison operation supplied here also matches if the type member Tuple[N]::type matches T
254  */
255 template<typename T, class TL, template<int, class, class> class Comparison = detail::MatchTypeOrTypeMember>
256 struct FindIndex
257 {
258 };
259
260 /*! \brief Specialization to only enable this trait if TL has template parameters
261  *
262  * \tparam T          a type to look for in the template parameters of TL
263  * \tparam TL         a template template parameter, e.g. std::tuple or nblib::TypeList
264  * \tparam Ts         template parameters of TL
265  * \tparam Comparison comparison operation
266  *
267  *  Note that \a T is added to \a TL as a sentinel to terminate the recursion
268  *  and prevent an out of bounds tuple access compiler error.
269  */
270 template<typename T, template<class...> class TL, class... Ts, template<int, class, class> class Comparison>
271 struct FindIndex<T, TL<Ts...>, Comparison> : detail::MatchField_<0, T, std::tuple<Ts..., T>, Comparison>
272 {
273 };
274
275 /*! \brief Meta function to return the element in Tuple whose type matches T
276  *
277  * If there are more than one, the first occurrence will be returned
278  * If there is no such that, a compiler error is generated due to accessing
279  * the tuple out of bounds
280  */
281 template<typename T, typename Tuple>
282 decltype(auto) pickType(Tuple& tup)
283 {
284     return std::get<FindIndex<T, std::decay_t<Tuple>>{}>(tup);
285 }
286
287 //! \brief template meta function to determine whether T is contained in TL
288 template<class T, class TL>
289 struct Contains
290 {
291 };
292
293 /*! \brief implementation of the Contains trait to look for T in TL
294  *
295  * \tparam T   type to look for in TL
296  * \tparam TL  a variadic type, such as std::tuple or TypeList
297  * \tparam Ts  the template parameters of TL
298  *
299  * Note that this clang-format enforced formatting is unfortunate, it should be:
300  * struct Contains<T, TL<Ts...>> : std::bool_constant<FindIndex<T, TL<Ts...>>{} < sizeof...(Ts)>
301  */
302 template<class T, template<class...> class TL, class... Ts>
303         struct Contains<T, TL<Ts...>> : std::bool_constant < FindIndex<T, TL<Ts...>>{}<sizeof...(Ts)>
304 {
305 };
306
307 } // namespace nblib
308
309 #endif // NBLIB_UTIL_TRAITS_HPP