userver: userver/utils/meta.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
meta.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/meta.hpp
4/// @brief Metaprogramming, template variables and concepts
5/// @ingroup userver_universal
6
7#include <iosfwd>
8#include <iterator>
9#include <optional>
10#include <type_traits>
11#include <vector>
12
13#include <userver/utils/meta_light.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17namespace meta {
18
19namespace impl {
20
21using std::begin;
22using std::end;
23
24template <typename T>
25using KeyType = typename T::key_type;
26
27template <typename T>
28using MappedType = typename T::mapped_type;
29
30template <typename T>
31using IsRange = ExpectSame<std::decay_t<decltype(begin(std::declval<T&>()))>,
32 std::decay_t<decltype(end(std::declval<T&>()))>>;
33
34template <typename T>
35using IteratorType = std::enable_if_t<kIsDetected<IsRange, T>,
36 decltype(begin(std::declval<T&>()))>;
37
38template <typename T, typename = void>
39struct IsIterator : std::false_type {};
40
41template <typename T>
42struct IsIterator<
43 T, std::void_t<typename std::iterator_traits<T>::iterator_category>>
44 : std::true_type {};
45
46template <typename T>
47using RangeValueType =
48 typename std::iterator_traits<DetectedType<IteratorType, T>>::value_type;
49
50template <typename T>
51using OstreamWriteResult =
52 decltype(std::declval<std::ostream&>()
53 << std::declval<const std::remove_reference_t<T>&>());
54
55template <typename T, typename U>
56using EqualityComparisonResult =
57 decltype(std::declval<const T&>() == std::declval<const U&>());
58
59template <typename T>
60using StdHashResult = decltype(std::hash<T>{}(std::declval<const T&>()));
61
62template <typename T>
63using IsSizable = decltype(std::size(std::declval<T>()));
64
65template <typename T>
66using ReserveResult = decltype(std::declval<T&>().reserve(1));
67
68template <typename T>
69using AtResult =
70 decltype(std::declval<const T&>().at(std::declval<typename T::key_type>()));
71
72template <typename T>
73using SubscriptOperatorResult =
74 decltype(std::declval<T>()[std::declval<typename T::key_type>()]);
75
76template <typename T>
77using PushBackResult = decltype(std::declval<T&>().push_back({}));
78
79template <typename T>
80struct IsFixedSizeContainer : std::false_type {};
81
82// Boost and std arrays
83template <typename T, std::size_t Size,
84 template <typename, std::size_t> typename Array>
85struct IsFixedSizeContainer<Array<T, Size>>
86 : std::bool_constant<sizeof(Array<T, Size>) == sizeof(T) * Size> {};
87
88template <typename... Args>
89constexpr bool IsSingleRange() {
90 if constexpr (sizeof...(Args) == 1) {
91 return kIsDetected<IsRange, Args...>;
92 } else {
93 return false;
94 }
95}
96
97} // namespace impl
98
99template <typename T>
100inline constexpr bool kIsVector = kIsInstantiationOf<std::vector, T>;
101
102template <typename T>
103inline constexpr bool kIsRange = kIsDetected<impl::IsRange, T>;
104
105/// Returns true if T is an ordered or unordered map or multimap
106template <typename T>
107inline constexpr bool kIsMap =
108 kIsDetected<impl::IsRange, T> && kIsDetected<impl::KeyType, T> &&
109 kIsDetected<impl::MappedType, T>;
110
111/// Returns true if T is a map (but not a multimap!)
112template <typename T>
113inline constexpr bool kIsUniqueMap =
114 kIsMap<T> && kIsDetected<impl::SubscriptOperatorResult,
115 T>; // no operator[] in multimaps
116
117template <typename T>
118using MapKeyType = DetectedType<impl::KeyType, T>;
119
120template <typename T>
121using MapValueType = DetectedType<impl::MappedType, T>;
122
123template <typename T>
124using RangeValueType = DetectedType<impl::RangeValueType, T>;
125
126template <typename T>
127inline constexpr bool kIsRecursiveRange =
128 std::is_same_v<DetectedType<impl::RangeValueType, T>, T>;
129
130template <typename T>
131inline constexpr bool kIsIterator = impl::IsIterator<T>::value;
132
133template <typename T>
134inline constexpr bool kIsOptional = kIsInstantiationOf<std::optional, T>;
135
136template <typename T>
137inline constexpr bool kIsOstreamWritable =
138 std::is_same_v<DetectedType<impl::OstreamWriteResult, T>, std::ostream&>;
139
140template <typename T, typename U = T>
141inline constexpr bool kIsEqualityComparable =
142 std::is_same_v<DetectedType<impl::EqualityComparisonResult, T, U>, bool>;
143
144template <typename T>
145inline constexpr bool kIsStdHashable =
146 std::is_same_v<DetectedType<impl::StdHashResult, T>, std::size_t> &&
147 kIsEqualityComparable<T>;
148
149/// @brief Check if std::size is applicable to container
150template <typename T>
151inline constexpr bool kIsSizable = kIsDetected<impl::IsSizable, T>;
152
153/// @brief Check if a container has `reserve`
154template <typename T>
155inline constexpr bool kIsReservable = kIsDetected<impl::ReserveResult, T>;
156
157/// @brief Check if a container has 'push_back'
158template <typename T>
159inline constexpr bool kIsPushBackable = kIsDetected<impl::PushBackResult, T>;
160
161/// @brief Check if a container has fixed size (e.g. std::array)
162template <typename T>
163inline constexpr bool kIsFixedSizeContainer =
165
166/// @brief Returns default inserter for a container
167template <typename T>
168auto Inserter(T& container) {
169 if constexpr (kIsPushBackable<T>) {
170 return std::back_inserter(container);
171 } else if constexpr (kIsFixedSizeContainer<T>) {
172 return container.begin();
173 } else {
174 return std::inserter(container, container.end());
175 }
176}
177
178} // namespace meta
179
180USERVER_NAMESPACE_END