Skip to content

Commit c40f600

Browse files
committed
feat universal: use concepts
Improves compile times up to 10% for cases of heavy concepts usage (postgres, logging, ...). Before the change: ``` [1065 ms] [CC] userver/universal/src/utils/meta_test.cpp [started: 0 (1775733436077), finished: 1065 (1775733437142)] ``` After: ``` [933 ms] [CC] userver/universal/src/utils/meta_test.cpp [started: 0 (1775733324601), finished: 933 (1775733325534)] ``` Tests: протестировано CI commit_hash:283402cab3c4130d0ee8ac7b4e9549eda3cd067b
1 parent 8bb1d3f commit c40f600

File tree

2 files changed

+26
-46
lines changed

2 files changed

+26
-46
lines changed

universal/include/userver/utils/meta.hpp

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
/// @brief Metaprogramming, template variables and concepts
55
/// @ingroup userver_universal
66

7+
#include <concepts>
78
#include <iosfwd>
89
#include <iterator>
910
#include <optional>
@@ -34,39 +35,21 @@ using IsRange =
3435
template <typename T>
3536
using IteratorType = std::enable_if_t<IsDetected<IsRange, T>, decltype(begin(std::declval<T&>()))>;
3637

37-
template <typename T, typename = void>
38-
struct IsIterator : std::false_type {};
39-
40-
template <typename T>
41-
struct IsIterator<T, std::void_t<typename std::iterator_traits<T>::iterator_category>> : std::true_type {};
42-
4338
template <typename T>
4439
using RangeValueType = typename std::iterator_traits<DetectedType<IteratorType, T>>::value_type;
4540

4641
template <typename T>
4742
using OstreamWriteResult = decltype(std::declval<std::ostream&>() << std::declval<const std::remove_reference_t<T>&>());
4843

49-
template <typename T, typename U>
50-
using EqualityComparisonResult = decltype(std::declval<const T&>() == std::declval<const U&>());
51-
5244
template <typename T>
5345
using StdHashResult = decltype(std::hash<T>{}(std::declval<const T&>()));
5446

55-
template <typename T>
56-
using IsSizable = decltype(std::size(std::declval<T>()));
57-
58-
template <typename T>
59-
using ReserveResult = decltype(std::declval<T&>().reserve(1));
60-
6147
template <typename T>
6248
using AtResult = decltype(std::declval<const T&>().at(std::declval<typename T::key_type>()));
6349

6450
template <typename T>
6551
using SubscriptOperatorResult = decltype(std::declval<T>()[std::declval<typename T::key_type>()]);
6652

67-
template <typename T>
68-
using PushBackResult = decltype(std::declval<T&>().push_back({}));
69-
7053
template <typename T>
7154
struct IsFixedSizeContainer : std::false_type {};
7255

@@ -85,20 +68,21 @@ constexpr bool IsSingleRange() {
8568

8669
} // namespace impl
8770

71+
// NOLINTBEGIN(readability-identifier-naming)
72+
8873
template <typename T>
89-
inline constexpr bool kIsVector = kIsInstantiationOf<std::vector, T>;
74+
concept kIsVector = kIsInstantiationOf<std::vector, T>;
9075

9176
template <typename T>
92-
inline constexpr bool kIsRange = IsDetected<impl::IsRange, T>;
77+
concept kIsRange = IsDetected<impl::IsRange, T>;
9378

9479
/// Returns true if T is an ordered or unordered map or multimap
9580
template <typename T>
96-
inline constexpr bool
97-
kIsMap = IsDetected<impl::IsRange, T> && IsDetected<impl::KeyType, T> && IsDetected<impl::MappedType, T>;
81+
concept kIsMap = IsDetected<impl::IsRange, T> && IsDetected<impl::KeyType, T> && IsDetected<impl::MappedType, T>;
9882

9983
/// Returns true if T is a map (but not a multimap!)
10084
template <typename T>
101-
inline constexpr bool kIsUniqueMap =
85+
concept kIsUniqueMap =
10286
kIsMap<T> &&
10387
IsDetected<
10488
impl::SubscriptOperatorResult,
@@ -114,39 +98,38 @@ template <typename T>
11498
using RangeValueType = DetectedType<impl::RangeValueType, T>;
11599

116100
template <typename T>
117-
inline constexpr bool kIsRecursiveRange = std::is_same_v<DetectedType<impl::RangeValueType, T>, T>;
101+
concept kIsRecursiveRange = std::is_same_v<DetectedType<impl::RangeValueType, T>, T>;
118102

119103
template <typename T>
120-
inline constexpr bool kIsIterator = impl::IsIterator<T>::value;
104+
concept kIsIterator = requires { typename std::iterator_traits<T>::iterator_category; };
121105

122106
template <typename T>
123-
inline constexpr bool kIsOptional = kIsInstantiationOf<std::optional, T>;
107+
concept kIsOptional = kIsInstantiationOf<std::optional, T>;
124108

125109
template <typename T>
126-
inline constexpr bool kIsOstreamWritable = std::is_same_v<DetectedType<impl::OstreamWriteResult, T>, std::ostream&>;
110+
concept kIsOstreamWritable = std::is_same_v<DetectedType<impl::OstreamWriteResult, T>, std::ostream&>;
127111

