Version: SMASH-3.4
traits.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2024-2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #ifndef SRC_INCLUDE_SMASH_TRAITS_H_
11 #define SRC_INCLUDE_SMASH_TRAITS_H_
12 
13 #include <bitset>
14 #include <map>
15 #include <set>
16 #include <sstream>
17 #include <string>
18 #include <type_traits>
19 #include <vector>
20 
21 #include "stringify.h"
22 
23 namespace smash {
24 
25 /// specialize a type for all of the STL containers.
26 namespace detail {
27 
28 /**
29  * Implementation of the type trait to infer if a type is an STL container. This
30  * is the general case for types that are not STL containers.
31  *
32  * \attention This is at the moment meant to serve the SMASH codebase only and
33  * it is on purpose not to add all STL possible containers here. In particular,
34  * the goal is to implement the \c is_writable_to_stream trait in a general way
35  * since we need it in the Configuration class through the Key::as_yaml method,
36  * where the type streamed is totally generic and we need to avoid using the
37  * stream \c operator<< on types that do not support it. If SMASH is used as
38  * library and another container is needed, a specialization of the
39  * <tt>is_stl_container</tt> template can be easily added in smash::detail
40  * namespace.
41  *
42  * \warning The type \c std::array should NOT be added here as STL container.
43  * This would lead to an ambiguity in the \c is_writable_to_stream trait and
44  * would break compilation. In fact, it is standard to consider \c std::array as
45  * tuple-like and so it is done here.
46  *
47  * \tparam T The type to be tested.
48  */
49 template <typename T>
50 struct is_stl_container : std::false_type {};
51 
52 /**
53  * Trait specialization for <tt>std::vector</tt>.
54  *
55  * \tparam Args The STL container template parameters.
56  */
57 template <typename... Args>
58 struct is_stl_container<std::vector<Args...>> : std::true_type {};
59 
60 /**
61  * Trait specialization for <tt>std::set</tt>.
62  *
63  * \tparam Args The STL container template parameters.
64  */
65 template <typename... Args>
66 struct is_stl_container<std::set<Args...>> : std::true_type {};
67 
68 /**
69  * Trait specialization for <tt>std::map</tt>.
70  *
71  * \tparam Args The STL container template parameters.
72  */
73 template <typename... Args>
74 struct is_stl_container<std::map<Args...>> : std::true_type {};
75 
76 } // namespace detail
77 
78 //============================================================================//
79 /**
80  * Type trait to infer if a type is an STL container. Inheriting from
81  * <tt>std::bool_constant</tt> will provide a \c value boolean static constant
82  * member set depending on whether the type is an STL container.
83  *
84  * \tparam T The type to be tested.
85  */
86 template <typename T>
88  : std::bool_constant<detail::is_stl_container<std::decay_t<T>>::value> {};
89 
90 /**
91  * Helper alias which is common to be defined next to a type trait.
92  */
93 template <typename T>
95 
96 //============================================================================//
97 /**
98  * Type trait to infer if a type is tuple-like (\c std::pair or \c std::tuple or
99  * \c std::array or few others).
100  *
101  * \tparam T The type to be tested.
102  * \tparam Enable Type set to \c void as utility to implement the type trait.
103  */
104 template <typename T, typename Enable = void>
105 struct is_tuple_like : std::false_type {};
106 
107 /**
108  * Trait specialization for the case when the type is tuple-like. A tuple-like
109  * type is recognised trying to access \c std::tuple_size<T>::value which exists
110  * only for tuple-like types.
111  *
112  * \tparam T The type to be tested.
113  */
114 template <typename T>
115 struct is_tuple_like<T, std::void_t<decltype(std::tuple_size<T>::value)>>
116  : std::true_type {};
117 
118 /**
119  * Helper alias which is common to be defined next to a type trait.
120  */
121 template <typename T>
122 inline constexpr bool is_tuple_like_v = is_tuple_like<T>::value;
123 
124 //============================================================================//
125 /**
126  * Type trait to infer if a type is map-like (for the moment only \c std::map is
127  * considered). \see detail::is_stl_container for the reason why we limit the
128  * considered types.
129  *
130  * \tparam T The type to be tested.
131  */
132 template <typename T>
133 struct is_map_like : std::false_type {};
134 
135 /**
136  * Trait specialization for the case when the type is <tt>std::map</tt>.
137  *
138  * \tparam K The map key type.
139  * \tparam V The map value type.
140  * \tparam Args The map remaining template types.
141  */
142 template <typename K, typename V, typename... Args>
143 struct is_map_like<std::map<K, V, Args...>> : std::true_type {};
144 
145 /**
146  * Helper alias which is common to be defined next to a type trait.
147  */
148 template <typename T>
149 inline constexpr bool is_map_like_v = is_map_like<std::decay_t<T>>::value;
150 
151 //============================================================================//
152 /**
153  * Type trait to infer if a type can be streamed via the \c << operator. This is
154  * the general case for not streamable types.
155  *
156  * \tparam S Type of the stream.
157  * \tparam T Type to be tested.
158  * \tparam Enable Type set to \c void as utility to implement the type trait.
159  */
160 template <typename S, typename T, typename Enable = void>
161 struct is_streamable : std::false_type {};
162 
163 /**
164  * Trait specialization for the case when the type is streamable.
165  *
166  * \tparam S Type of the stream.
167  * \tparam T Type to be tested.
168  */
169 template <typename S, typename T>
171  S, T, std::void_t<decltype(std::declval<S&>() << std::declval<T>())>>
172  : std::true_type {};
173 
174 /**
175  * Helper alias which is common to be defined next to a type trait.
176  */
177 template <typename S, typename T>
178 inline constexpr bool is_streamable_v = is_streamable<S, T>::value;
179 
180 //============================================================================//
181 /**
182  * Type trait to infer if a type can be written via the \c << operator, by that
183  * not only meaning that an overload exists, but also that possible contained
184  * types can be streamed, too. This is the general case for not streamable
185  * types.
186  *
187  * \tparam S Type of the stream.
188  * \tparam T Type to be tested.
189  * \tparam Enable Type set to \c void as utility to implement the type trait.
190  */
191 template <typename S, typename T, typename Enable = void>
192 struct is_writable_to_stream : std::false_type {};
193 
194 /**
195  * Trait specialization for the case in which the type is not a container and
196  * not tuple-like. Inheriting from \c std::bool_constant will provide a \c value
197  * boolean static constant member set depending on whether the type is
198  * streamable to the given stream.
199  *
200  * \note If \c T is not an STL container, it cannot be map-like and there is no
201  * need to explicitly exclude map-like types that are treated in a separate
202  * specialization.
203  *
204  * \tparam S Type of the stream.
205  * \tparam T Type to be tested.
206  */
207 template <typename S, typename T>
208 struct is_writable_to_stream<
209  S, T, std::enable_if_t<!is_stl_container_v<T> && !is_tuple_like_v<T>>>
210  : std::bool_constant<is_streamable_v<S, T>> {};
211 
212 /**
213  * Trait specialization for the case in which the type is an STL container, but
214  * not map-like. Inheriting from \c std::bool_constant will provide a \c value
215  * boolean static constant member set depending on whether the type is
216  * streamable to the given stream and the container value type is also
217  * streamable to the given stream.
218  *
219  * \note As the container value type might be itself a container or something
220  * tuple-like, it is necessary here to recurse on \c T::value_type passing it to
221  * the type trait itself.
222  *
223  * \tparam S Type of the stream.
224  * \tparam T Type to be tested.
225  */
226 template <typename S, typename T>
227 struct is_writable_to_stream<
228  S, T, std::enable_if_t<is_stl_container_v<T> && !is_map_like_v<T>>>
229  : std::bool_constant<
230  is_streamable_v<S, T> &&
231  is_writable_to_stream<S, typename T::value_type>::value> {};
232 
233 /**
234  * Trait specialization for the case in which the type is map-like. This time
235  * we do not inherit from \c std::bool_constant because we need to define a
236  * couple of type aliases to identify the map key and value types. Hence we
237  * provide ourselves a \c value member.
238  *
239  * \note As the map key or value types might be themselves containers or
240  * tuple-like, it is necessary also here to recurse on the type trait itself.
241  *
242  * \tparam S Type of the stream.
243  * \tparam T Type to be tested.
244  */
245 template <typename S, typename T>
246 struct is_writable_to_stream<S, T, std::enable_if_t<is_map_like_v<T>>> {
247  private:
248  /// Type alias for the key type
249  using key_type = typename T::key_type;
250  /// Type alias for the value type
251  using mapped_type = typename T::mapped_type;
252 
253  public:
254  /// The type trait value which is set testing the private aliases.
255  static constexpr bool value = is_streamable_v<S, T> &&
256  is_writable_to_stream<S, key_type>::value &&
257  is_writable_to_stream<S, mapped_type>::value;
258 };
259 
260 /**
261  * Trait specialization for the case in which the type is tuple-like. This time
262  * we do not inherit from \c std::bool_constant because we need an helper
263  * function to check all tuple types. Hence we provide ourselves a \c value
264  * member.
265  *
266  * \note As the tuple value types might be themselves containers or something
267  * tuple-like, it is necessary also here to recurse on the type trait itself.
268  *
269  * \tparam S Type of the stream.
270  * \tparam T Type to be tested.
271  */
272 template <typename S, typename T>
273 struct is_writable_to_stream<S, T, std::enable_if_t<is_tuple_like_v<T>>> {
274  private:
275  /**
276  * Helper function to check whether all tuple types are writable to the given
277  * stream. This has to be done with a variadic template, as so is
278  * <tt>std::tuple</tt>.
279  *
280  * \tparam I A pack of \c std::size_t types.
281  * \return true if all tuple types are writable to the given stream;
282  * \return false otherwise.
283  */
284  template <std::size_t... I>
285  static constexpr bool check(std::index_sequence<I...>) {
286  return (is_writable_to_stream<S, std::tuple_element_t<I, T>>::value && ...);
287  }
288 
289  public:
290  /// The type trait value which is set using the private method.
291  static constexpr bool value =
292  is_streamable_v<S, T> &&
293  check(std::make_index_sequence<std::tuple_size_v<T>>{});
294 };
295 
296 /**
297  * Helper alias which is common to be defined next to a type trait.
298  */
299 template <typename S, typename T>
300 inline constexpr bool is_writable_to_stream_v =
301  is_writable_to_stream<S, T>::value;
302 
303 /**
304  * Type trait to infer if there is an <tt>std::string to_string(T)</tt> overload
305  * for a given type <tt>T</tt>. This is the general case in which the overload
306  * is missing.
307  *
308  * \tparam T The type for which the overload has to be checked.
309  * \tparam Enable Type set to \c void as utility to implement the type trait.
310  */
311 template <typename T, typename Enable = void>
312 struct has_to_string : std::false_type {};
313 
314 /**
315  * Trait specialization for the case when the overload is present. The test is
316  * done in two parts. The second template parameter is used to test if there
317  * exists a \c smash::to_string(T) overload, while inheriting from \c is_same
318  * will provide a \c value boolean static constant member set to \c true if the
319  * return value is an \c std::string and to \c false otherwise.
320  *
321  * \attention Here we test the existence of the overload in the \c smash
322  * namespace and hence argument-dependent lookup (ADL) does not kick in. Said
323  * differently \c smash::to_string is unqualified and non-dependent and
324  * non-dependent names are looked up immediately, at the point of the template
325  * definition. Therefore, we need to include here the file that has the defined
326  * conversions, otherwise compilation would fail.
327  *
328  * \tparam T The type for which the overload has to be checked.
329  */
330 template <typename T>
331 struct has_to_string<T,
332  std::void_t<decltype(smash::to_string(std::declval<T>()))>>
333  : std::is_same<decltype(smash::to_string(std::declval<T>())), std::string> {
334 };
335 
336 /**
337  * Trait specialization for \c std::bitset types for which a different signature
338  * of the overload is required. Because of how these are used in SMASH when
339  * parsing the input YAML file, it makes sense that the \c to_string overload
340  * returns an \c std::vector of strings which are the corresponding enum entries
341  * converted to string.
342  *
343  * \tparam N The size of the bitset.
344  */
345 template <std::size_t N>
346 struct has_to_string<std::bitset<N>, std::void_t<decltype(smash::to_string(
347  std::declval<std::bitset<N>>()))>>
348  : std::is_same<decltype(smash::to_string(std::declval<std::bitset<N>>())),
349  std::vector<std::string>> {};
350 
351 /**
352  * Helper alias which is always defined next to a type trait.
353  */
354 template <typename T>
355 inline constexpr bool has_to_string_v = has_to_string<T>::value;
356 
357 } // namespace smash
358 
359 #endif // SRC_INCLUDE_SMASH_TRAITS_H_
Definition: action.h:24
constexpr bool is_tuple_like_v
Helper alias which is common to be defined next to a type trait.
Definition: traits.h:122
constexpr bool is_map_like_v
Helper alias which is common to be defined next to a type trait.
Definition: traits.h:149
constexpr bool is_stl_container_v
Helper alias which is common to be defined next to a type trait.
Definition: traits.h:94
static const uint32_t K[64]
The K array.
Definition: sha256.cc:70
#define S(x, n)
Definition: sha256.cc:54
Implementation of the type trait to infer if a type is an STL container.
Definition: traits.h:50
Type trait to infer if a type is map-like (for the moment only std::map is considered).
Definition: traits.h:133
Type trait to infer if a type is an STL container.
Definition: traits.h:88
Type trait to infer if a type can be streamed via the << operator.
Definition: traits.h:161
Type trait to infer if a type is tuple-like (std::pair or std::tuple or std::array or few others).
Definition: traits.h:105