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,
45 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
46inline constexpr bool kIsStrongTypedefDirectlyMapped =
47 kIsMappedToUserType<
48 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> ||
49 kIsMappedToSystemType<
50 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> ||
51 kIsMappedToArray<
52 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>;
53
54template <typename Tag, typename T,
55 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
57 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
59 kIsMappedToPg<T>> {};
60
61// Mark that strong typedef mapping is a special case for disambiguating
62// specialization of CppToPg
63template <typename Tag, typename T,
64 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
66 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
68 kIsMappedToPg<T>> {};
69
70template <typename Tag, typename T,
71 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
72struct IsNullable<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
73 : IsNullable<T> {};
74
75template <typename Tag, typename T,
76 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
78 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> {
79 using ValueType =
80 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>;
81 using UnderlyingGetSet = GetSetNull<T>;
82 inline static bool IsNull(const ValueType& v) {
83 return UnderlyingGetSet::IsNull(v.GetUnderlying());
84 }
85 inline static void SetNull(ValueType& v) {
86 UnderlyingGetSet::SetNull(v.GetUnderlying());
87 }
88 inline static void SetDefault(ValueType& v) {
89 UnderlyingGetSet::SetDefault(v.GetUnderlying());
90 }
91};
92
93/// A metafunction that enables an enum type serialization to its
94/// underlying type. Can be specialized.
95template <typename T, typename = USERVER_NAMESPACE::utils::void_t<>>
97
98namespace impl {
99
100template <typename T>
101constexpr bool CheckCanUseEnumAsStrongTypedef() {
102 if constexpr (CanUseEnumAsStrongTypedef<T>{}) {
103 static_assert(std::is_enum_v<T>,
104 "storages::postgres::io::traits::CanUseEnumAsStrongTypedef "
105 "should be specialized only for enums");
106 static_assert(
107 std::is_signed_v<std::underlying_type_t<T>>,
108 "storages::postgres::io::traits::CanUseEnumAsStrongTypedef should be "
109 "specialized only for enums with signed underlying type");
110
111 return true;
112 }
113
114 return false;
115}
116
117} // namespace impl
118
119template <typename T>
122
123} // namespace traits
124
125template <typename Tag, typename T,
126 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
128 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
130 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>> {
132 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>;
133 using BaseType::BaseType;
134
135 template <typename Buffer>
136 void operator()(const UserTypes& types, Buffer& buf) const {
138 }
139};
140
141namespace detail {
142template <typename StrongTypedef, bool Categories = false>
143struct StrongTypedefParser : BufferParserBase<StrongTypedef> {
144 using BaseType = BufferParserBase<StrongTypedef>;
145 using UnderlyingType = typename StrongTypedef::UnderlyingType;
146
147 using BaseType::BaseType;
148
149 void operator()(const FieldBuffer& buffer) {
150 UnderlyingType& v = this->value.GetUnderlying();
151 io::ReadBuffer(buffer, v);
152 }
153};
154
155template <typename StrongTypedef>
156struct StrongTypedefParser<StrongTypedef, true>
157 : BufferParserBase<StrongTypedef> {
158 using BaseType = BufferParserBase<StrongTypedef>;
159 using UnderlyingType = typename StrongTypedef::UnderlyingType;
160
161 using BaseType::BaseType;
162
163 void operator()(const FieldBuffer& buffer,
164 const TypeBufferCategory& categories) {
165 UnderlyingType& v = this->value.GetUnderlying();
166 io::ReadBuffer(buffer, v, categories);
167 }
168};
169
170} // namespace detail
171
172template <typename Tag, typename T,
173 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
175 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>
177 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>,
180 USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>,
182 using BaseType::BaseType;
183};
184
185// StrongTypedef template mapping specialization
186template <typename Tag, typename T,
187 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
188struct CppToPg<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>,
190 Tag, T, Ops, Enable> &&
191 traits::kIsMappedToPg<T>>> : CppToPg<T> {};
192
193namespace traits {
194
195template <typename Tag, typename T,
196 USERVER_NAMESPACE::utils::StrongTypedefOps Ops, typename Enable>
198 BufferParser<USERVER_NAMESPACE::utils::StrongTypedef<Tag, T, Ops, Enable>>>
199 : ParserBufferCategory<typename traits::IO<T>::ParserType> {};
200
201} // namespace traits
202
203namespace detail {
204
205template <typename T>
206struct EnumStrongTypedefFormatter : BufferFormatterBase<T> {
207 using BaseType = BufferFormatterBase<T>;
208 using BaseType::BaseType;
209
210 template <typename Buffer>
211 void operator()(const UserTypes& types, Buffer& buf) const {
212 io::WriteBuffer(types, buf,
213 USERVER_NAMESPACE::utils::UnderlyingValue(this->value));
214 }
215};
216
217template <typename T>
218struct EnumStrongTypedefParser : BufferParserBase<T> {
219 using BaseType = BufferParserBase<T>;
220 using ValueType = typename BaseType::ValueType;
221 using UnderlyingType = std::underlying_type_t<ValueType>;
222
223 using BaseType::BaseType;
224
225 void operator()(const FieldBuffer& buffer) {
226 UnderlyingType v;
227 io::ReadBuffer(buffer, v);
228 this->value = static_cast<ValueType>(v);
229 }
230};
231
232} // namespace detail
233
234namespace traits {
235
236template <typename T>
239};
240
241template <typename T>
244};
245
246template <typename T>
248};
249
250template <typename T>
252 : std::true_type {};
253
254} // namespace traits
255
256// enum class strong typedef mapping specialization
257template <typename T>
260
261} // namespace storages::postgres::io
262
263USERVER_NAMESPACE_END