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 <vector>
13
14#include <userver/utils/meta_light.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace meta {
19
20namespace impl {
21
22using std::begin;
23using std::end;
24
25template <typename T>
26using KeyType = typename T::key_type;
27
28template <typename T>
29using MappedType = typename T::mapped_type;
30
31template <typename T>
32using IsRange =
33 ExpectSame<std::decay_t<decltype(begin(std::declval<T&>()))>, std::decay_t<decltype(end(std::declval<T&>()))>>;
34
35template <typename T>
36using IteratorType = std::enable_if_t<IsDetected<IsRange, T>, decltype(begin(std::declval<T&>()))>;
37
38template <typename T>
39using RangeValueType = typename std::iterator_traits<DetectedType<IteratorType, T>>::value_type;
40
41template <typename T>
42using OstreamWriteResult = decltype(std::declval<std::ostream&>() << std::declval<const std::remove_reference_t<T>&>());
43
44template <typename T>
45using StdHashResult = decltype(std::hash<T>{}(std::declval<const T&>()));
46
47template <typename T>
48using AtResult = decltype(std::declval<const T&>().at(std::declval<typename T::key_type>()));
49
50template <typename T>
51using SubscriptOperatorResult = decltype(std::declval<T>()[std::declval<typename T::key_type>()]);
52
53template <typename T>
54struct IsFixedSizeContainer : std::false_type {};
55
56// Boost and std arrays
57template <typename T, std::size_t Size, template <typename, std::size_t> typename Array>
58struct IsFixedSizeContainer<Array<T, Size>> : std::bool_constant<sizeof(Array<T, Size>) == sizeof(T) * Size> {};
59
60template <typename... Args>
61constexpr bool IsSingleRange() {
62 if constexpr (sizeof...(Args) == 1) {
63 return IsDetected<IsRange, Args...>;
64 } else {
65 return false;
66 }
67}
68
69} // namespace impl
70
71// NOLINTBEGIN(readability-identifier-naming)
72
73template <typename T>
74concept kIsVector = kIsInstantiationOf<std::vector, T>;
75
76template <typename T>
77concept kIsRange = IsDetected<impl::IsRange, T>;
78
79/// Returns true if T is an ordered or unordered map or multimap
80template <typename T>
81concept kIsMap = IsDetected<impl::IsRange, T> && IsDetected<impl::KeyType, T> && IsDetected<impl::MappedType, T>;
82
83/// Returns true if T is a map (but not a multimap!)
84template <typename T>
85concept kIsUniqueMap =
86 kIsMap<T> &&
87 IsDetected<
88 impl::SubscriptOperatorResult,
89 T>; // no operator[] in multimaps
90
91template <typename T>
92using MapKeyType = DetectedType<impl::KeyType, T>;
93
94template <typename T>
95using MapValueType = DetectedType<impl::MappedType, T>;
96
97template <typename T>
98using RangeValueType = DetectedType<impl::RangeValueType, T>;
99
100template <typename T>
101concept kIsRecursiveRange = std::is_same_v<DetectedType<impl::RangeValueType, T>, T>;
102
103template <typename T>
104concept kIsIterator = requires { typename std::iterator_traits<T>::iterator_category; };
105
106template <typename T>
107concept kIsOptional = kIsInstantiationOf<std::optional, T>;
108
109template <typename T>
110concept kIsOstreamWritable = std::is_same_v<DetectedType<impl::OstreamWriteResult, T>, std::ostream&>;
111
112template <typename T, typename U = T>
113concept kIsEqualityComparable = std::equality_comparable_with<T, U>;
114
115template <typename T>
116concept kIsStdHashable = std::is_same_v<DetectedType<impl::StdHashResult, T>, std::size_t> && kIsEqualityComparable<T>;
117
118/// @brief Check if std::size is applicable to container
119template <typename T>
120concept kIsSizable = requires(T value) { std::size(value); };
121
122/// @brief Check if a container has `reserve`
123template <typename T>
124concept kIsReservable = requires(T value) { value.reserve(1); };
125
126/// @brief Check if a container has 'push_back'
127template <typename T>
128concept kIsPushBackable = requires(T value) { value.push_back({}); };
129
130/// @brief Check if a container has fixed size (e.g. std::array)
131template <typename T>
132concept kIsFixedSizeContainer = impl::IsFixedSizeContainer<T>::value;
133
134/// @brief Returns default inserter for a container
135template <typename T>
136auto Inserter(T& container) {
137 if constexpr (kIsPushBackable<T>) {
138 return std::back_inserter(container);
139 } else if constexpr (kIsFixedSizeContainer<T>) {
140 return container.begin();
141 } else {
142 return std::inserter(container, container.end());
143 }
144}
145
146// NOLINTEND(readability-identifier-naming)
147
148} // namespace meta
149
150USERVER_NAMESPACE_END