userver: userver/utils/meta.hpp Source File
Loading...
Searching...
No Matches
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 <concepts>
8#include <iosfwd>
9#include <iterator>
10#include <optional>
11#include <type_traits>
12#include <utility>
13
14#include <userver/compiler/impl/nodebug.hpp>
15#include <userver/utils/meta_light.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19/// @brief Metaprogramming utilities, concepts, and type traits helpers.
20namespace meta {
21
22namespace impl {
23
24using std::begin;
25using std::end;
26
27template <typename T>
28concept IsRange = requires(T& t) {
29 {
30 begin(t)
31 };
32 {
33 end(t)
34 };
35};
36
37template <IsRange T>
38using IteratorType USERVER_IMPL_NODEBUG = decltype(begin(std::declval<T&>()));
39
40template <typename T>
41using RangeValueType USERVER_IMPL_NODEBUG = typename std::iterator_traits<IteratorType<T>>::value_type;
42
43template <typename T>
44struct IsFixedSizeContainer : std::false_type {};
45
46// Boost and std arrays
47template <typename T, std::size_t Size, template <typename, std::size_t> typename Array>
48struct IsFixedSizeContainer<Array<T, Size>> : std::bool_constant<sizeof(Array<T, Size>) == sizeof(T) * Size> {};
49
50template <typename... Args>
51concept IsSingleRange = (sizeof...(Args) == 1) && (impl::IsRange<Args> && ...);
52
53} // namespace impl
54
55/// @warning Use std::ranges::range instead of this concept, except possibly in common headers
56/// where compilation time is a concern.
57template <typename T>
58concept IsRange = impl::IsRange<T>;
59
60/// Returns true if T is an ordered or unordered map or multimap
61template <typename T>
62concept IsMap = IsRange<T> && requires {
63 typename T::key_type;
64 typename T::mapped_type;
65};
66
67/// Returns true if T is a map (but not a multimap!)
68template <typename T>
69concept IsUniqueMap = IsMap<T> && requires(T& map, typename T::key_type key) {
70 map[key]; // no operator[] in multimaps
71};
72
73template <IsMap T>
74using MapKeyType = typename T::key_type;
75
76template <IsMap T>
77using MapValueType = typename T::mapped_type;
78
79/// @warning Use std::ranges::range_value_t instead of this type, except possibly in common headers
80/// where compilation time is a concern.
81template <IsRange T>
82using RangeValueType = impl::RangeValueType<T>;
83
84template <typename T>
85concept IsRecursiveRange = IsRange<T> && std::same_as<impl::RangeValueType<T>, T>;
86
87template <typename T>
88concept IsOptional = kIsInstantiationOf<std::optional, T>;
89
90template <typename T>
91concept IsOstreamWritable = requires(std::ostream& os, const std::remove_reference_t<T>& val) {
92 {
93 os << val
94 } -> std::same_as<std::ostream&>;
95};
96
97template <typename T>
98concept IsStdHashable = requires(const T& val) {
99 {
100 std::hash<T>{}(val)
101 } -> std::same_as<std::size_t>;
102} && std::equality_comparable<T>;
103
104/// @brief Check if std::size is applicable to container
105/// @warning Use std::ranges::sized_range instead of this concept, except possibly in common headers
106/// where compilation time is a concern.
107template <typename T>
108concept IsSizable = IsRange<T> && requires(T value) { std::size(value); };
109
110/// @brief Check if a container has `reserve`
111template <typename T>
112concept IsReservable = IsSizable<T> && requires(T value) { value.reserve(1); };
113
114/// @brief Check if a container has 'push_back'
115template <typename T>
116concept IsPushBackable = IsRange<T> && requires(T value, RangeValueType<T> element) {
117 value.push_back(std::move(element));
118};
119
120/// @brief Check if a container has fixed size (e.g. `std::array`)
121template <typename T>
122concept IsFixedSizeContainer = IsRange<T> && impl::IsFixedSizeContainer<T>::value;
123
124template <typename T>
125concept IsVectorLike = IsRange<T> && std::default_initializable<T> && IsReservable<T> && IsPushBackable<T>;
126
127/// @brief Returns default inserter for a container
128template <typename T>
129auto Inserter(T& container) {
130 if constexpr (IsPushBackable<T>) {
131 return std::back_inserter(container);
132 } else if constexpr (IsFixedSizeContainer<T>) {
133 return container.begin();
134 } else {
135 return std::inserter(container, container.end());
136 }
137}
138
139/// @deprecated Use @ref meta::IsVectorLike instead.
140template <typename T>
141// NOLINTNEXTLINE(readability-identifier-naming)
142concept kIsVector = IsVectorLike<T>;
143
144/// @deprecated Use @ref meta::IsRange instead.
145template <typename T>
146// NOLINTNEXTLINE(readability-identifier-naming)
147concept kIsRange = IsRange<T>;
148
149/// @deprecated Use @ref meta::IsMap instead.
150template <typename T>
151// NOLINTNEXTLINE(readability-identifier-naming)
152concept kIsMap = IsMap<T>;
153
154/// @deprecated Use @ref meta::IsOptional instead.
155template <typename T>
156// NOLINTNEXTLINE(readability-identifier-naming)
157concept kIsOptional = IsOptional<T>;
158
159/// @deprecated Use @ref meta::IsSizable instead.
160template <typename T>
161// NOLINTNEXTLINE(readability-identifier-naming)
162concept kIsSizable = IsSizable<T>;
163
164/// @deprecated Use @ref meta::IsReservable instead.
165template <typename T>
166// NOLINTNEXTLINE(readability-identifier-naming)
167concept kIsReservable = IsReservable<T>;
168
169} // namespace meta
170
171USERVER_NAMESPACE_END