Coverage Report

Created: 2025-12-14 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glaze/include/glaze/tuplet/tuple.hpp
Line
Count
Source
1
// Glaze Library
2
// For the license information refer to glaze.hpp
3
4
// original source (significantly refactored): https://github.com/codeinred/tuplet
5
6
#pragma once
7
8
#include <compare>
9
#include <concepts>
10
#include <cstddef>
11
#include <type_traits>
12
#include <utility>
13
14
#include "glaze/util/inline.hpp"
15
16
#if (__has_cpp_attribute(no_unique_address))
17
#define GLZ_NO_UNIQUE_ADDRESS [[no_unique_address]]
18
#elif (__has_cpp_attribute(msvc::no_unique_address)) || ((defined _MSC_VER) && (!defined __clang__))
19
// Note __has_cpp_attribute(msvc::no_unique_address) itself doesn't work as
20
// of 19.30.30709, even though the attribute itself is supported. See
21
// https://github.com/llvm/llvm-project/issues/49358#issuecomment-981041089
22
#define GLZ_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
23
#else
24
// no_unique_address is not available.
25
#define GLZ_NO_UNIQUE_ADDRESS
26
#endif
27
28
namespace glz
29
{
30
   // tuplet concepts and traits
31
   namespace tuplet
32
   {
33
      template <class T>
34
      using identity_t = T;
35
36
      template <class T>
37
      using type_t = typename T::type;
38
39
      template <size_t I>
40
      using tag = std::integral_constant<size_t, I>;
41
42
      template <size_t N>
43
      using tag_range = std::make_index_sequence<N>;
44
45
      template <class T, class U>
46
      concept other_than = !std::is_same_v<std::decay_t<T>, U>;
47
48
      template <class Tup>
49
      using base_list_t = typename std::decay_t<Tup>::base_list;
50
51
      template <class Tuple>
52
      concept base_list_tuple = requires() { typename std::decay_t<Tuple>::base_list; };
53
54
      template <class T>
55
      concept stateless = std::is_empty_v<std::decay_t<T>>;
56
57
      template <class T>
58
      concept indexable = stateless<T> || requires(T t) { t[tag<0>()]; };
59
60
      template <class U, class T>
61
      concept assignable_to = requires(U u, T t) { t = u; };
62
63
      template <class T>
64
      concept ordered = requires(const T& t) {
65
         { t <=> t };
66
      };
67
      template <class T>
68
      concept equality_comparable = requires(const T& t) {
69
         { t == t } -> std::same_as<bool>;
70
      };
71
   } // namespace tuplet
72
73
   template <class... T>
74
   struct tuple;
75
76
   // tuplet::type_list implementation
77
   // tuplet::type_map implementation
78
   // tuplet::tuple_elem implementation
79
   // tuplet::deduce_elems
80
   namespace tuplet
81
   {
82
      template <class... T>
83
      struct type_list
84
      {};
85
86
      template <class... Ls, class... Rs>
87
      constexpr auto operator+(type_list<Ls...>, type_list<Rs...>)
88
      {
89
         return type_list<Ls..., Rs...>{};
90
      }
91
92
      template <class... Bases>
93
      struct type_map : Bases...
94
      {
95
         using base_list = type_list<Bases...>;
96
         using Bases::operator[]...;
97
         using Bases::decl_elem...;
98
         auto operator<=>(const type_map&) const = default;
99
         bool operator==(const type_map&) const = default;
100
      };
101
102
      template <size_t I, class T>
103
      struct tuple_elem
104
      {
105
         // Like declval, but with the element
106
         static T decl_elem(tag<I>);
107
         using type = T;
108
109
         GLZ_NO_UNIQUE_ADDRESS T value;
110
111
0
         constexpr decltype(auto) operator[](tag<I>) & { return (value); }
Unexecuted instantiation: glz::tuplet::tuple_elem<0ul, int const&>::operator[](std::__1::integral_constant<unsigned long, 0ul>) &
Unexecuted instantiation: glz::tuplet::tuple_elem<0ul, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>::operator[](std::__1::integral_constant<unsigned long, 0ul>) &
Unexecuted instantiation: glz::tuplet::tuple_elem<0ul, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>::operator[](std::__1::integral_constant<unsigned long, 0ul>) &
112
         constexpr decltype(auto) operator[](tag<I>) const& { return (value); }
113
16.0k
         constexpr decltype(auto) operator[](tag<I>) && { return (std::move(*this).value); }
glz::tuplet::tuple_elem<0ul, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>::operator[](std::__1::integral_constant<unsigned long, 0ul>) &&
Line
Count
Source
113
2.41k
         constexpr decltype(auto) operator[](tag<I>) && { return (std::move(*this).value); }
glz::tuplet::tuple_elem<1ul, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>::operator[](std::__1::integral_constant<unsigned long, 1ul>) &&
Line
Count
Source
113
4.02k
         constexpr decltype(auto) operator[](tag<I>) && { return (std::move(*this).value); }
glz::tuplet::tuple_elem<2ul, unsigned short&>::operator[](std::__1::integral_constant<unsigned long, 2ul>) &&
Line
Count
Source
113
9.59k
         constexpr decltype(auto) operator[](tag<I>) && { return (std::move(*this).value); }
Unexecuted instantiation: glz::tuplet::tuple_elem<0ul, int const&>::operator[](std::__1::integral_constant<unsigned long, 0ul>) &&
Unexecuted instantiation: glz::tuplet::tuple_elem<0ul, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>::operator[](std::__1::integral_constant<unsigned long, 0ul>) &&
Unexecuted instantiation: glz::tuplet::tuple_elem<1ul, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&>::operator[](std::__1::integral_constant<unsigned long, 1ul>) &&
Unexecuted instantiation: glz::tuplet::tuple_elem<2ul, unsigned short const&>::operator[](std::__1::integral_constant<unsigned long, 2ul>) &&
114
         auto operator<=>(const tuple_elem&) const = default;
115
         bool operator==(const tuple_elem&) const = default;
116
         // Implements comparison for tuples containing reference types
117
         constexpr auto operator<=>(const tuple_elem& other) const noexcept(noexcept(value <=> other.value))
118
            requires(std::is_reference_v<T> && ordered<T>)
119
0
         {
120
0
            return value <=> other.value;
121
0
         }
Unexecuted instantiation: _ZNK3glz6tuplet10tuple_elemILm0ERKiEssERKS4_Qaasr3stdE14is_reference_vIT0_E7orderedIS7_E
Unexecuted instantiation: _ZNK3glz6tuplet10tuple_elemILm0ERNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEssERKSA_Qaasr3stdE14is_reference_vIT0_E7orderedISD_E
Unexecuted instantiation: _ZNK3glz6tuplet10tuple_elemILm1ERNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEssERKSA_Qaasr3stdE14is_reference_vIT0_E7orderedISD_E
Unexecuted instantiation: _ZNK3glz6tuplet10tuple_elemILm2ERtEssERKS3_Qaasr3stdE14is_reference_vIT0_E7orderedIS6_E
Unexecuted instantiation: _ZNK3glz6tuplet10tuple_elemILm0ERKNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEssERKSB_Qaasr3stdE14is_reference_vIT0_E7orderedISE_E
Unexecuted instantiation: _ZNK3glz6tuplet10tuple_elemILm1ERKNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEssERKSB_Qaasr3stdE14is_reference_vIT0_E7orderedISE_E
Unexecuted instantiation: _ZNK3glz6tuplet10tuple_elemILm2ERKtEssERKS4_Qaasr3stdE14is_reference_vIT0_E7orderedIS7_E
122
         constexpr bool operator==(const tuple_elem& other) const noexcept(noexcept(value == other.value))
123
            requires(std::is_reference_v<T> && equality_comparable<T>)
124
         {
125
            return value == other.value;
126
         }
127
      };
128
   } // namespace tuplet
129
130
   template <class T>
131
   using unwrap_ref_decay_t = typename std::unwrap_ref_decay<T>::type;
132
133
   // tuplet::get_tuple_base implementation
134
   // tuplet::apply_impl
135
   namespace tuplet
136
   {
137
      template <class A, class... T>
138
      struct get_tuple_base;
139
140
      template <size_t... I, class... T>
141
      struct get_tuple_base<std::index_sequence<I...>, T...>
142
      {
143
         using type = type_map<tuple_elem<I, T>...>;
144
      };
145
146
      template <class F, class T, class... Bases>
147
      constexpr decltype(auto) apply_impl(F&& f, T&& t, type_list<Bases...>)
148
      {
149
         return static_cast<F&&>(f)(static_cast<T&&>(t).identity_t<Bases>::value...);
150
      }
151
152
      template <class First, class>
153
      using first_t = First;
154
155
      template <class T, class... Q>
156
      constexpr auto repeat_type(type_list<Q...>)
157
      {
158
         return type_list<first_t<T, Q>...>{};
159
      }
160
      template <class... Outer>
161
      constexpr auto get_outer_bases(type_list<Outer...>)
162
      {
163
         return (repeat_type<Outer>(base_list_t<type_t<Outer>>{}) + ...);
164
      }
165
      template <class... Outer>
166
      constexpr auto get_inner_bases(type_list<Outer...>)
167
      {
168
         return (base_list_t<type_t<Outer>>{} + ...);
169
      }
170
171
      // This takes a forwarding tuple as a parameter. The forwarding tuple only
172
      // contains references, so it should just be taken by value.
173
      template <class T, class... Outer, class... Inner>
174
      constexpr auto cat_impl([[maybe_unused]] T tup, type_list<Outer...>, type_list<Inner...>)
175
         -> tuple<type_t<Inner>...>
176
      {
177
         return {{{static_cast<type_t<Outer>&&>(tup.identity_t<Outer>::value).identity_t<Inner>::value}...}};
178
      }
179
180
      template <class... T>
181
      using tuple_base_t = typename get_tuple_base<tag_range<sizeof...(T)>, T...>::type;
182
   }
183
184
   template <class... T>
185
   struct tuple : tuplet::tuple_base_t<T...>
186
   {
187
      static constexpr auto glaze_reflect = false;
188
      static constexpr size_t N = sizeof...(T);
189
      using super = tuplet::tuple_base_t<T...>;
190
      using super::operator[];
191
      using base_list = typename super::base_list;
192
      using super::decl_elem;
193
194
      template <tuplet::other_than<tuple> U> // Preserves default assignments
195
      constexpr auto& operator=(U&& tup)
196
      {
197
         using tuple2 = std::decay_t<U>;
198
         if constexpr (tuplet::base_list_tuple<tuple2>) {
199
            eq_impl(static_cast<U&&>(tup), base_list(), typename tuple2::base_list());
200
         }
201
         else {
202
            eq_impl(static_cast<U&&>(tup), tuplet::tag_range<N>());
203
         }
204
         return *this;
205
      }
206
207
      // TODO: currently segfaults clang
208
      /*template <assignable_to<T>... U>
209
      constexpr auto& assign(U&&... values) {
210
         assign_impl(base_list(), static_cast<U&&>(values)...);
211
         return *this;
212
      }*/
213
214
      auto operator<=>(const tuple&) const = default;
215
      bool operator==(const tuple&) const = default;
216
217
      // Applies a function to every element of the tuple. The order is the
218
      // declaration order, so first the function will be applied to element 0,
219
      // then element 1, then element 2, and so on, where element N is identified
220
      // by get<N>
221
      template <class F>
222
      constexpr void for_each(F&& func) &
223
      {
224
         for_each_impl(base_list(), static_cast<F&&>(func));
225
      }
226
      template <class F>
227
      constexpr void for_each(F&& func) const&
228
      {
229
         for_each_impl(base_list(), static_cast<F&&>(func));
230
      }
231
      template <class F>
232
      constexpr void for_each(F&& func) &&
233
      {
234
         static_cast<tuple&&>(*this).for_each_impl(base_list(), static_cast<F&&>(func));
235
      }
236
237
      // Applies a function to each element successively, until one returns a
238
      // truthy value. Returns true if any application returned a truthy value,
239
      // and false otherwise
240
      template <class F>
241
      constexpr bool any(F&& func) &
242
      {
243
         return any_impl(base_list(), static_cast<F&&>(func));
244
      }
245
      template <class F>
246
      constexpr bool any(F&& func) const&
247
      {
248
         return any_impl(base_list(), static_cast<F&&>(func));
249
      }
250
      template <class F>
251
      constexpr bool any(F&& func) &&
252
      {
253
         return static_cast<tuple&&>(*this).any_impl(base_list(), static_cast<F&&>(func));
254
      }
255
256
      // Applies a function to each element successively, until one returns a
257
      // falsy value. Returns true if every application returned a truthy value,
258
      // and false otherwise
259
      template <class F>
260
      constexpr bool all(F&& func) &
261
      {
262
         return all_impl(base_list(), static_cast<F&&>(func));
263
      }
264
      template <class F>
265
      constexpr bool all(F&& func) const&
266
      {
267
         return all_impl(base_list(), static_cast<F&&>(func));
268
      }
269
      template <class F>
270
      constexpr bool all(F&& func) &&
271
      {
272
         return static_cast<tuple&&>(*this).all_impl(base_list(), static_cast<F&&>(func));
273
      }
274
275
     private:
276
      template <class U, class... B1, class... B2>
277
      constexpr void eq_impl(U&& u, tuplet::type_list<B1...>, tuplet::type_list<B2...>)
278
      {
279
         // See:
280
         // https://developercommunity.visualstudio.com/t/fold-expressions-unreliable-in-171-with-c20/1676476
281
         (void(B1::value = static_cast<U&&>(u).B2::value), ...);
282
      }
283
      template <class U, size_t... I>
284
      constexpr void eq_impl(U&& u, std::index_sequence<I...>)
285
      {
286
         (void(tuplet::tuple_elem<I, T>::value = get<I>(static_cast<U&&>(u))), ...);
287
      }
288
      template <class... U, class... B>
289
      constexpr void assign_impl(tuplet::type_list<B...>, U&&... u)
290
      {
291
         (void(B::value = static_cast<U&&>(u)), ...);
292
      }
293
294
      template <class F, class... B>
295
      constexpr void for_each_impl(tuplet::type_list<B...>, F&& func) &
296
      {
297
         (void(func(B::value)), ...);
298
      }
299
      template <class F, class... B>
300
      constexpr void for_each_impl(tuplet::type_list<B...>, F&& func) const&
301
      {
302
         (void(func(B::value)), ...);
303
      }
304
      template <class F, class... B>
305
      constexpr void for_each_impl(tuplet::type_list<B...>, F&& func) &&
306
      {
307
         (void(func(static_cast<T&&>(B::value))), ...);
308
      }
309
310
      template <class F, class... B>
311
      constexpr bool any_impl(tuplet::type_list<B...>, F&& func) &
312
      {
313
         return (bool(func(B::value)) || ...);
314
      }
315
      template <class F, class... B>
316
      constexpr bool any_impl(tuplet::type_list<B...>, F&& func) const&
317
      {
318
         return (bool(func(B::value)) || ...);
319
      }
320
      template <class F, class... B>
321
      constexpr bool any_impl(tuplet::type_list<B...>, F&& func) &&
322
      {
323
         return (bool(func(static_cast<T&&>(B::value))) || ...);
324
      }
325
326
      template <class F, class... B>
327
      constexpr bool all_impl(tuplet::type_list<B...>, F&& func) &
328
      {
329
         return (bool(func(B::value)) && ...);
330
      }
331
      template <class F, class... B>
332
      constexpr bool all_impl(tuplet::type_list<B...>, F&& func) const&
333
      {
334
         return (bool(func(B::value)) && ...);
335
      }
336
      template <class F, class... B>
337
      constexpr bool all_impl(tuplet::type_list<B...>, F&& func) &&
338
      {
339
         return (bool(func(static_cast<T&&>(B::value))) && ...);
340
      }
341
   };
342
   template <>
343
   struct tuple<> : tuplet::tuple_base_t<>
344
   {
345
      constexpr static size_t N = 0;
346
      using super = tuplet::tuple_base_t<>;
347
      using base_list = tuplet::type_list<>;
348
349
      template <tuplet::other_than<tuple> U> // Preserves default assignments
350
         requires tuplet::stateless<U> // Check that U is similarly stateless
351
      constexpr auto& operator=(U&&) noexcept
352
      {
353
         return *this;
354
      }
355
356
0
      constexpr auto& assign() noexcept { return *this; }
357
      auto operator<=>(const tuple&) const = default;
358
      bool operator==(const tuple&) const = default;
359
360
      // Applies a function to every element of the tuple. The order is the
361
      // declaration order, so first the function will be applied to element 0,
362
      // then element 1, then element 2, and so on, where element N is identified
363
      // by get<N>
364
      //
365
      // (Does nothing when invoked on empty tuple)
366
      template <class F>
367
      constexpr void for_each(F&&) const noexcept
368
      {}
369
370
      // Applies a function to each element successively, until one returns a
371
      // truthy value. Returns true if any application returned a truthy value,
372
      // and false otherwise
373
      //
374
      // (Returns false for empty tuple)
375
      template <class F>
376
      constexpr bool any(F&&) const noexcept
377
      {
378
         return false;
379
      }
380
381
      // Applies a function to each element successively, until one returns a
382
      // falsy value. Returns true if every application returned a truthy value,
383
      // and false otherwise
384
      //
385
      // (Returns true for empty tuple)
386
      template <class F>
387
      constexpr bool all(F&&) const noexcept
388
      {
389
         return true;
390
      }
391
   };
392
   template <class... Ts>
393
   tuple(Ts...) -> tuple<unwrap_ref_decay_t<Ts>...>;
394
395
   // tuplet::convert implementation
396
   namespace tuplet
397
   {
398
      // Converts from one tuple type to any other tuple or U
399
      template <class Tuple>
400
      struct convert final
401
      {
402
         using base_list = typename std::decay_t<Tuple>::base_list;
403
         Tuple tuple;
404
         template <class U>
405
         constexpr operator U() &&
406
         {
407
            return convert_impl<U>(base_list{});
408
         }
409
410
        private:
411
         template <class U, class... Bases>
412
         constexpr U convert_impl(type_list<Bases...>)
413
         {
414
            return U{static_cast<Tuple&&>(tuple).identity_t<Bases>::value...};
415
         }
416
      };
417
      template <class Tuple>
418
      convert(Tuple&) -> convert<Tuple&>;
419
      template <class Tuple>
420
      convert(const Tuple&) -> convert<const Tuple&>;
421
      template <class Tuple>
422
      convert(Tuple&&) -> convert<Tuple>;
423
   } // namespace tuplet
424
425
   // glz::get implementation
426
   // glz::tie implementation
427
   // glz::apply implementation
428
   template <size_t I, tuplet::indexable Tup>
429
   GLZ_ALWAYS_INLINE constexpr decltype(auto) get(Tup&& tup) noexcept
430
16.0k
   {
431
16.0k
      return static_cast<Tup&&>(tup)[tuplet::tag<I>()];
432
16.0k
   }
_ZN3glz3getILm0ETkNS_6tuplet9indexableENS_5tupleIJRNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESA_RtEEEEEDcOT0_
Line
Count
Source
430
2.41k
   {
431
2.41k
      return static_cast<Tup&&>(tup)[tuplet::tag<I>()];
432
2.41k
   }
_ZN3glz3getILm1ETkNS_6tuplet9indexableENS_5tupleIJRNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESA_RtEEEEEDcOT0_
Line
Count
Source
430
4.02k
   {
431
4.02k
      return static_cast<Tup&&>(tup)[tuplet::tag<I>()];
432
4.02k
   }
_ZN3glz3getILm2ETkNS_6tuplet9indexableENS_5tupleIJRNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESA_RtEEEEEDcOT0_
Line
Count
Source
430
9.59k
   {
431
9.59k
      return static_cast<Tup&&>(tup)[tuplet::tag<I>()];
432
9.59k
   }
Unexecuted instantiation: _ZN3glz3getILm0ETkNS_6tuplet9indexableENS_5tupleIJRKiEEEEEDcOT0_
Unexecuted instantiation: _ZN3glz3getILm0ETkNS_6tuplet9indexableENS_5tupleIJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESB_RKtEEEEEDcOT0_
Unexecuted instantiation: _ZN3glz3getILm1ETkNS_6tuplet9indexableENS_5tupleIJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESB_RKtEEEEEDcOT0_
Unexecuted instantiation: _ZN3glz3getILm2ETkNS_6tuplet9indexableENS_5tupleIJRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEESB_RKtEEEEEDcOT0_
433
434
#if defined(__clang__)
435
#pragma clang diagnostic push
436
#pragma clang diagnostic ignored "-Wmissing-braces"
437
#endif
438
   template <class... T>
439
   constexpr tuple<T&...> tie(T&... t)
440
16.0k
   {
441
16.0k
      return {t...};
442
16.0k
   }
glz::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, unsigned short&> glz::tie<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, unsigned short>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, unsigned short&)
Line
Count
Source
440
16.0k
   {
441
16.0k
      return {t...};
442
16.0k
   }