128112
template <typename T, typename U = T>
129-
inline constexpr bool kIsEqualityComparable = std::is_same_v<DetectedType<impl::EqualityComparisonResult, T, U>, bool>;
113+
concept kIsEqualityComparable = std::equality_comparable_with<T, U>;
130114

131115
template <typename T>
132-
inline constexpr bool
133-
kIsStdHashable = std::is_same_v<DetectedType<impl::StdHashResult, T>, std::size_t> && kIsEqualityComparable<T>;
116+
concept kIsStdHashable = std::is_same_v<DetectedType<impl::StdHashResult, T>, std::size_t> && kIsEqualityComparable<T>;
134117

135118
/// @brief Check if std::size is applicable to container
136119
template <typename T>
137-
inline constexpr bool kIsSizable = IsDetected<impl::IsSizable, T>;
120+
concept kIsSizable = requires(T value) { std::size(value); };
138121

139122
/// @brief Check if a container has `reserve`
140123
template <typename T>
141-
inline constexpr bool kIsReservable = IsDetected<impl::ReserveResult, T>;
124+
concept kIsReservable = requires(T value) { value.reserve(1); };
142125

143126
/// @brief Check if a container has 'push_back'
144127
template <typename T>
145-
inline constexpr bool kIsPushBackable = IsDetected<impl::PushBackResult, T>;
128+
concept kIsPushBackable = requires(T value) { value.push_back({}); };
146129

147130
/// @brief Check if a container has fixed size (e.g. std::array)
148131
template <typename T>
149-
inline constexpr bool kIsFixedSizeContainer = impl::IsFixedSizeContainer<T>::value;
132+
concept kIsFixedSizeContainer = impl::IsFixedSizeContainer<T>::value;
150133

151134
/// @brief Returns default inserter for a container
152135
template <typename T>
@@ -160,6 +143,8 @@ auto Inserter(T& container) {
160143
}
161144
}
162145

146+
// NOLINTEND(readability-identifier-naming)
147+
163148
} // namespace meta
164149

165150
USERVER_NAMESPACE_END

universal/include/userver/utils/meta_light.hpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct IsInstantiationOf<Template, Template<Args...>> : std::true_type {};
3939
/// @see utils::meta::IsDetected
4040
struct NotDetected {};
4141

42-
#if defined(__cpp_concepts) || defined(DOXYGEN)
42+
// NOLINTBEGIN(readability-identifier-naming)
4343

4444
/// @brief Checks whether a trait is correct for the given template args
4545
///
@@ -58,13 +58,6 @@ struct NotDetected {};
5858
template <template <typename...> typename Trait, typename... Args>
5959
concept IsDetected = requires { typename Trait<Args...>; };
6060

61-
#else
62-
63-
template <template <typename...> typename Trait, typename... Args>
64-
inline constexpr bool IsDetected = impl::Detector<NotDetected, void, Trait, Args...>::value_t::value;
65-
66-
#endif
67-
6861
/// @brief Produces the result type of a trait, or utils::meta::NotDetected if
6962
/// it's incorrect for the given template args
7063
/// @see utils::meta::IsDetected
@@ -83,22 +76,24 @@ using ExpectSame = std::enable_if_t<std::is_same_v<T, U>>;
8376

8477
/// Returns `true` if the type is an instantiation of the specified template.
8578
template <template <typename...> typename Template, typename T>
86-
inline constexpr bool kIsInstantiationOf = impl::IsInstantiationOf<Template, T>::value;
79+
concept kIsInstantiationOf = impl::IsInstantiationOf<Template, T>::value;
8780

8881
/// Returns `true` if the type (with remove cv-qualifiers) is an instantiation of the specified template.
8982
template <template <typename...> typename Template, typename T>
90-
inline constexpr bool kIsCvInstantiationOf = kIsInstantiationOf<Template, std::remove_cv_t<T>>;
83+
concept kIsCvInstantiationOf = kIsInstantiationOf<Template, std::remove_cv_t<T>>;
9184

9285
/// Returns `true` if the type is a fundamental character type.
9386
/// `signed char` and `unsigned char` are not character types.
9487
template <typename T>
95-
inline constexpr bool kIsCharacter =
88+
concept kIsCharacter =
9689
std::is_same_v<T, char> || std::is_same_v<T, wchar_t> || std::is_same_v<T, char16_t> || std::is_same_v<T, char32_t>;
9790

9891
/// Returns `true` if the type is a true integer type (not `*char*` or `bool`)
9992
/// `signed char` and `unsigned char` are integer types
10093
template <typename T>
101-
inline constexpr bool kIsInteger = std::is_integral_v<T> && !kIsCharacter<T> && !std::is_same_v<T, bool>;
94+
concept kIsInteger = std::is_integral_v<T> && !kIsCharacter<T> && !std::is_same_v<T, bool>;
95+
96+
// NOLINTEND(readability-identifier-naming)
10297

10398
} // namespace meta
10499

0 commit comments

Comments
 (0)