Update instructions in containers.rst
[gromacs.git] / src / gromacs / utility / enumerationhelpers.h
blobc74866f9a8ad78f0fc35d8d7177594d8c31a7e54
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019,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.
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.
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.
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.
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.
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.
35 /*! \file
36 * \brief Defines helper types for class enumerations.
38 * These helper types facilitate iterating over class enums, and
39 * maintaining a type-safe and value-safe matching list of names. The
40 * code is closely based on the public-domain code by Guilherme
41 * R. Lampert, found in commit c94c18a of
42 * https://github.com/glampert/enum_helpers/blob/master/enum_helpers.hpp
43 * Thanks Guilherme!
45 * NOTE This functionality only works for enumerations of monotonically
46 * increasing values, starting with the value zero.
48 * Usage examples:
50 * enum class Foo : int
51 * {
52 * Bar,
53 * Baz,
54 * Fooz,
55 * Count
56 * };
58 * for (Foo c : EnumerationWrapper<Foo>{})
59 * {
60 * // 'c' is a constant from Foo
61 * }
64 * const EnumerationArray<Foo, std::string> fooStrings = { { "Bar", "Baz", "Fooz" } };
65 * std::cout << fooStrings[Foo::Baz];
66 * std::cout << fooStrings[Foo::Count]; // Triggers an assertion
68 * for (Foo c : keysOf(fooStrings))
69 * {
70 * print(fooStrings[c]);
71 * }
73 * ArrayRef<const std::string> namesRef(fooStrings);
75 * \author Mark Abraham <mark.j.abraham@gmail.com>
76 * \inlibraryapi
77 * \ingroup module_utility
79 #ifndef GMX_UTILITY_ENUMHELPERS_H
80 #define GMX_UTILITY_ENUMHELPERS_H
82 #include <cstddef>
84 #include <iterator>
85 #include <type_traits>
87 #if __has_include(<boost/stl_interfaces/iterator_interface.hpp>)
88 # include <boost/stl_interfaces/iterator_interface.hpp>
89 #else // fallback for installed headers
90 # include <gromacs/external/boost/stl_interfaces/iterator_interface.hpp>
91 #endif
93 #include "gromacs/utility/gmxassert.h"
95 namespace gmx
98 /*! \libinternal
99 * \brief Allows iterating sequential enumerators.
101 * You can also provide an increment step > 1 if each constant is
102 * spaced by a larger value. Terminating constant is assumed to be a
103 * 'Count' member, which is never iterated. A different name for the
104 * terminating constant can also be specified on declaration.
106 * NOTE This functionality only works for enumerations of monotonically
107 * increasing values, starting with the value zero.
109 * See file documentation for usage example.
111 * \tparam EnumType The enum (class) type.
112 * \tparam Last Last constant or number thereof (assumes a default 'Count' member).
113 * \tparam Step Step increment.
115 template<typename EnumType, EnumType Last = EnumType::Count, std::ptrdiff_t Step = 1>
116 class EnumerationIterator final :
117 public boost::stl_interfaces::iterator_interface<EnumerationIterator<EnumType, Last, Step>, std::random_access_iterator_tag, EnumType>
119 public:
120 // TODO: Use std::is_enum_v when CUDA 11 is a requirement.
121 static_assert(std::is_enum<EnumType>::value, "Enumeration iterator must be over an enum type.");
122 //! Convenience alias
123 using IntegerType = std::underlying_type_t<EnumType>;
125 constexpr EnumerationIterator() noexcept : m_current{ 0 } // Assumes 0 is the first constant
128 //! Conversion constructor
129 explicit constexpr EnumerationIterator(const EnumType index) noexcept :
130 m_current(static_cast<IntegerType>(index))
133 //! Addition-assignment operator
134 constexpr EnumerationIterator& operator+=(std::ptrdiff_t i) noexcept
136 m_current += Step * i;
137 return *this;
139 //! Dereference operator
140 constexpr EnumType operator*() const noexcept
142 GMX_ASSERT(m_current < static_cast<IntegerType>(Last), "dereferencing out of range");
143 return static_cast<EnumType>(m_current);
145 //! Difference operator
146 constexpr std::ptrdiff_t operator-(const EnumerationIterator other) const noexcept
148 return (static_cast<std::ptrdiff_t>(m_current) - static_cast<std::ptrdiff_t>(other.m_current)) / Step;
151 private:
152 IntegerType m_current;
155 /*! \libinternal
156 * \brief Allows constructing iterators for looping over sequential enumerators.
158 * These are particularly useful for range-based for statements.
160 * You can also provide an increment step > 1 if each constant is
161 * spaced by a larger value. Terminating constant is assumed to be a
162 * 'Count' member, which is never iterated. A different name for the
163 * terminating constant can also be specified on declaration.
165 * See file documentation for usage example.
167 * \tparam EnumType The enum (class) type.
168 * \tparam Last Last constant or number thereof (assumes a default 'Count' member).
169 * \tparam Step Step increment.
171 template<typename EnumType, EnumType Last = EnumType::Count, unsigned int Step = 1>
172 class EnumerationWrapper final
174 public:
175 //! Convenience alias.
176 using IteratorType = EnumerationIterator<EnumType, Last, Step>;
178 //! Functions required for range-based for statements to work.
179 /*!@{*/
180 IteratorType begin() const { return IteratorType{}; }
181 IteratorType end() const { return IteratorType{ Last }; }
182 /*!@}*/
185 /*! \libinternal
186 * \brief Wrapper for a C-style array with size and indexing defined
187 * by an enum. Useful for declaring arrays of enum names for debug
188 * or other printing. An ArrayRef<DataType> may be constructed from
189 * an object of this type.
191 * See file documentation for usage example.
193 * \tparam EnumType The enum (class) type.
194 * \tparam DataType Type of the data stored in the array.
195 * \tparam ArraySize Size in entries of the array.
197 template<typename EnumType, // The enum (class) type.
198 typename DataType, // Type of the data stored in the array.
199 EnumType ArraySize = EnumType::Count // Size in entries of the array.
201 struct EnumerationArray final
203 //! Convenience alias
204 using EnumerationWrapperType = EnumerationWrapper<EnumType, ArraySize>;
206 /*! \brief Data for names.
208 * Data is kept public so we can use direct aggregate
209 * initialization just like in a plain C-style array. */
210 DataType m_elements[std::size_t(ArraySize)];
212 //! Returns an object that provides iterators over the keys.
213 static constexpr EnumerationWrapperType keys() { return EnumerationWrapperType{}; }
214 //! Returns the size of the enumeration.
215 static constexpr std::size_t size() { return std::size_t(ArraySize); }
217 /*!@{*/
218 //! Array access with asserts:
219 DataType& operator[](const std::size_t index)
221 GMX_ASSERT(index < size(), "index out of range");
222 return m_elements[index];
224 const DataType& operator[](const std::size_t index) const
226 GMX_ASSERT(index < size(), "index out of range");
227 return m_elements[index];
230 DataType& operator[](const EnumType index)
232 GMX_ASSERT(std::size_t(index) < size(), "index out of range");
233 return m_elements[std::size_t(index)];
235 const DataType& operator[](const EnumType index) const
237 GMX_ASSERT(std::size_t(index) < size(), "index out of range");
238 return m_elements[std::size_t(index)];
240 /*!@}*/
242 /*!@{*/
243 //! Range iterators (unchecked)
244 using iterator = DataType*;
245 using const_iterator = const DataType*;
246 using reverse_iterator = std::reverse_iterator<iterator>;
247 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
248 /*!@}*/
250 /*!@{*/
251 //! Getters for forward iterators for ranges
252 iterator begin() { return &m_elements[0]; }
253 iterator end() { return &m_elements[size()]; }
254 const_iterator begin() const { return &m_elements[0]; }
255 const_iterator end() const { return &m_elements[size()]; }
256 /*!@}*/
258 /*!@{*/
259 //! Getters for reverse iterators for ranges
260 reverse_iterator rbegin() { return reverse_iterator{ end() }; }
261 reverse_iterator rend() { return reverse_iterator{ begin() }; }
262 const_reverse_iterator rbegin() const { return const_reverse_iterator{ end() }; }
263 const_reverse_iterator rend() const { return const_reverse_iterator{ begin() }; }
264 /*!@}*/
266 /*!@{*/
267 //! Pointers (unchecked)
268 using pointer = DataType*;
269 using const_pointer = const DataType*;
270 /*!@}*/
272 //! Returns a const raw pointer to the contents of the array.
273 const_pointer data() const { return &m_elements[0]; }
274 //! Returns a raw pointer to the contents of the array.
275 pointer data() { return &m_elements[0]; }
278 /*! \brief Returns an object that provides iterators over the keys
279 * associated with \c EnumerationArrayType.
281 * This helper function is useful in contexts where there is an object
282 * of an EnumerationArray, and we want to use a range-based for loop
283 * over the keys associated with it, and it would be inconvenient to
284 * use the very word EnumerationArray<...> type, nor introduce a using
285 * statement for this purpose. It is legal in C++ to call a static
286 * member function (such as keys()) via an object rather than the
287 * type, but clang-tidy warns about that. So instead we make available
288 * a free function that calls that static method. */
289 template<typename EnumerationArrayType>
290 typename EnumerationArrayType::EnumerationWrapperType keysOf(const EnumerationArrayType& /* arrayObject */)
292 return EnumerationArrayType::keys();
295 } // namespace gmx
297 #endif