Unexecuted instantiation: glz::tuple<int const&> glz::tie<int const>(int const&)
Unexecuted instantiation: glz::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned short const&> glz::tie<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, unsigned short const>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned short const&)
443
#if defined(__clang__)
444
#pragma clang diagnostic pop
445
#endif
446
447
   template <class F, tuplet::base_list_tuple Tup>
448
   constexpr decltype(auto) apply(F&& func, Tup&& tup)
449
   {
450
      return tuplet::apply_impl(static_cast<F&&>(func), static_cast<Tup&&>(tup),
451
                                typename std::decay_t<Tup>::base_list());
452
   }
453
454
   // tuplet::tuple_cat implementation
455
   // tuplet::make_tuple implementation
456
   // tuplet::forward_as_tuple implementation
457
   namespace tuplet
458
   {
459
      template <base_list_tuple... T>
460
      constexpr auto tuple_cat(T&&... ts)
461
      {
462
         if constexpr (sizeof...(T) == 0) {
463
            return tuple<>();
464
         }
465
         else {
466
            /**
467
             * It appears that Clang produces better assembly when
468
             * TUPLET_CAT_BY_FORWARDING_TUPLE == 0, while GCC produces better assembly when
469
             * TUPLET_CAT_BY_FORWARDING_TUPLE == 1. MSVC always produces terrible assembly
470
             * in either case. This will set TUPLET_CAT_BY_FORWARDING_TUPLE to the correct
471
             * value (0 for clang, 1 for everyone else)
472
             *
473
             * See: https://github.com/codeinred/tuplet/discussions/14
474
             */
475
#if !defined(TUPLET_CAT_BY_FORWARDING_TUPLE)
476
#if defined(__clang__)
477
#define TUPLET_CAT_BY_FORWARDING_TUPLE 0
478
#else
479
#define TUPLET_CAT_BY_FORWARDING_TUPLE 1
480
#endif
481
#endif
482
#if TUPLET_CAT_BY_FORWARDING_TUPLE
483
            using big_tuple = tuple<T&&...>;
484
#else
485
            using big_tuple = tuple<std::decay_t<T>...>;
486
#endif
487
            using outer_bases = base_list_t<big_tuple>;
488
            constexpr auto outer = get_outer_bases(outer_bases{});
489
            constexpr auto inner = get_inner_bases(outer_bases{});
490
            return cat_impl(big_tuple{{{std::forward<T>(ts)}...}}, outer, inner);
491
         }
492
      }
493
494
      template <typename... Ts>
495
      constexpr auto make_tuple(Ts&&... args)
496
      {
497
         return tuple<unwrap_ref_decay_t<Ts>...>{{{std::forward<Ts>(args)}...}};
498
      }
499
500
      template <typename... T>
501
      constexpr auto forward_as_tuple(T&&... a) noexcept
502
      {
503
         return tuple<T&&...>{static_cast<T&&>(a)...};
504
      }
505
   } // namespace tuplet
