userver: userver/storages/postgres/io/strong_typedef.hpp Source File
Loading...
Searching...
No Matches
strong_typedef.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/strong_typedef.hpp
4/// @brief utils::StrongTypedef I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <userver/storages/postgres/exceptions.hpp>
8#include <userver/storages/postgres/io/buffer_io.hpp>
9#include <userver/storages/postgres/io/buffer_io_base.hpp>
10#include <userver/storages/postgres/io/nullable_traits.hpp>
11
12#include <userver/utils/strong_typedef.hpp>
13#include <userver/utils/void_t.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17namespace storages::postgres::io {
18
19/// @page pg_strong_typedef uPg: support for C++ 'strong typedef' idiom
20///
21/// Within userver a strong typedef can be expressed as an enum class for
22/// integral types and as an instance of
23/// `USERVER_NAMESPACE::utils::StrongTypedef` template for all types. Both of
24/// them are supported transparently by the PostgresSQL driver with minor
25/// limitations. No additional code required.
26///
27/// @warning The underlying integral type for a strong typedef MUST be
28/// signed as PostgreSQL doesn't have unsigned types
29///
30/// The underlying type for a strong typedef must be mapped to a system or a
31/// user PostgreSQL data type. Strong typedef type derives nullability from
32/// underlying type.
33///
34/// Using `USERVER_NAMESPACE::utils::StrongTypedef` example:
35/// @snippet storages/postgres/tests/strong_typedef_pgtest.cpp Strong typedef
36///
37/// Using `enum class` example:
38/// @snippet storages/postgres/tests/strong_typedef_pgtest.cpp Enum typedef
39
40namespace traits {
41
42// Helper to detect if the strong typedef mapping is explicitly defined,
43// e.g. TimePointTz
44template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
45inline constexpr bool kIsStrongTypedefDirectlyMapped =
46 // NOLINTNEXTLINE(google-readability-casting)
47 kIsMappedToUserType<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> ||
48 // NOLINTNEXTLINE(google-readability-casting)
49 kIsMappedToSystemType<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> ||
50 // NOLINTNEXTLINE(google-readability-casting)
51 kIsMappedToArray<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>;
52
53template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
54struct IsMappedToPg<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
56
57// Mark that strong typedef mapping is a special case for disambiguating
58// specialization of CppToPg
59template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
60struct IsSpecialMapping<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
62
63template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
64struct IsNullable<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> : IsNullable<T> {};
65
66template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
67struct GetSetNull<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> {
68 using ValueType = USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>;
69 using UnderlyingGetSet = GetSetNull<T>;
70 inline static bool IsNull(const ValueType& v) { return UnderlyingGetSet::IsNull(v.GetUnderlying()); }
71 inline static void SetNull(ValueType& v) { UnderlyingGetSet::SetNull(v.GetUnderlying()); }
72 inline static void SetDefault(ValueType& v) { UnderlyingGetSet::SetDefault(v.GetUnderlying()); }
73};
74
75/// A metafunction that enables an enum type serialization to its
76/// underlying type. Can be specialized.
77template <typename T, typename = USERVER_NAMESPACE::utils::void_t<>>
78struct CanUseEnumAsStrongTypedef : std::false_type {};
79
80namespace impl {
81
82template <typename T>
83constexpr bool CheckCanUseEnumAsStrongTypedef() {
84 if constexpr (CanUseEnumAsStrongTypedef<T>{}) {
85 static_assert(
86 std::is_enum_v<T>,
87 "storages::postgres::io::traits::CanUseEnumAsStrongTypedef "
88 "should be specialized only for enums"
89 );
90 static_assert(
91 std::is_signed_v<std::underlying_type_t<T>>,
92 "storages::postgres::io::traits::CanUseEnumAsStrongTypedef should be "
93 "specialized only for enums with signed underlying type"
94 );
95
96 return true;
97 }
98
99 return false;
100}
101
102} // namespace impl
103
104template <typename T>
106
107} // namespace traits
108
109template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
110struct BufferFormatter<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
111 : detail::BufferFormatterBase<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> {
112 using BaseType = detail::BufferFormatterBase<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>;
113 using BaseType::BaseType;
114
115 template <typename Buffer>
116 void operator()(const UserTypes& types, Buffer& buf) const {
117 io::WriteBuffer(types, buf, this->value.GetUnderlying());
118 }
119};
120
121namespace detail {
122template <typename StrongTypedef, bool Categories = false>
123struct StrongTypedefParser : BufferParserBase<StrongTypedef> {
124 using BaseType = BufferParserBase<StrongTypedef>;
125 using UnderlyingType = typename StrongTypedef::UnderlyingType;
126
127 using BaseType::BaseType;
128
129 void operator()(const FieldBuffer& buffer) {
130 UnderlyingType& v = this->value.GetUnderlying();
131 io::ReadBuffer(buffer, v);
132 }
133};
134
135template <typename StrongTypedef>
136struct StrongTypedefParser<StrongTypedef, true> : BufferParserBase<StrongTypedef> {
137 using BaseType = BufferParserBase<StrongTypedef>;
138 using UnderlyingType = typename StrongTypedef::UnderlyingType;
139
140 using BaseType::BaseType;
141
142 void operator()(const FieldBuffer& buffer, const TypeBufferCategory& categories) {
143 UnderlyingType& v = this->value.GetUnderlying();
144 io::ReadBuffer(buffer, v, categories);
145 }
146};
147
148} // namespace detail
149
150template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
151struct BufferParser<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
152 : detail::StrongTypedefParser<
153 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>,
154 detail::kParserRequiresTypeCategories<T>> {
155 using BaseType = detail::StrongTypedefParser<
156 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>,
157 detail::kParserRequiresTypeCategories<T>>;
158 using BaseType::BaseType;
159};
160
161// StrongTypedef template mapping specialization
162template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
163struct CppToPg<
164 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>,
165 std::enable_if_t<!traits::kIsStrongTypedefDirectlyMapped<Tag, T, Ops, Enable> && traits::kIsMappedToPg<T>>>
166 : CppToPg<T> {};
167
168namespace traits {
169
170template <typename Tag, typename T, USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
172 : ParserBufferCategory<typename traits::IO<T>::ParserType> {};
173
174} // namespace traits
175
176namespace detail {
177
178template <typename T>
179struct EnumStrongTypedefFormatter : BufferFormatterBase<T> {
180 using BaseType = BufferFormatterBase<T>;
181 using BaseType::BaseType;
182
183 template <typename Buffer>
184 void operator()(const UserTypes& types, Buffer& buf) const {
185 io::WriteBuffer(types, buf, USERVER_NAMESPACE::utils::UnderlyingValue(this->value));
186 }
187};
188
189template <typename T>
190struct EnumStrongTypedefParser : BufferParserBase<T> {
191 using BaseType = BufferParserBase<T>;
192 using ValueType = typename BaseType::ValueType;
193 using UnderlyingType = std::underlying_type_t<ValueType>;
194
195 using BaseType::BaseType;
196
197 void operator()(const FieldBuffer& buffer) {
198 UnderlyingType v;
199 io::ReadBuffer(buffer, v);
200 this->value = static_cast<ValueType>(v);
201 }
202};
203
204} // namespace detail
205
206namespace traits {
207
208template <typename T>
211};
212
213template <typename T>
216};
217
218template <typename T>
220
221template <typename T>
223
224} // namespace traits
225
226// enum class strong typedef mapping specialization
227template <typename T>
228struct CppToPg<T, traits::EnableIfCanUseEnumAsStrongTypedef<T>> : CppToPg<std::underlying_type_t<T>> {};
229
230} // namespace storages::postgres::io
231
232USERVER_NAMESPACE_END