fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
StructBinding.h
1/*
2 * MIT License
3 *
4 * Copyright (c) 2025 Adriano dos Santos Fernandes
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#ifndef FBCPP_STRUCT_BINDING_H
26#define FBCPP_STRUCT_BINDING_H
27
28#include <cstddef>
29#include <optional>
30#include <tuple>
31#include <type_traits>
32#include <utility>
33#include <variant>
34
35
36namespace fbcpp
37{
41 template <typename T>
42 concept Aggregate = std::is_aggregate_v<T> && !std::is_array_v<T> && !std::is_union_v<T>;
43
47 template <typename T>
48 concept TupleLike = !Aggregate<T> && requires { typename std::tuple_size<T>::type; };
49} // namespace fbcpp
50
51namespace fbcpp::impl::reflection
52{
54 inline constexpr std::size_t maxFieldCount = 32;
55
57 template <typename T>
58 struct IsOptional : std::false_type
59 {
60 };
61
62 template <typename T>
63 struct IsOptional<std::optional<T>> : std::true_type
64 {
65 };
66
67 template <typename T>
68 inline constexpr bool isOptionalV = IsOptional<T>::value;
69
73 template <typename T>
74 struct IsVariant : std::false_type
75 {
76 };
77
78 template <typename... Ts>
79 struct IsVariant<std::variant<Ts...>> : std::true_type
80 {
81 };
82
83 template <typename T>
84 inline constexpr bool isVariantV = IsVariant<T>::value;
85
89 template <typename T, typename Variant>
90 struct VariantContains : std::false_type
91 {
92 };
93
94 template <typename T, typename... Ts>
95 struct VariantContains<T, std::variant<Ts...>> : std::disjunction<std::is_same<T, Ts>...>
96 {
97 };
98
99 template <typename T, typename Variant>
100 inline constexpr bool variantContainsV = VariantContains<T, Variant>::value;
101
106 template <typename T>
107 struct IsOpaqueType : std::false_type
108 {
109 };
110
111 template <typename T>
112 inline constexpr bool isOpaqueTypeV = IsOpaqueType<T>::value;
113
115 struct UniversalType
116 {
117 template <typename T>
118 constexpr operator T() const noexcept
119 {
120 if constexpr (isOptionalV<T>)
121 return T{std::nullopt};
122 else
123 return T{};
124 }
125 };
126
127 // Field count detection via SFINAE
128 template <typename T, typename Seq, typename = void>
129 struct IsBraceConstructibleImpl : std::false_type
130 {
131 };
132
133#if defined(__GNUC__) && !defined(__clang__)
134#pragma GCC diagnostic push
135#pragma GCC diagnostic ignored "-Wconversion"
136#endif
137
138 template <typename T, std::size_t... Is>
139 struct IsBraceConstructibleImpl<T, std::index_sequence<Is...>,
140 std::void_t<decltype(T{(void(Is), UniversalType{})...})>> : std::true_type
141 {
142 };
143
144#if defined(__GNUC__) && !defined(__clang__)
145#pragma GCC diagnostic pop
146#endif
147
148 template <typename T, std::size_t N>
149 inline constexpr bool isBraceConstructibleV = IsBraceConstructibleImpl<T, std::make_index_sequence<N>>::value;
150
151 // Binary search for field count
152 template <typename T, std::size_t Lo, std::size_t Hi>
153 constexpr std::size_t detectFieldCount()
154 {
155 if constexpr (Lo == Hi)
156 return Lo;
157 else
158 {
159 constexpr std::size_t Mid = Lo + (Hi - Lo + 1) / 2;
160 if constexpr (isBraceConstructibleV<T, Mid>)
161 return detectFieldCount<T, Mid, Hi>();
162 else
163 return detectFieldCount<T, Lo, Mid - 1>();
164 }
165 }
166
168 template <typename T>
169 inline constexpr std::size_t fieldCountV = detectFieldCount<T, 0, maxFieldCount>();
170
171 // toTupleRef implementation for each field count (0-32)
172 template <typename T>
173 auto toTupleRef(T&& obj)
174 {
175 constexpr std::size_t N = fieldCountV<std::remove_cvref_t<T>>;
176
177 if constexpr (N == 0)
178 return std::tuple<>{};
179 else if constexpr (N == 1)
180 {
181 auto&& [v1] = std::forward<T>(obj);
182 return std::forward_as_tuple(v1);
183 }
184 else if constexpr (N == 2)
185 {
186 auto&& [v1, v2] = std::forward<T>(obj);
187 return std::forward_as_tuple(v1, v2);
188 }
189 else if constexpr (N == 3)
190 {
191 auto&& [v1, v2, v3] = std::forward<T>(obj);
192 return std::forward_as_tuple(v1, v2, v3);
193 }
194 else if constexpr (N == 4)
195 {
196 auto&& [v1, v2, v3, v4] = std::forward<T>(obj);
197 return std::forward_as_tuple(v1, v2, v3, v4);
198 }
199 else if constexpr (N == 5)
200 {
201 auto&& [v1, v2, v3, v4, v5] = std::forward<T>(obj);
202 return std::forward_as_tuple(v1, v2, v3, v4, v5);
203 }
204 else if constexpr (N == 6)
205 {
206 auto&& [v1, v2, v3, v4, v5, v6] = std::forward<T>(obj);
207 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6);
208 }
209 else if constexpr (N == 7)
210 {
211 auto&& [v1, v2, v3, v4, v5, v6, v7] = std::forward<T>(obj);
212 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7);
213 }
214 else if constexpr (N == 8)
215 {
216 auto&& [v1, v2, v3, v4, v5, v6, v7, v8] = std::forward<T>(obj);
217 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8);
218 }
219 else if constexpr (N == 9)
220 {
221 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9] = std::forward<T>(obj);
222 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9);
223 }
224 else if constexpr (N == 10)
225 {
226 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10] = std::forward<T>(obj);
227 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10);
228 }
229 else if constexpr (N == 11)
230 {
231 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11] = std::forward<T>(obj);
232 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11);
233 }
234 else if constexpr (N == 12)
235 {
236 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12] = std::forward<T>(obj);
237 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12);
238 }
239 else if constexpr (N == 13)
240 {
241 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13] = std::forward<T>(obj);
242 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13);
243 }
244 else if constexpr (N == 14)
245 {
246 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14] = std::forward<T>(obj);
247 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14);
248 }
249 else if constexpr (N == 15)
250 {
251 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15] = std::forward<T>(obj);
252 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15);
253 }
254 else if constexpr (N == 16)
255 {
256 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16] = std::forward<T>(obj);
257 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16);
258 }
259 else if constexpr (N == 17)
260 {
261 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17] = std::forward<T>(obj);
262 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17);
263 }
264 else if constexpr (N == 18)
265 {
266 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18] =
267 std::forward<T>(obj);
268 return std::forward_as_tuple(
269 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18);
270 }
271 else if constexpr (N == 19)
272 {
273 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19] =
274 std::forward<T>(obj);
275 return std::forward_as_tuple(
276 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19);
277 }
278 else if constexpr (N == 20)
279 {
280 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20] =
281 std::forward<T>(obj);
282 return std::forward_as_tuple(
283 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20);
284 }
285 else if constexpr (N == 21)
286 {
287 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21] =
288 std::forward<T>(obj);
289 return std::forward_as_tuple(
290 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21);
291 }
292 else if constexpr (N == 22)
293 {
294 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21,
295 v22] = std::forward<T>(obj);
296 return std::forward_as_tuple(
297 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22);
298 }
299 else if constexpr (N == 23)
300 {
301 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
302 v23] = std::forward<T>(obj);
303 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
304 v18, v19, v20, v21, v22, v23);
305 }
306 else if constexpr (N == 24)
307 {
308 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
309 v23, v24] = std::forward<T>(obj);
310 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
311 v18, v19, v20, v21, v22, v23, v24);
312 }
313 else if constexpr (N == 25)
314 {
315 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
316 v23, v24, v25] = std::forward<T>(obj);
317 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
318 v18, v19, v20, v21, v22, v23, v24, v25);
319 }
320 else if constexpr (N == 26)
321 {
322 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
323 v23, v24, v25, v26] = std::forward<T>(obj);
324 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
325 v18, v19, v20, v21, v22, v23, v24, v25, v26);
326 }
327 else if constexpr (N == 27)
328 {
329 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
330 v23, v24, v25, v26, v27] = std::forward<T>(obj);
331 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
332 v18, v19, v20, v21, v22, v23, v24, v25, v26, v27);
333 }
334 else if constexpr (N == 28)
335 {
336 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
337 v23, v24, v25, v26, v27, v28] = std::forward<T>(obj);
338 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
339 v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28);
340 }
341 else if constexpr (N == 29)
342 {
343 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
344 v23, v24, v25, v26, v27, v28, v29] = std::forward<T>(obj);
345 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
346 v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29);
347 }
348 else if constexpr (N == 30)
349 {
350 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
351 v23, v24, v25, v26, v27, v28, v29, v30] = std::forward<T>(obj);
352 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
353 v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30);
354 }
355 else if constexpr (N == 31)
356 {
357 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
358 v23, v24, v25, v26, v27, v28, v29, v30, v31] = std::forward<T>(obj);
359 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
360 v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31);
361 }
362 else if constexpr (N == 32)
363 {
364 auto&& [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22,
365 v23, v24, v25, v26, v27, v28, v29, v30, v31, v32] = std::forward<T>(obj);
366 return std::forward_as_tuple(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17,
367 v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32);
368 }
369 else
370 static_assert(N <= maxFieldCount, "Struct has too many fields for struct binding (max 32)");
371 }
372
374 template <typename T>
375 using TupleType = decltype(toTupleRef(std::declval<T&>()));
376
378 template <typename T, std::size_t I>
379 using FieldType = std::remove_reference_t<std::tuple_element_t<I, TupleType<T>>>;
380} // namespace fbcpp::impl::reflection
381
382namespace fbcpp
383{
387 template <typename T>
388 concept VariantLike = impl::reflection::isVariantV<std::remove_cvref_t<T>>;
389} // namespace fbcpp
390
391
392#endif // FBCPP_STRUCT_BINDING_H
Concept constraining types to aggregates suitable for struct binding.
Concept constraining types to tuple-like types (std::tuple, std::pair, std::array).
Concept constraining types to std::variant types.
fb-cpp namespace.
Definition Attachment.h:42