506
} // namespace glz
507
508
#include <array>
509
#include <tuple>
510
#include <variant>
511
512
namespace glz
513
{
514
   template <class... T>
515
   struct tuple_size;
516
517
   template <class T>
518
   constexpr size_t tuple_size_v = tuple_size<std::remove_const_t<T>>::value;
519
520
   template <class T, size_t N>
521
   struct tuple_size<std::array<T, N>>
522
   {
523
      static constexpr size_t value = N;
524
   };
525
526
   template <class... Types>
527
   struct tuple_size<std::tuple<Types...>>
528
   {
529
      static constexpr size_t value = sizeof...(Types);
530
   };
531
532
   template <size_t I, class... T>
533
   struct tuple_element;
534
535
   template <size_t I, class Tuple>
536
   using tuple_element_t = typename tuple_element<I, Tuple>::type;
537
538
   template <size_t I, class... T>
539
   struct tuple_element<I, std::tuple<T...>>
540
   {
541
      using type = typename std::tuple_element<I, std::tuple<T...>>::type;
542
   };
543
544
   template <std::size_t I, typename T1, typename T2>
545
   struct tuple_element<I, std::pair<T1, T2>>
546
   {
547
      using type = typename std::conditional<I == 0, T1, T2>::type;
548
   };
549
550
   template <class... T>
551
   struct tuple_size<glz::tuple<T...>> : std::integral_constant<size_t, sizeof...(T)>
552
   {};
553
554
   template <size_t I, class... T>
555
   struct tuple_element<I, glz::tuple<T...>>
556
   {
557
      using type = decltype(glz::tuple<T...>::decl_elem(glz::tuplet::tag<I>()));
558
   };
559
}