71a84c18dec4928f46f9f8ea82fcea47883460e3
[alexxy/gromacs.git] / src / gromacs / utility / enumerationhelpers.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2019, 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 /*! \libinternal
36  * \file
37  * \brief Defines helper types for class enumerations.
38  *
39  * These helper types facilitate iterating over class enums, and
40  * maintaining a type-safe and value-safe matching list of names. The
41  * code is closely based on the public-domain code by Guilherme
42  * R. Lampert, found in commit c94c18a of
43  * https://github.com/glampert/enum_helpers/blob/master/enum_helpers.hpp
44  * Thanks Guilherme!
45  *
46  * NOTE This functionality only works for enumerations of monotonically
47  * increasing values, starting with the value zero.
48  *
49  * Usage examples:
50  *
51  *  enum class Foo
52  *  {
53  *      Bar, Baz, Fooz,
54  *      Count
55  *  };
56  *
57  *  EnumerationWrapper<Foo> iter;
58  *
59  *  for (Foo c : iter)
60  *  {
61  *      // 'c' is a constant from Foo
62  *  }
63  *
64  *  const EnumerationArray<Foo, std::string> fooStrings = { { "Bar", "Baz", "Fooz" } };
65  *
66  *  for (Foo c : keysOf(fooStrings))
67  *  {
68  *      print(fooStrings[c]);
69  *  }
70  *
71  *  ArrayRef<const std::string> namesRef(fooStrings);
72  *
73  * \author Mark Abraham <mark.j.abraham@gmail.com>
74  * \inlibraryapi
75  * \ingroup module_utility
76  */
77 #ifndef GMX_UTILITY_ENUMHELPERS_H
78 #define GMX_UTILITY_ENUMHELPERS_H
79
80 #include <cstddef>
81
82 #include <iterator>
83 #include <type_traits>
84
85 #include "gromacs/utility/gmxassert.h"
86
87 namespace gmx
88 {
89
90 /*! \libinternal
91  * \brief Allows iterating sequential enumerators.
92  *
93  * You can also provide an increment step > 1 if each constant is
94  * spaced by a larger value.  Terminating constant is assumed to be a
95  * 'Count' member, which is never iterated. A different name for the
96  * terminating constant can also be specified on declaration.
97  *
98  * NOTE This functionality only works for enumerations of monotonically
99  * increasing values, starting with the value zero.
100  *
101  * See file documentation for usage example.
102  *
103  * \tparam  EnumType   The enum (class) type.
104  * \tparam  Last       Last constant or number thereof (assumes a default 'Count' member).
105  * \tparam  Step       Step increment.
106  */
107 template
108 <
109     typename EnumType,
110     EnumType Last     = EnumType::Count,
111     unsigned int Step = 1
112 >
113 class EnumerationIterator final
114 {
115     public:
116         //! Convenience alias
117         using IntegerType = typename std::underlying_type<EnumType>::type;
118
119         /*! \name Iterator type traits
120          * Satisfies the requirements for STL forward iterator.
121          * \{
122          */
123         using iterator_category = std::forward_iterator_tag;
124         using value_type        = EnumType;
125         using difference_type   = std::ptrdiff_t;
126         using pointer           = EnumType*;
127         using reference         = EnumType&;
128         //! \}
129
130         constexpr EnumerationIterator() noexcept :
131         m_current { 0 } // Assumes 0 is the first constant
132         { }
133         //! Copy constructor
134         constexpr EnumerationIterator(const EnumType index) noexcept
135         : m_current(static_cast<IntegerType>(index))
136         { }
137         //! Pre-increment operator
138         EnumerationIterator operator++()
139         {
140             m_current += Step;
141             return *this;
142         }
143         //! Post-increment operator
144         EnumerationIterator operator++(int)
145         {
146             EnumerationIterator old_val { *this };
147             m_current += Step;
148             return old_val;
149         }
150         //! Dereference operator
151         EnumType operator*() const
152         {
153             GMX_ASSERT(m_current < static_cast<IntegerType>(Last), "dereferencing out of range");
154             return static_cast<EnumType>(m_current);
155         }
156
157         /*!@{*/
158         //! Comparision operators
159         bool operator== (const EnumerationIterator other) const noexcept { return m_current == other.m_current; }
160         bool operator!= (const EnumerationIterator other) const noexcept { return m_current != other.m_current; }
161         bool operator<  (const EnumerationIterator other) const noexcept { return m_current <  other.m_current; }
162         bool operator>  (const EnumerationIterator other) const noexcept { return m_current >  other.m_current; }
163         bool operator<= (const EnumerationIterator other) const noexcept { return m_current <= other.m_current; }
164         bool operator>= (const EnumerationIterator other) const noexcept { return m_current >= other.m_current; }
165         /*!@}*/
166
167     private:
168         IntegerType m_current;
169 };
170
171 /*! \libinternal
172  * \brief Allows constructing iterators for looping over sequential enumerators.
173  *
174  * These are particularly useful for range-based for statements.
175  *
176  * You can also provide an increment step > 1 if each constant is
177  * spaced by a larger value.  Terminating constant is assumed to be a
178  * 'Count' member, which is never iterated. A different name for the
179  * terminating constant can also be specified on declaration.
180  *
181  * See file documentation for usage example.
182  *
183  * \tparam  EnumType   The enum (class) type.
184  * \tparam  Last       Last constant or number thereof (assumes a default 'Count' member).
185  * \tparam  Step       Step increment.
186  */
187 template
188 <
189     typename EnumType,
190     EnumType Last     = EnumType::Count,
191     unsigned int Step = 1
192 >
193 class EnumerationWrapper final
194 {
195     public:
196         //! Convenience alias.
197         using IteratorType = EnumerationIterator<EnumType, Last, Step>;
198
199         //! Functions required for range-based for statements to work.
200         /*!@{*/
201         IteratorType begin() const { return IteratorType {}; }
202         IteratorType end()   const { return IteratorType { Last }; }
203         /*!@}*/
204 };
205
206 /*! \libinternal
207  * \brief Wrapper for a C-style array with size and indexing defined
208  * by an enum. Useful for declaring arrays of enum names for debug
209  * or other printing. An ArrayRef<DataType> may be constructed from
210  * an object of this type.
211  *
212  * See file documentation for usage example.
213  *
214  * \tparam  EnumType   The enum (class) type.
215  * \tparam  DataType   Type of the data stored in the array.
216  * \tparam  ArraySize  Size in entries of the array.
217  */
218 template
219 <
220     typename EnumType,                   // The enum (class) type.
221     typename DataType,                   // Type of the data stored in the array.
222     EnumType ArraySize = EnumType::Count // Size in entries of the array.
223 >
224 struct EnumerationArray final
225 {
226     //! Convenience alias
227     using EnumerationWrapperType = EnumerationWrapper<EnumType, ArraySize>;
228
229     /*! \brief Data for names.
230      *
231      * Data is kept public so we can use direct aggregate
232      * initialization just like in a plain C-style array. */
233     DataType m_elements[std::size_t(ArraySize)];
234
235     //! Returns an object that provides iterators over the keys.
236     static constexpr EnumerationWrapperType keys() { return EnumerationWrapperType {}; }
237     //! Returns the size of the enumeration.
238     static constexpr std::size_t size() { return std::size_t(ArraySize); }
239
240     /*!@{*/
241     //! Array access with asserts:
242     DataType &operator[](const std::size_t index)
243     {
244         GMX_ASSERT(index < size(), "index out of range");
245         return m_elements[index];
246     }
247     const DataType &operator[](const std::size_t index) const
248     {
249         GMX_ASSERT(index < size(), "index out of range");
250         return m_elements[index];
251     }
252
253     DataType &operator[](const EnumType index)
254     {
255         GMX_ASSERT(std::size_t(index) < size(), "index out of range");
256         return m_elements[std::size_t(index)];
257     }
258     const DataType &operator[](const EnumType index) const
259     {
260         GMX_ASSERT(std::size_t(index) < size(), "index out of range");
261         return m_elements[std::size_t(index)];
262     }
263     /*!@}*/
264
265     /*!@{*/
266     //! Range iterators (unchecked)
267     using iterator               = DataType *;
268     using const_iterator         = const DataType *;
269     using reverse_iterator       = std::reverse_iterator<iterator>;
270     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
271     /*!@}*/
272
273     /*!@{*/
274     //! Getters for forward iterators for ranges
275     iterator               begin()        { return &m_elements[0];      }
276     iterator               end()          { return &m_elements[size()]; }
277     const_iterator         begin()  const { return &m_elements[0];      }
278     const_iterator         end()    const { return &m_elements[size()]; }
279     /*!@}*/
280
281     /*!@{*/
282     //! Getters for reverse iterators for ranges
283     reverse_iterator       rbegin()       { return reverse_iterator { end() }; }
284     reverse_iterator       rend()         { return reverse_iterator { begin() }; }
285     const_reverse_iterator rbegin() const { return const_reverse_iterator { end() }; }
286     const_reverse_iterator rend()   const { return const_reverse_iterator { begin() }; }
287     /*!@}*/
288
289     /*!@{*/
290     //! Pointers (unchecked)
291     using pointer       = DataType *;
292     using const_pointer = const DataType *;
293     /*!@}*/
294
295     //! Returns a const raw pointer to the contents of the array.
296     const_pointer data() const { return &m_elements[0]; }
297 };
298
299 /*! \brief Returns an object that provides iterators over the keys
300  * associated with \c EnumerationArrayType.
301  *
302  * This helper function is useful in contexts where there is an object
303  * of an EnumerationArray, and we want to use a range-based for loop
304  * over the keys associated with it, and it would be inconvenient to
305  * use the very word EnumerationArray<...> type, nor introduce a using
306  * statement for this purpose. It is legal in C++ to call a static
307  * member function (such as keys()) via an object rather than the
308  * type, but clang-tidy warns about that. So instead we make available
309  * a free function that calls that static method. */
310 template <typename EnumerationArrayType>
311 typename EnumerationArrayType::EnumerationWrapperType keysOf(const EnumerationArrayType & /* arrayObject */)
312 {
313     return EnumerationArrayType::keys();
314 }
315
316 } // namespace gmx
317
318 #endif