GCC Code Coverage Report


Directory: include/kumi/
File: include/kumi/product_types/record.hpp
Date: 2026-10-14 16:33:56
Exec Total Coverage
Lines: 46 46 100.0%
Functions: 126 162 77.8%
Branches: 114 157 72.6%

Line Branch Exec Source
1 //======================================================================================================================
2 //! @file
3 /*
4 KUMI - Compact Tuple Tools
5 Copyright : KUMI Project Contributors
6 SPDX-License-Identifier: BSL-1.0
7 */
8 //======================================================================================================================
9 #pragma once
10
11 #include <kumi/product_types/tuple.hpp>
12
13 namespace kumi
14 {
15 //====================================================================================================================
16 /**
17 @class record
18 @ingroup kumi_record_related
19 @brief Fixed-size collection of heterogeneous tagged fields, tags are unique.
20
21 kumi::record provides an aggregate based implementation of a record. It provides algorithms and
22 functions designed to facilitate record's handling and transformations.
23
24 kumi::record is also compatible with standard tuple operations and structured bindings to some
25 extent.
26
27 @tparam Ts Sequence of fields stored inside kumi::record.
28
29 @see @ref kumi_record_type
30
31 ## Example:
32 @include doc/record/api/introduction.cpp
33 **/
34 //====================================================================================================================
35 template<typename... Ts> struct record
36 {
37 using is_record_type = void;
38 using set_t = kumi::_::make_set_t<Ts...>;
39
40 static constexpr bool is_homogeneous = false;
41
42 set_t impl;
43
44 //==================================================================================================================
45 /**
46 @name Accessors
47 @{
48 **/
49 //==================================================================================================================
50
51 //==================================================================================================================
52 /**
53 @brief Extracts the Ith field from a kumi::record
54
55 @note Does not participate in overload resolution if `I` is not in [0, sizeof...(Ts)).
56 @param i Compile-time index of the field to access
57 @return A reference to the selected field of current record.
58
59 ## Example:
60 @include doc/record/api/subscript.cpp
61 **/
62 //==================================================================================================================
63 template<std::size_t I>
64 KUMI_ABI constexpr decltype(auto) operator[]([[maybe_unused]] kumi::index_t<I> i) & noexcept
65 requires(I < sizeof...(Ts))
66 {
67 using T = kumi::element_t<I, tuple<Ts...>>;
68 1448 return static_cast<T&>(impl);
69 }
70
71 /// @overload
72 template<std::size_t I>
73 KUMI_ABI constexpr decltype(auto) operator[](kumi::index_t<I>) && noexcept
74 requires(I < sizeof...(Ts))
75 {
76 using T = kumi::element_t<I, tuple<Ts...>>;
77 615 return static_cast<T&&>(static_cast<set_t&&>(impl));
78 }
79
80 /// @overload
81 template<std::size_t I>
82 KUMI_ABI constexpr decltype(auto) operator[](kumi::index_t<I>) const&& noexcept
83 requires(I < sizeof...(Ts))
84 {
85 using T = kumi::element_t<I, tuple<Ts...>>;
86 13 return static_cast<T const&&>(static_cast<set_t const&&>(impl));
87 }
88
89 /// @overload
90 template<std::size_t I>
91 KUMI_ABI constexpr decltype(auto) operator[](kumi::index_t<I>) const& noexcept
92 requires(I < sizeof...(Ts))
93 {
94 using T = kumi::element_t<I, tuple<Ts...>>;
95 904 return static_cast<T const&>(impl);
96 }
97
98 //==================================================================================================================
99 /**
100 @brief Extracts the element with type T from a kumi::record
101
102 @note Does not participate in overload resolution if `T` is not present in the record or if
103 the record contains duplicate types
104
105 @param type The type to access in the record
106 @return A reference to the selected element of current record.
107
108 ## Example:
109 @include doc/record/api/typed_subscript.cpp
110 **/
111 //==================================================================================================================
112 template<typename T>
113 KUMI_ABI constexpr decltype(auto) operator[]([[maybe_unused]] kumi::as<T> type) & noexcept
114 requires(kumi::concepts::uniquely_typed<kumi::_::type_of_t<Ts>...> &&
115 kumi::concepts::contains_type<T, kumi::_::type_of_t<Ts>...>)
116 {
117 10 return impl(std::type_identity<T>{});
118 }
119
120 /// @overload
121 template<typename T>
122 KUMI_ABI constexpr decltype(auto) operator[](kumi::as<T>) && noexcept
123 requires(kumi::concepts::uniquely_typed<kumi::_::type_of_t<Ts>...> &&
124 kumi::concepts::contains_type<T, kumi::_::type_of_t<Ts>...>)
125 {
126 return static_cast<set_t&&>(impl)(std::type_identity<T>{});
127 }
128
129 /// @overload
130 template<typename T>
131 KUMI_ABI constexpr decltype(auto) operator[](kumi::as<T>) const&& noexcept
132 requires(kumi::concepts::uniquely_typed<kumi::_::type_of_t<Ts>...> &&
133 kumi::concepts::contains_type<T, kumi::_::type_of_t<Ts>...>)
134 {
135 return static_cast<set_t const&&>(impl)(std::type_identity<T>{});
136 }
137
138 /// @overload
139 template<typename T>
140 KUMI_ABI constexpr decltype(auto) operator[](kumi::as<T>) const& noexcept
141 requires(kumi::concepts::uniquely_typed<kumi::_::type_of_t<Ts>...> &&
142 kumi::concepts::contains_type<T, kumi::_::type_of_t<Ts>...>)
143 {
144 3 return impl(std::type_identity<T>{});
145 }
146
147 //==================================================================================================================
148 /**
149 @brief Extracts the element of the field labeled L from a kumi::record
150
151 @note Does not participate in overload resolution if the label is not present in the record
152
153 @param l label of the field to access
154 @return A reference to the element of the selected field of current record.
155
156 ## Example:
157 @include doc/record/api/labeled_subscript.cpp
158 **/
159 //==================================================================================================================
160
161 template<kumi::str Name>
162 KUMI_ABI constexpr decltype(auto) operator[]([[maybe_unused]] kumi::label_t<Name> l) & noexcept
163 requires(kumi::concepts::contains_label<kumi::label_t<Name>, Ts...>)
164 {
165 35 return impl(std::integral_constant<kumi::str<Name.size()>, Name>{});
166 }
167
168 /// @overload
169 template<kumi::str Name>
170 KUMI_ABI constexpr decltype(auto) operator[](kumi::label_t<Name>) && noexcept
171 requires(kumi::concepts::contains_label<kumi::label_t<Name>, Ts...>)
172 {
173 return static_cast<set_t&&>(impl)(std::integral_constant<kumi::str<Name.size()>, Name>{});
174 }
175
176 /// @overload
177 template<kumi::str Name>
178 KUMI_ABI constexpr decltype(auto) operator[](kumi::label_t<Name>) const&& noexcept
179 requires(kumi::concepts::contains_label<kumi::label_t<Name>, Ts...>)
180 {
181 return static_cast<set_t const&&>(impl)(std::integral_constant<kumi::str<Name.size()>, Name>{});
182 }
183
184 /// @overload
185 template<kumi::str Name>
186 KUMI_ABI constexpr decltype(auto) operator[](kumi::label_t<Name>) const& noexcept
187 requires(kumi::concepts::contains_label<kumi::label_t<Name>, Ts...>)
188 {
189 3 return impl(std::integral_constant<kumi::str<Name.size()>, Name>{});
190 }
191
192 //==================================================================================================================
193 /**
194 @brief Extracts the element whose identifier matches Id from a kumi::record
195
196 @note Does not participate in overload resolution if no field whose identifier matches Id is present in
197 the record.
198 @tparam Id Identifier of the element to access
199 @return A reference to the selected element of current record.
200
201 ## Example:
202 @include doc/record/api/named_subscript.cpp
203 **/
204 //==================================================================================================================
205 template<kumi::concepts::identifier Id>
206 KUMI_ABI constexpr decltype(auto) operator[](Id const&) & noexcept
207 requires(kumi::concepts::contains_identifier<Id, Ts...>)
208 {
209 196 return impl(kumi::_::tag_of_t<Id>{});
210 }
211
212 /// @overload
213 template<kumi::concepts::identifier Id>
214 KUMI_ABI constexpr decltype(auto) operator[](Id const&) && noexcept
215 requires(kumi::concepts::contains_identifier<Id, Ts...>)
216 {
217 22 return static_cast<set_t&&>(impl)(kumi::_::tag_of_t<Id>{});
218 }
219
220 /// @overload
221 template<kumi::concepts::identifier Id>
222 KUMI_ABI constexpr decltype(auto) operator[](Id const&) const&& noexcept
223 requires(kumi::concepts::contains_identifier<Id, Ts...>)
224 {
225 return static_cast<set_t const&&>(impl)(kumi::_::tag_of_t<Id>{});
226 }
227
228 /// @overload
229 template<kumi::concepts::identifier Id>
230 KUMI_ABI constexpr decltype(auto) operator[](Id const&) const& noexcept
231 requires(kumi::concepts::contains_identifier<Id, Ts...>)
232 {
233 1230 return impl(kumi::_::tag_of_t<Id>{});
234 }
235
236 //==================================================================================================================
237 //! @}
238 //==================================================================================================================
239
240 //==================================================================================================================
241 /**
242 @name Properties
243 @{
244 **/
245 //==================================================================================================================
246
247 /// @return Returns the number of elements in a kumi::record
248 21 [[nodiscard]] KUMI_ABI static constexpr auto size() noexcept { return sizeof...(Ts); }
249
250 /// @return Returns `true` if a kumi::record contains 0 elements
251 [[nodiscard]] KUMI_ABI static constexpr bool empty() noexcept { return sizeof...(Ts) == 0; }
252
253 /// Returns the identifiers associated to the elements of a kumi::record
254 [[nodiscard]] KUMI_ABI static constexpr auto identifiers() noexcept { return tuple{kumi::identifier_of<Ts>()...}; }
255
256 /// Returns the labels associated to the elements of a kumi::record
257 [[nodiscard]] KUMI_ABI static constexpr auto labels() noexcept { return tuple{kumi::label_of<Ts>()...}; }
258
259 // The type is named rather than taken from decltype(*this): nvcc rejects that spelling inside a
260 // lambda in a member function, and it is the only thing that kept a record out of a kernel.
261 /// Returns references to the values of the elements in a kumi::record
262 [[nodiscard]] KUMI_ABI constexpr auto values() noexcept
263 {
264 return [&]<std::size_t... I>(std::index_sequence<I...>) {
265 return tuple<stored_member_t<I, record&>...>{kumi::field_value_of(get<I>(*this))...};
266 }(std::make_index_sequence<sizeof...(Ts)>{});
267 }
268
269 /// @overload
270 [[nodiscard]] KUMI_ABI constexpr auto values() const noexcept
271 {
272 return [&]<std::size_t... I>(std::index_sequence<I...>) {
273 return tuple<stored_member_t<I, record const&>...>{kumi::field_value_of(get<I>(*this))...};
274 }(std::make_index_sequence<sizeof...(Ts)>{});
275 }
276
277 //==================================================================================================================
278 //! @}
279 //==================================================================================================================
280
281 //==================================================================================================================
282 /**
283 @brief Replaces the content of the record with the content of another record.
284 @param other kumi::record to copy or move from
285 @return `*this`
286
287 @note This function does not participate in overload resolution if the fields in each record
288 do not match or if the types between two matching fields do not match.
289 **/
290 //==================================================================================================================
291 template<typename... Us>
292 KUMI_ABI constexpr record& operator=(record<Us...> const& other)
293 requires(kumi::concepts::equivalent<record, record<Us...>>)
294 #ifndef KUMI_DOXYGEN_INVOKED
295 && (kumi::_::fieldwise_convertible<record, record<Us...>>)
296 #endif
297 {
298 ((get<kumi::identifier_of<Ts>()>(*this) = get<kumi::identifier_of<Ts>()>(KUMI_FWD(other))), ...);
299 return *this;
300 }
301
302 /// @overload
303 template<typename... Us>
304 KUMI_ABI constexpr record& operator=(record<Us...>&& other)
305 requires(kumi::concepts::equivalent<record, record<Us...>>)
306 #ifndef KUMI_DOXYGEN_INVOKED
307 && (kumi::_::fieldwise_convertible<record, record<Us...>>)
308 #endif
309 {
310 11 ((get<kumi::identifier_of<Ts>()>(*this) = get<kumi::identifier_of<Ts>()>(KUMI_FWD(other))), ...);
311 2 return *this;
312 }
313 };
314
315 //==================================================================================================================
316 /**
317 @name Comparison operators
318 @related kumi::record
319 @{
320 **/
321 //==================================================================================================================
322
323 /// @brief Compares a record with another for equality
324 template<typename... Ts, typename... Us>
325 KUMI_ABI constexpr auto operator==(record<Ts...> const& self, record<Us...> const& other) noexcept
326 #ifndef KUMI_DOXYGEN_INVOKED
327 requires(kumi::_::fieldwise_comparable<record<Ts...>, record<Us...>>)
328 #endif
329 {
330
113/156
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19 times.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 5 times.
✓ Branch 10 taken 16 times.
✓ Branch 11 taken 5 times.
✓ Branch 12 taken 8 times.
✓ Branch 13 taken 12 times.
✓ Branch 14 taken 6 times.
✓ Branch 15 taken 6 times.
✓ Branch 16 taken 12 times.
✓ Branch 17 taken 8 times.
✓ Branch 18 taken 3 times.
✓ Branch 19 taken 12 times.
✓ Branch 20 taken 11 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 12 times.
✓ Branch 23 taken 6 times.
✓ Branch 24 taken 1 times.
✓ Branch 25 taken 12 times.
✓ Branch 26 taken 6 times.
✗ Branch 27 not taken.
✓ Branch 28 taken 12 times.
✓ Branch 29 taken 6 times.
✓ Branch 30 taken 1 times.
✓ Branch 31 taken 9 times.
✓ Branch 32 taken 8 times.
✓ Branch 33 taken 1 times.
✓ Branch 34 taken 10 times.
✓ Branch 35 taken 8 times.
✓ Branch 36 taken 1 times.
✓ Branch 37 taken 12 times.
✓ Branch 38 taken 5 times.
✓ Branch 39 taken 1 times.
✓ Branch 40 taken 12 times.
✓ Branch 41 taken 5 times.
✓ Branch 42 taken 2 times.
✓ Branch 43 taken 10 times.
✓ Branch 44 taken 6 times.
✓ Branch 45 taken 1 times.
✓ Branch 46 taken 9 times.
✓ Branch 47 taken 14 times.
✓ Branch 48 taken 1 times.
✓ Branch 49 taken 5 times.
✓ Branch 50 taken 14 times.
✗ Branch 51 not taken.
✓ Branch 52 taken 3 times.
✓ Branch 53 taken 15 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 3 times.
✓ Branch 56 taken 11 times.
✓ Branch 57 taken 1 times.
✓ Branch 58 taken 3 times.
✓ Branch 59 taken 5 times.
✓ Branch 60 taken 1 times.
✓ Branch 61 taken 3 times.
✓ Branch 62 taken 4 times.
✓ Branch 63 taken 1 times.
✓ Branch 64 taken 3 times.
✓ Branch 65 taken 4 times.
✓ Branch 66 taken 1 times.
✓ Branch 67 taken 2 times.
✓ Branch 68 taken 2 times.
✓ Branch 69 taken 1 times.
✓ Branch 70 taken 2 times.
✓ Branch 71 taken 2 times.
✓ Branch 72 taken 1 times.
✓ Branch 73 taken 2 times.
✓ Branch 74 taken 1 times.
✓ Branch 75 taken 1 times.
✓ Branch 76 taken 1 times.
✓ Branch 77 taken 1 times.
✗ Branch 78 not taken.
✓ Branch 79 taken 1 times.
✓ Branch 80 taken 1 times.
✗ Branch 81 not taken.
✓ Branch 82 taken 1 times.
✓ Branch 83 taken 1 times.
✗ Branch 84 not taken.
✓ Branch 85 taken 1 times.
✓ Branch 86 taken 1 times.
✗ Branch 87 not taken.
✓ Branch 89 taken 1 times.
✓ Branch 90 taken 1 times.
✗ Branch 91 not taken.
✓ Branch 92 taken 1 times.
✓ Branch 93 taken 1 times.
✗ Branch 94 not taken.
✓ Branch 95 taken 1 times.
✓ Branch 96 taken 1 times.
✗ Branch 97 not taken.
✓ Branch 98 taken 2 times.
✗ Branch 99 not taken.
✓ Branch 101 taken 2 times.
✗ Branch 102 not taken.
✓ Branch 104 taken 2 times.
✗ Branch 105 not taken.
✓ Branch 107 taken 3 times.
✗ Branch 108 not taken.
✓ Branch 110 taken 3 times.
✗ Branch 111 not taken.
✓ Branch 113 taken 3 times.
✗ Branch 114 not taken.
✓ Branch 116 taken 3 times.
✗ Branch 117 not taken.
✓ Branch 119 taken 3 times.
✗ Branch 120 not taken.
✓ Branch 122 taken 2 times.
✗ Branch 123 not taken.
✓ Branch 125 taken 2 times.
✗ Branch 126 not taken.
✓ Branch 128 taken 2 times.
✗ Branch 129 not taken.
✓ Branch 131 taken 3 times.
✗ Branch 132 not taken.
✓ Branch 134 taken 3 times.
✗ Branch 135 not taken.
✓ Branch 137 taken 3 times.
✗ Branch 138 not taken.
✓ Branch 140 taken 1 times.
✗ Branch 141 not taken.
✓ Branch 143 taken 1 times.
✗ Branch 144 not taken.
✓ Branch 146 taken 1 times.
✗ Branch 147 not taken.
✓ Branch 149 taken 1 times.
✗ Branch 150 not taken.
✓ Branch 152 taken 1 times.
✗ Branch 153 not taken.
✓ Branch 155 taken 1 times.
✗ Branch 156 not taken.
✓ Branch 158 taken 1 times.
✗ Branch 159 not taken.
✓ Branch 161 taken 1 times.
✗ Branch 162 not taken.
✓ Branch 165 taken 2 times.
✗ Branch 166 not taken.
✓ Branch 168 taken 2 times.
✗ Branch 169 not taken.
✓ Branch 171 taken 2 times.
✗ Branch 172 not taken.
✓ Branch 174 taken 2 times.
✗ Branch 175 not taken.
✓ Branch 177 taken 2 times.
✗ Branch 178 not taken.
✓ Branch 180 taken 2 times.
✗ Branch 181 not taken.
✓ Branch 183 taken 2 times.
✗ Branch 184 not taken.
✓ Branch 186 taken 2 times.
✗ Branch 187 not taken.
1170 return ((get<kumi::identifier_of<Ts>()>(self) == get<kumi::identifier_of<Ts>()>(other)) && ...);
331 }
332
333 /// @brief Compares a record with another for inequality
334 template<typename... Ts, typename... Us>
335 KUMI_ABI constexpr auto operator!=(record<Ts...> const& self, record<Us...> const& other) noexcept
336 #ifndef KUMI_DOXYGEN_INVOKED
337 requires(kumi::_::fieldwise_comparable<record<Ts...>, record<Us...>>)
338 #endif
339 {
340 return !(self == other);
341 }
342
343 //==================================================================================================================
344 //! @}
345 //==================================================================================================================
346
347 //==================================================================================================================
348 /**
349 @related kumi::record
350 @brief Inserts a kumi::record in an output stream
351 **/
352 //==================================================================================================================
353 template<typename CharT, typename Traits, typename... Ts>
354 291 std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, record<Ts...> const& t) noexcept
355 {
356 291 return kumi::_::print(os, t, '{', ',', '}', std::make_index_sequence<sizeof...(Ts) - 1>{});
357 }
358
359 template<> struct record<>
360 {
361 using is_record_type = void;
362 static constexpr bool is_homogeneous = false;
363
364 6 [[nodiscard]] KUMI_ABI static constexpr auto size() noexcept { return std::size_t{0}; }
365
366 [[nodiscard]] KUMI_ABI static constexpr auto empty() noexcept { return true; }
367
368 [[nodiscard]] KUMI_ABI static constexpr auto names() noexcept { return kumi::tuple{}; }
369
370 [[nodiscard]] KUMI_ABI static constexpr auto values() noexcept { return kumi::tuple{}; }
371
372 32 KUMI_ABI friend constexpr auto operator<=>(record<>, record<>) noexcept = default;
373
374 template<typename T>
375 [[nodiscard]] KUMI_ABI constexpr operator T() const noexcept
376 requires(kumi::concepts::unit_type<T>)
377 {
378 return {};
379 }
380
381 template<typename T>
382 [[nodiscard]] KUMI_ABI constexpr operator T() noexcept
383 requires(kumi::concepts::unit_type<T>)
384 {
385 return {};
386 }
387
388 template<typename CharT, typename Traits>
389 2 friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, record<>) noexcept
390 {
391 2 return os << "{}";
392 }
393 };
394
395 //====================================================================================================================
396 // Specialisation to clearly signal errors due to duplicate fields
397 //====================================================================================================================
398 template<typename... Ts>
399 requires(!kumi::concepts::uniquely_named<Ts...> || !kumi::concepts::fully_named<Ts...> ||
400 !kumi::concepts::uniquely_labeled<Ts...>)
401 struct record<Ts...>
402 {
403 static_assert(kumi::concepts::uniquely_named<Ts...>, "Duplicate fields in record definition");
404 static_assert(kumi::concepts::uniquely_labeled<Ts...>, "Duplicate identifier representation in record definition");
405 static_assert(kumi::concepts::fully_named<Ts...>, "Anonymous field usage in record definition");
406 record(Ts&&...) = delete;
407 };
408
409 //====================================================================================================================
410 /**
411 @name Record Deduction Guides
412 @{
413 **/
414 //====================================================================================================================
415
416 //====================================================================================================================
417 /**
418 @related kumi::record
419 @brief kumi::record deduction guide
420 @tparam Ts Type lists to build the record with.
421 **/
422 //====================================================================================================================
423 template<typename... Ts> record(Ts&&...) -> record<std::unwrap_ref_decay_t<Ts>...>;
424
425 //====================================================================================================================
426 //! @}
427 //====================================================================================================================
428
429 //====================================================================================================================
430 /**
431 @name Record construction
432 @{
433 **/
434 //====================================================================================================================
435
436 template<kumi::concepts::identifier auto... Fields> struct tie_as_record_t
437 {
438 template<typename... Ts> [[nodiscard]] KUMI_ABI constexpr auto operator()(Ts&... ts) const
439 {
440 10 return kumi::record<kumi::field<decltype(Fields), Ts&>...>{ts...};
441 }
442 };
443
444 //====================================================================================================================
445 /**
446 @ingroup kumi_record_related
447
448 @var tie_as_record
449 @brief Creates a kumi::record of lvalue references to its arguments.
450
451 @qualifier inline
452 @qualifier constexpr
453 @qualifier nodiscard
454
455 @groupheader{Header file}
456 @code
457 #include <kumi/product_types/record.hpp>
458 @endcode
459
460 @groupheader{Call Signature}
461
462 @code
463 template<kumi::concepts::identifier auto... Fields, typename... Ts>
464 constexpr auto tie_as_record(Ts&... ts);
465 @endcode
466
467 @subgroupheader{Parameters}
468
469 - `Fields` : Non type template parameters names to associate with each element.
470 - `ts`: Zero or more lvalue arguments to construct the record from.
471
472 @subgroupheader{Return value}
473
474 - A kumi::record object containing lvalue references.
475
476 @groupheader{Example}
477
478 @godbolt{doc/record/api/tie_as_record.cpp}
479
480 **/
481 //====================================================================================================================
482 template<kumi::concepts::identifier auto... Fields>
483 KUMI_VARIABLE_ABI constexpr tie_as_record_t<Fields...> tie_as_record{};
484
485 template<kumi::concepts::identifier auto... Fields> struct forward_as_record_t
486 {
487 template<typename... Ts>
488 [[nodiscard]] KUMI_ABI constexpr auto operator()(Ts&&... ts) const
489 requires(sizeof...(Fields) == sizeof...(Ts))
490 {
491 6 return kumi::record<kumi::field<decltype(Fields), Ts&&>...>{KUMI_FWD(ts)...};
492 }
493 };
494
495 //====================================================================================================================
496 /**
497 @ingroup kumi_record_related
498
499 @var forward_as_record
500 @brief Creates a kumi::record of forwarding references to its arguments.
501
502 Constructs a record of references to the arguments in ts suitable for forwarding as an
503 argument to a function. The record has rvalue reference data members when rvalues are used as
504 arguments, and otherwise has lvalue reference data members.
505
506 @note If the arguments are temporaries, `forward_as_record` does not extend their lifetime;
507 they have to be used before the end of the full expression.
508
509 @qualifier inline
510 @qualifier constexpr
511 @qualifier nodiscard
512
513 @groupheader{Header file}
514 @code
515 #include <kumi/product_types/record.hpp>
516 @endcode
517
518 @groupheader{Call Signature}
519
520 @code
521 template<typename... Ts>
522 constexpr auto forward_as_record(Ts&&... ts);
523 @endcode
524
525 @subgroupheader{Parameters}
526 - `Fields`: Non type template parameters names to associate with each element.
527 - `ts`: Zero or more lvalue arguments to construct the record from.
528
529 @subgroupheader{Return value}
530
531 - A kumi::record constructed as `kumi::record<kumi::field<decltype(Fields),Ts&&>...>(std::forward<Ts>(ts)...)`
532
533 @groupheader{Example}
534
535 @godbolt{doc/record/api/forward_as_record.cpp}
536
537 **/
538 //====================================================================================================================
539 template<kumi::concepts::identifier auto... Fields>
540 KUMI_VARIABLE_ABI constexpr forward_as_record_t<Fields...> forward_as_record{};
541
542 struct make_record_t
543 {
544 template<typename... Ts>
545 [[nodiscard]] KUMI_ABI constexpr auto operator()(Ts&&... ts) const -> kumi::record<std::unwrap_ref_decay_t<Ts>...>
546 requires(kumi::concepts::uniquely_named<Ts...> && kumi::concepts::fully_named<Ts...>)
547 {
548 185 return {KUMI_FWD(ts)...};
549 1 }
550 };
551
552 //====================================================================================================================
553 /**
554 @ingroup kumi_record_related
555
556 @var make_record
557 @brief Creates a record object, deducing the target type from the types of arguments.
558
559 @qualifier inline
560 @qualifier constexpr
561 @qualifier nodiscard
562
563 @groupheader{Header file}
564 @code
565 #include <kumi/product_types/record.hpp>
566 @endcode
567
568 @groupheader{Call Signature}
569
570 @code
571 template<typename... Ts>
572 constexpr auto make_record(Ts&&... ts);
573 @endcode
574
575 @subgroupheader{Parameters}
576
577 - `ts`: Zero or more arguments to construct the record from.
578
579 @subgroupheader{Return value}
580
581 - A kumi::record constructed from the arguments or their inner references when an argument
582 is an instance of `std::reference_wrapper`.
583
584 @groupheader{Example}
585
586 @godbolt{doc/record/api/make_record.cpp}
587
588 **/
589 //====================================================================================================================
590 KUMI_VARIABLE_ABI constexpr make_record_t make_record{};
591
592 namespace _
593 {
594 template<kumi::concepts::record_type T, std::size_t... I>
595 18 KUMI_HIDDEN_ABI constexpr auto to_ref_(kumi::_::adl_tag_t, T&& t, std::index_sequence<I...>) -> kumi::record<
596 kumi::field<decltype(kumi::identifier_of<kumi::element_t<I, T>>()), kumi::stored_member_t<I, T>&&>...>
597 {
598 132 return {kumi::field_value_of(get<I>(KUMI_FWD(t)))...};
599 }
600 }
601
602 //====================================================================================================================
603 //! @}
604 //====================================================================================================================
605
606 //====================================================================================================================
607 /**
608 @name Record conversion
609 @{
610 **/
611 //====================================================================================================================
612
613 template<kumi::concepts::record_type Type> struct from_record_t
614 {
615 private:
616 template<typename... Ts, std::size_t... I>
617 3 KUMI_HIDDEN_ABI constexpr Type impl(record<Ts...> const& r, std::index_sequence<I...>) const
618 {
619 5 return Type{get<kumi::identifier_of<kumi::element_t<I, Type>>()>(r)...};
620 3 }
621
622 public:
623 template<typename... Ts>
624 [[nodiscard]] KUMI_ABI constexpr Type operator()(record<Ts...> const& r) const
625 requires(
626 kumi::concepts::equivalent<typename kumi::_::as_tuple<Type, std::make_index_sequence<kumi::size_v<Type>>>::type,
627 kumi::tuple<Ts...>>)
628 {
629
1/1
✓ Branch 1 taken 1 times.
3 return impl(r, std::make_index_sequence<sizeof...(Ts)>{});
630 }
631 };
632
633 //====================================================================================================================
634 /**
635 @ingroup kumi_record_related
636
637 @var from_record
638 @brief Converts a kumi::record to an instance of an arbitrary type that models kumi::record_type
639
640
641 Constructs an instance of `Type` by passing elements of `r` to the appropriate constructor.
642
643 @qualifier inline
644 @qualifier constexpr
645 @qualifier nodiscard
646
647 @groupheader{Header file}
648 @code
649 #include <kumi/product_types/record.hpp>
650 @endcode
651
652 @groupheader{Call Signature}
653
654 @code
655 template<typename Type, record_type R>
656 constexpr auto from_record(R&& r);
657 @endcode
658
659 @subgroupheader{Parameters}
660
661 - `Type` : Type to generate
662 - `r`: kumi::record to convert.
663
664 @subgroupheader{Return value}
665
666 - An instance of `Type` constructed from each element of `r` in order.
667
668 @groupheader{Example}
669
670 @godbolt{doc/record/api/from_record.cpp}
671
672 **/
673 //====================================================================================================================
674 template<kumi::concepts::record_type Type> KUMI_VARIABLE_ABI constexpr from_record_t<Type> from_record{};
675
676 struct to_record_t
677 {
678 private:
679 5 template<typename R, std::size_t... I> KUMI_HIDDEN_ABI constexpr auto impl(R&& r, std::index_sequence<I...>) const
680 {
681 if constexpr (sizeof...(I) == 0) return kumi::record{};
682 else
683 return kumi::record{kumi::capture_field<kumi::identifier_of<kumi::element_t<I, R>>()>(
684 16 get<kumi::identifier_of<kumi::element_t<I, R>>()>(KUMI_FWD(r)))...};
685 }
686
687 public:
688 template<kumi::concepts::record_type Type> [[nodiscard]] KUMI_ABI constexpr auto operator()(Type&& r) const
689 {
690 3 return impl(KUMI_FWD(r), std::make_index_sequence<kumi::size_v<Type>>{});
691 }
692 };
693
694 //====================================================================================================================
695 /**
696 @ingroup kumi_record_related
697
698 @var to_record
699 @brief Converts a kumi::concepts::record_type to an instance of kumi::record
700
701 Constructs an instance of kumi::record from the elements of the kumi::concepts::record_type parameters
702
703 @qualifier inline
704 @qualifier constexpr
705 @qualifier nodiscard
706
707 @groupheader{Header file}
708 @code
709 #include <kumi/product_types/record.hpp>
710 @endcode
711
712 @groupheader{Call Signature}
713
714 @code
715 template<record_type R>
716 constexpr auto to_record(R&& r);
717 @endcode
718
719 @subgroupheader{Parameters}
720
721 - `r`: kumi::concepts::record_type to convert
722
723 @subgroupheader{Return value}
724
725 - An instance of kumi::record constructed from each element of `r` in order.
726
727 @groupheader{Example}
728
729 @godbolt{doc/record/api/to_record.cpp}
730
731 **/
732 //====================================================================================================================
733 KUMI_VARIABLE_ABI constexpr to_record_t to_record{};
734
735 //====================================================================================================================
736 //! @}
737 //====================================================================================================================
738
739 //====================================================================================================================
740 /**
741 @name Record accessors
742 @{
743 **/
744 //====================================================================================================================
745
746 //====================================================================================================================
747 /**
748 @related record
749 @brief Extracts the Ith field from a kumi::record
750
751 @note Does not participate in overload resolution if `I` is not in [0, sizeof...(Ts)).
752 @tparam I Compile-time index of the field to access
753 @param r Record to index
754 @return A reference to the selected field of r.
755
756 @qualifier nodiscard
757 @qualifier inline
758 @qualifier constexpr
759 @qualifier noexcept
760
761 ## Example:
762 @include doc/record/api/get.cpp
763 **/
764 //====================================================================================================================
765 template<std::size_t I, typename... Ts>
766 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...>& r) noexcept
767 requires(I < sizeof...(Ts))
768 {
769 1450 return r[kumi::index<I>];
770 }
771
772 /// @related record
773 /// @overload
774 template<std::size_t I, typename... Ts>
775 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...>&& r) noexcept
776 requires(I < sizeof...(Ts))
777 {
778 681 return static_cast<record<Ts...>&&>(r)[kumi::index<I>];
779 }
780
781 /// @related record
782 /// @overload
783 template<std::size_t I, typename... Ts>
784 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...> const& r) noexcept
785 requires(I < sizeof...(Ts))
786 {
787 889 return r[kumi::index<I>];
788 }
789
790 /// @related record
791 /// @overload
792 template<std::size_t I, typename... Ts>
793 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...> const&& r) noexcept
794 requires(I < sizeof...(Ts))
795 {
796 13 return static_cast<record<Ts...> const&&>(r)[kumi::index<I>];
797 }
798
799 //====================================================================================================================
800 /**
801 @related record
802 @brief Extracts the element of the field labeled L from a kumi::record if it exists
803
804 @note Does not participate in overload resolution if the names are not unique
805 @tparam L Non type template parameter label of the field to access
806 @param r Record to index
807 @return A reference to the element of the selected field of r.
808
809 @qualifier nodiscard
810 @qualifier inline
811 @qualifier constexpr
812 @qualifier noexcept
813
814 ## Example:
815 @include doc/record/api/labeled_get.cpp
816 **/
817 //====================================================================================================================
818 template<kumi::str L, typename... Ts>
819 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...>& r) noexcept
820 requires(kumi::_::contains_label<L, Ts...>())
821 {
822 30 return r[kumi::label<L>];
823 }
824
825 /// @related record
826 /// @overload
827 template<kumi::str L, typename... Ts>
828 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...>&& r) noexcept
829 requires(kumi::_::contains_label<L, Ts...>())
830 {
831 return static_cast<record<Ts...>&&>(r)[kumi::label<L>];
832 }
833
834 /// @related record
835 /// @overload
836 template<kumi::str L, typename... Ts>
837 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...> const& r) noexcept
838 requires(kumi::_::contains_label<L, Ts...>())
839 {
840 3 return r[kumi::label<L>];
841 }
842
843 /// @related record
844 /// @overload
845 template<kumi::str L, typename... Ts>
846 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...> const&& r) noexcept
847 requires(kumi::_::contains_label<L, Ts...>())
848 {
849 return static_cast<record<Ts...> const&&>(r)[kumi::label<L>];
850 }
851
852 //====================================================================================================================
853 /**
854 @related record
855 @brief Extracts the field identified by Id from a kumi::record if it exists
856
857 @note Does not participate in overload resolution if the names are not unique
858 @tparam Id Non type template parameter identifier of the element to access
859 @param r Record to index
860 @return A reference to the selected element of r.
861
862 @qualifier nodiscard
863 @qualifier inline
864 @qualifier constexpr
865 @qualifier noexcept
866
867 ## Example:
868 @include doc/record/api/named_get.cpp
869 **/
870 //====================================================================================================================
871 template<kumi::concepts::identifier auto Id, typename... Ts>
872 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...>& r) noexcept
873 requires(kumi::concepts::contains_identifier<decltype(Id), Ts...>)
874 {
875 163 return r[Id];
876 }
877
878 /// @related record
879 /// @overload
880 template<kumi::concepts::identifier auto Id, typename... Ts>
881 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...>&& r) noexcept
882 requires(kumi::concepts::contains_identifier<decltype(Id), Ts...>)
883 {
884 19 return static_cast<record<Ts...>&&>(r)[Id];
885 }
886
887 /// @related record
888 /// @overload
889 template<kumi::concepts::identifier auto Id, typename... Ts>
890 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...> const& r) noexcept
891 requires(kumi::concepts::contains_identifier<decltype(Id), Ts...>)
892 {
893 1230 return r[Id];
894 }
895
896 /// @related record
897 /// @overload
898 template<kumi::concepts::identifier auto Id, typename... Ts>
899 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...> const&& r) noexcept
900 requires(kumi::concepts::contains_identifier<decltype(Id), Ts...>)
901 {
902 return static_cast<record<Ts...> const&&>(r)[Id];
903 }
904
905 //====================================================================================================================
906 /**
907 @related record
908 @brief Extracts the field whose underlying type is T from a kumi::record if it exists
909
910 @note Does not participate in overload resolution if the types are not unique
911 @tparam T Type of the element to access
912 @param r Record to index
913 @return A reference to the selected element of r.
914
915 @qualifier nodiscard
916 @qualifier inline
917 @qualifier constexpr
918 @qualifier noexcept
919
920 ## Example:
921 @include doc/record/api/typed_get.cpp
922 **/
923 //====================================================================================================================
924 template<typename T, typename... Ts>
925 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...>& r) noexcept
926 requires(kumi::concepts::uniquely_typed<kumi::_::type_of_t<Ts>...> &&
927 kumi::concepts::contains_type<T, kumi::_::type_of_t<Ts>...>)
928 {
929 9 return r[kumi::as<T>{}];
930 }
931
932 /// @related record
933 /// @overload
934 template<typename T, typename... Ts>
935 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...>&& r) noexcept
936 requires(kumi::concepts::uniquely_typed<kumi::_::type_of_t<Ts>...> &&
937 kumi::concepts::contains_type<T, kumi::_::type_of_t<Ts>...>)
938 {
939 return static_cast<record<Ts...>&&>(r)[kumi::as<T>{}];
940 }
941
942 /// @related record
943 /// @overload
944 template<typename T, typename... Ts>
945 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...> const& r) noexcept
946 requires(kumi::concepts::uniquely_typed<kumi::_::type_of_t<Ts>...> &&
947 kumi::concepts::contains_type<T, kumi::_::type_of_t<Ts>...>)
948 {
949 6 return r[kumi::as<T>{}];
950 }
951
952 /// @related record
953 /// @overload
954 template<typename T, typename... Ts>
955 [[nodiscard]] KUMI_ABI constexpr decltype(auto) get(record<Ts...> const&& r) noexcept
956 requires(kumi::concepts::uniquely_typed<kumi::_::type_of_t<Ts>...> &&
957 kumi::concepts::contains_type<T, kumi::_::type_of_t<Ts>...>)
958 {
959 return static_cast<record<Ts...> const&&>(r)[kumi::as<T>{}];
960 }
961
962 //====================================================================================================================
963 //! @}
964 //====================================================================================================================
965 #ifndef KUMI_DOXYGEN_INVOKED
966 /// Improves diagnostic for out of bounds index
967 template<std::size_t I, typename R>
968 requires(kumi::is_kumi_record_v<std::remove_cvref_t<R>> && ((I >= kumi::size_v<R>) || (I < 0)))
969 constexpr auto get(R&& r) = delete;
970
971 /// Improves diagnostic for non present label
972 template<kumi::str S, typename R>
973 requires(kumi::is_kumi_record_v<std::remove_cvref_t<R>> && !kumi::_::contains_identifier<S, R>())
974 constexpr auto get(R&& r) = delete;
975
976 /// Improves diagnostic for non present identifier
977 template<kumi::concepts::identifier auto S, typename R>
978 requires(kumi::is_kumi_record_v<std::remove_cvref_t<R>> && !kumi::concepts::contains_identifier<decltype(S), R>)
979 constexpr auto get(R&& r) = delete;
980
981 /// Improves diagnostic for non present type
982 template<typename U, typename R>
983 requires(kumi::is_kumi_record_v<std::remove_cvref_t<R>> && !kumi::concepts::contains_type<U, R>)
984 constexpr auto get(R&& r) = delete;
985 #endif
986
987 // Builder protocol
988 template<kumi::concepts::record_type R> struct builder<R>
989 {
990 using type = R;
991
992 template<typename... Us> using to = kumi::record<Us...>;
993
994 template<typename... Args> [[nodiscard]] KUMI_ABI static constexpr auto make(Args&&... args)
995 {
996 178 return kumi::make_record(KUMI_FWD(args)...);
997 }
998
999 template<typename... Args> [[nodiscard]] KUMI_ABI static constexpr auto build(Args&&... args)
1000 {
1001 return kumi::record{KUMI_FWD(args)...};
1002 }
1003 };
1004
1005 // As we are lacking a proper mechanism to find the least restrictive subtype, we fallback to a specializable trait
1006 template<kumi::concepts::record_type... Ts> struct common_product_type<Ts...>
1007 {
1008 using type = kumi::record<>;
1009 };
1010 }
1011