FastLED 3.10.8
Loading...
Searching...
No Matches
type_traits.h
Go to the documentation of this file.
1#pragma once
2
3/*
4Provides eanble_if and is_derived for compilers before C++14.
5*/
6
8#include "fl/stl/move.h"
9#include "fl/stl/int.h"
10#include "fl/stl/noexcept.h"
12
13namespace fl { // mandatory namespace to prevent name collision with
14 // std::enable_if.
15
16// Define integral_constant as base for true_type and false_type
17// Using enum instead of static constexpr to avoid ODR-use issues in C++11
18template <typename T, T v>
20 enum : T { value = v };
21 using value_type = T;
23 constexpr operator value_type() const FL_NO_EXCEPT { return value; }
24 constexpr value_type operator()() const FL_NO_EXCEPT { return value; }
25};
26
27// Define true_type and false_type
30
31// Define bool_constant for tag dispatch (C++17 compatibility)
32template <bool B>
34
35// Define identity trait - prevents template argument deduction
36template <typename T> struct identity {
37 using type = T;
38};
39
40// Define add_rvalue_reference trait (remove_reference is already defined in move.h)
41template <typename T> struct add_rvalue_reference {
42 using type = T&&;
43};
44
45template <typename T> struct add_rvalue_reference<T&> {
46 using type = T&;
47};
48
49// Define declval for use in SFINAE expressions
50template <typename T>
52
53// Define enable_if for SFINAE
54template <bool Condition, typename T = void> struct enable_if {};
55
56// Specialization for true condition
57template <typename T> struct enable_if<true, T> {
58 using type = T;
59};
60
61// if enable_if<Condition, T> is true, then there will be a member type
62// called type. Otherwise it will not exist. This is (ab)used to enable
63// constructors and other functions based on template parameters. If there
64// is no member type, then the compiler will not fail to bind to the target
65// function or operation.
66template <bool Condition, typename T = void>
68
69// Define is_base_of to check inheritance relationship
70template <typename Base, typename Derived> struct is_base_of {
71 private:
72 typedef u8 yes;
73 typedef u16 no;
74 static yes test(Base *) FL_NO_EXCEPT; // Matches if Derived is convertible to Base*
75 static no test(...) FL_NO_EXCEPT; // Fallback if not convertible
76 enum {
77 kSizeDerived = sizeof(test(static_cast<Derived *>(nullptr))),
78 };
79
80 public:
81 // Use enum instead of static constexpr to avoid ODR-use issues in C++11
82 enum : bool { value = (kSizeDerived == sizeof(yes)) };
83};
84
85// Define is_base_of_v for compatibility with pre-C++14
86// Replaced variable template with a constant static member
87template <typename Base, typename Derived> struct is_base_of_v_helper {
88 static constexpr bool value = is_base_of<Base, Derived>::value;
89};
90
91// Define is_same trait
92template <typename T, typename U> struct is_same {
93 enum : bool { value = false };
94};
95
96// Specialization for when T and U are the same type
97template <typename T> struct is_same<T, T> {
98 enum : bool { value = true };
99};
100
101// Define is_same_v for compatibility with variable templates
102template <typename T, typename U> struct is_same_v_helper {
103 static constexpr bool value = is_same<T, U>::value;
104};
105
106// Define conditional trait
107template <bool B, typename T, typename F> struct conditional {
108 using type = T;
109};
110
111template <typename T, typename F> struct conditional<false, T, F> {
112 using type = F;
113};
114
115template <bool B, typename T, typename F>
117
118// Define is_array trait
119template <typename T> struct is_array {
120 enum : bool { value = false };
121};
122
123template <typename T> struct is_array<T[]> {
124 enum : bool { value = true };
125};
126
127template <typename T, fl::size N> struct is_array<T[N]> {
128 enum : bool { value = true };
129};
130
131// Define remove_extent trait
132template <typename T> struct remove_extent {
133 using type = T;
134};
135
136template <typename T> struct remove_extent<T[]> {
137 using type = T;
138};
139
140template <typename T, fl::size N> struct remove_extent<T[N]> {
141 using type = T;
142};
143
144// Define is_function trait
145template <typename T> struct is_function {
146 enum : bool { value = false };
147};
148
149template <typename Ret, typename... Args> struct is_function<Ret(Args...)> {
150 enum : bool { value = true };
151};
152
153template <typename Ret, typename... Args>
154struct is_function<Ret(Args...) const> {
155 enum : bool { value = true };
156};
157
158template <typename Ret, typename... Args>
159struct is_function<Ret(Args...) volatile> {
160 enum : bool { value = true };
161};
162
163template <typename Ret, typename... Args>
164struct is_function<Ret(Args...) const volatile> {
165 enum : bool { value = true };
166};
167
168// Define add_pointer trait
169template <typename T> struct add_pointer {
170 using type = T *;
171};
172
173template <typename T> struct add_pointer<T &> {
174 using type = T *;
175};
176
177template <typename T> struct add_pointer<T &&> {
178 using type = T *;
179};
180
181template <typename T> using add_pointer_t = typename add_pointer<T>::type;
182
183// Define remove_const trait
184template <typename T> struct remove_const {
185 using type = T;
186};
187
188template <typename T> struct remove_const<const T> {
189 using type = T;
190};
191
192// Define is_const trait
193template <typename T> struct is_const {
194 enum : bool { value = false };
195};
196
197template <typename T> struct is_const<const T> {
198 enum : bool { value = true };
199};
200
201// Define is_lvalue_reference trait
202template <typename T> struct is_lvalue_reference {
203 enum : bool { value = false };
204};
205
206template <typename T> struct is_lvalue_reference<T &> {
207 enum : bool { value = true };
208};
209
210// Define is_rvalue_reference trait
211template <typename T> struct is_rvalue_reference {
212 enum : bool { value = false };
213};
214
215template <typename T> struct is_rvalue_reference<T &&> {
216 enum : bool { value = true };
217};
218
219// Define is_reference trait (true for both lvalue and rvalue references)
220template <typename T> struct is_reference {
222};
223
224// Define is_void trait
225template <typename T> struct is_void {
226 enum : bool { value = false };
227};
228
229template <> struct is_void<void> {
230 enum : bool { value = true };
231};
232
233namespace adl_barrier { // see fl/stl/adl_barrier.h
234// Implementation of forward
235template <typename T>
237 return static_cast<T &&>(t);
238}
239
240// Overload for rvalue references
241template <typename T>
244 "Cannot forward an rvalue as an lvalue");
245 return static_cast<T &&>(t);
246}
247} // namespace adl_barrier
248
249
250// Define remove_cv trait
251template <typename T> struct remove_cv {
252 using type = T;
253};
254
255template <typename T> struct remove_cv<const T> {
256 using type = T;
257};
258
259template <typename T> struct remove_cv<volatile T> {
260 using type = T;
261};
262
263template <typename T> struct remove_cv<const volatile T> {
264 using type = T;
265};
266
267template <typename T> using remove_cv_t = typename remove_cv<T>::type;
268
269// Define is_pointer trait
270template <typename T> struct is_pointer {
271 enum : bool { value = false };
272};
273
274template <typename T> struct is_pointer<T*> {
275 enum : bool { value = true };
276};
277
278template <typename T> struct is_pointer<T* const> {
279 enum : bool { value = true };
280};
281
282template <typename T> struct is_pointer<T* volatile> {
283 enum : bool { value = true };
284};
285
286template <typename T> struct is_pointer<T* const volatile> {
287 enum : bool { value = true };
288};
289
290// Define remove_pointer trait
291template <typename T> struct remove_pointer {
292 using type = T;
293};
294
295template <typename T> struct remove_pointer<T*> {
296 using type = T;
297};
298
299template <typename T> struct remove_pointer<T* const> {
300 using type = T;
301};
302
303template <typename T> struct remove_pointer<T* volatile> {
304 using type = T;
305};
306
307template <typename T> struct remove_pointer<T* const volatile> {
308 using type = T;
309};
310
311template <typename T> using remove_pointer_t = typename remove_pointer<T>::type;
312
313// Define decay trait
314template <typename T> struct decay {
315 private:
316 using U = typename remove_reference<T>::type;
317
318 public:
319 using type = typename conditional<
322 typename add_pointer<U>::type,
324};
325
326template <typename T> using decay_t = typename decay<T>::type;
327
328// Define is_pod trait (basic implementation)
329// Enums are POD types, so we use compiler intrinsics to detect them
330template <typename T> struct is_pod {
331#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
332 enum : bool { value = __is_enum(T) }; // Enums are POD
333#else
334 enum : bool { value = false }; // Default to false for safety
335#endif
336};
337
338// Specializations for fundamental types
339template <> struct is_pod<bool> {
340 enum : bool { value = true };
341};
342template <> struct is_pod<char> {
343 enum : bool { value = true };
344};
345template <> struct is_pod<signed char> {
346 enum : bool { value = true };
347};
348template <> struct is_pod<unsigned char> {
349 enum : bool { value = true };
350};
351template <> struct is_pod<short> {
352 enum : bool { value = true };
353};
354template <> struct is_pod<unsigned short> {
355 enum : bool { value = true };
356};
357template <> struct is_pod<int> {
358 enum : bool { value = true };
359};
360template <> struct is_pod<unsigned int> {
361 enum : bool { value = true };
362};
363template <> struct is_pod<long> {
364 enum : bool { value = true };
365};
366template <> struct is_pod<unsigned long> {
367 enum : bool { value = true };
368};
369template <> struct is_pod<long long> {
370 enum : bool { value = true };
371};
372template <> struct is_pod<unsigned long long> {
373 enum : bool { value = true };
374};
375template <> struct is_pod<float> {
376 enum : bool { value = true };
377};
378template <> struct is_pod<double> {
379 enum : bool { value = true };
380};
381template <> struct is_pod<long double> {
382 enum : bool { value = true };
383};
384
385// Helper struct for is_pod_v (similar to other _v helpers)
386template <typename T> struct is_pod_v_helper {
387 static constexpr bool value = is_pod<T>::value;
388};
389
390//----------------------------------------------------------------------------
391// trait to detect pointer‑to‑member‑function
392// must come before Function so SFINAE sees it
393//----------------------------------------------------------------------------
394template <typename T> struct is_member_function_pointer; // IWYU pragma: keep
395template <typename C, typename Ret, typename... A> // IWYU pragma: keep
396struct is_member_function_pointer<Ret (C::*)(A...)>; // IWYU pragma: keep
397template <typename C, typename Ret, typename... A> // IWYU pragma: keep
398struct is_member_function_pointer<Ret (C::*)(A...) const>; // IWYU pragma: keep
399
400template <typename T> struct is_member_function_pointer {
401 enum : bool { value = false };
402};
403
404template <typename C, typename Ret, typename... A>
405struct is_member_function_pointer<Ret (C::*)(A...)> {
406 enum : bool { value = true };
407};
408
409template <typename C, typename Ret, typename... A>
410struct is_member_function_pointer<Ret (C::*)(A...) const> {
411 enum : bool { value = true };
412};
413
414//-------------------------------------------------------------------------------
415// is_integral trait (built-in integer types only)
416//-------------------------------------------------------------------------------
417template <typename T> struct is_integral {
418 enum : bool { value = false };
419};
420template <> struct is_integral<bool> {
421 enum : bool { value = true };
422};
423template <> struct is_integral<char> {
424 enum : bool { value = true };
425};
426template <> struct is_integral<signed char> {
427 enum : bool { value = true };
428};
429template <> struct is_integral<unsigned char> {
430 enum : bool { value = true };
431};
432template <> struct is_integral<short> {
433 enum : bool { value = true };
434};
435template <> struct is_integral<unsigned short> {
436 enum : bool { value = true };
437};
438template <> struct is_integral<int> {
439 enum : bool { value = true };
440};
441template <> struct is_integral<unsigned int> {
442 enum : bool { value = true };
443};
444template <> struct is_integral<long> {
445 enum : bool { value = true };
446};
447template <> struct is_integral<unsigned long> {
448 enum : bool { value = true };
449};
450template <> struct is_integral<long long> {
451 enum : bool { value = true };
452};
453template <> struct is_integral<unsigned long long> {
454 enum : bool { value = true };
455};
456
457template <typename T> struct is_integral<const T> {
458 static constexpr bool value = is_integral<T>::value;
459};
460
461template <typename T> struct is_integral<volatile T> {
462 static constexpr bool value = is_integral<T>::value;
463};
464
465template <typename T> struct is_integral<T &> {
466 static constexpr bool value = is_integral<T>::value;
467};
468
469//-------------------------------------------------------------------------------
470// is_char_type trait - detects char, signed char, unsigned char
471//-------------------------------------------------------------------------------
472template <typename T> struct is_char_type {
473 enum : bool { value = false };
474};
475template <> struct is_char_type<char> {
476 enum : bool { value = true };
477};
478template <> struct is_char_type<signed char> {
479 enum : bool { value = true };
480};
481template <> struct is_char_type<unsigned char> {
482 enum : bool { value = true };
483};
484
485template <typename T> struct is_char_type<const T> {
486 static constexpr bool value = is_char_type<T>::value;
487};
488
489template <typename T> struct is_char_type<volatile T> {
490 static constexpr bool value = is_char_type<T>::value;
491};
492
493template <typename T> struct is_char_type<T &> {
494 static constexpr bool value = is_char_type<T>::value;
495};
496
497//-------------------------------------------------------------------------------
498// is_multi_byte_integer trait - integral types excluding char types
499//-------------------------------------------------------------------------------
500template <typename T> struct is_multi_byte_integer {
502};
503
504//-------------------------------------------------------------------------------
505// is_floating_point trait
506//-------------------------------------------------------------------------------
507template <typename T> struct is_floating_point {
508 enum : bool { value = false };
509};
510template <> struct is_floating_point<float> {
511 enum : bool { value = true };
512};
513template <> struct is_floating_point<double> {
514 enum : bool { value = true };
515};
516template <> struct is_floating_point<long double> {
517 enum : bool { value = true };
518};
519
520template <typename T> struct is_floating_point<const T> {
521 static constexpr bool value = is_floating_point<T>::value;
522};
523
524template <typename T> struct is_floating_point<volatile T> {
525 static constexpr bool value = is_floating_point<T>::value;
526};
527
528template <typename T> struct is_floating_point<T &> {
529 static constexpr bool value = is_floating_point<T>::value;
530};
531
532//-------------------------------------------------------------------------------
533// is_arithmetic trait - integral or floating-point types
534//-------------------------------------------------------------------------------
535template <typename T> struct is_arithmetic {
537};
538
539template <typename T> struct is_arithmetic<const T> {
540 static constexpr bool value = is_arithmetic<T>::value;
541};
542
543template <typename T> struct is_arithmetic<volatile T> {
544 static constexpr bool value = is_arithmetic<T>::value;
545};
546
547template <typename T> struct is_arithmetic<T &> {
548 static constexpr bool value = is_arithmetic<T>::value;
549};
550
551//-------------------------------------------------------------------------------
552// is_signed trait
553//-------------------------------------------------------------------------------
554template <typename T> struct is_signed {
555 enum : bool { value = false };
556};
557template <> struct is_signed<char> {
558 // char signedness is implementation-defined; detect at compile time.
559 enum : bool { value = (char(-1) < char(0)) };
560};
561template <> struct is_signed<signed char> {
562 enum : bool { value = true };
563};
564template <> struct is_signed<short> {
565 enum : bool { value = true };
566};
567template <> struct is_signed<int> {
568 enum : bool { value = true };
569};
570template <> struct is_signed<long> {
571 enum : bool { value = true };
572};
573template <> struct is_signed<long long> {
574 enum : bool { value = true };
575};
576template <> struct is_signed<float> {
577 enum : bool { value = true };
578};
579template <> struct is_signed<double> {
580 enum : bool { value = true };
581};
582template <> struct is_signed<long double> {
583 enum : bool { value = true };
584};
585// cv-qualified and reference forwarding
586template <typename T> struct is_signed<const T> {
587 enum : bool { value = is_signed<T>::value };
588};
589template <typename T> struct is_signed<volatile T> {
590 enum : bool { value = is_signed<T>::value };
591};
592template <typename T> struct is_signed<const volatile T> {
593 enum : bool { value = is_signed<T>::value };
594};
595// Note: sized integer types (i8, i16, i32, i64) are typedefs
596// for the basic types above, so they automatically inherit these specializations
597
598//-------------------------------------------------------------------------------
599// make_unsigned - converts signed integer types to their unsigned counterparts
600//-------------------------------------------------------------------------------
601// Uses size-based selection to work with any integer type (including platform-specific types)
602// Only valid for integral types excluding bool (matches std::make_unsigned behavior)
603
605
606// Size-based unsigned type selector
607template <fl::size Size> struct unsigned_by_size;
608template <> struct unsigned_by_size<1> { using type = unsigned char; };
609template <> struct unsigned_by_size<2> { using type = unsigned short; };
610template <> struct unsigned_by_size<4> { using type = unsigned int; };
611template <> struct unsigned_by_size<8> { using type = unsigned long long; };
612
613} // namespace make_unsigned_detail
614
615// Primary template - triggers static_assert for invalid types (float, double, bool, etc.)
616template <typename T, typename Enable = void>
618 // Dependent false to delay static_assert until instantiation
620 "make_unsigned requires a non-bool integral type");
621};
622
623// Specialization for valid integral types (excludes bool)
624template <typename T>
625struct make_unsigned<T, typename enable_if<
626 is_integral<T>::value && !is_same<typename remove_cv<T>::type, bool>::value
627>::type> {
629};
630
631//-------------------------------------------------------------------------------
632// Type size ranking for promotion rules
633//-------------------------------------------------------------------------------
634template <typename T> struct type_rank {
635 static constexpr int value = 0;
636};
637template <> struct type_rank<bool> {
638 static constexpr int value = 1;
639};
640template <> struct type_rank<signed char> {
641 static constexpr int value = 2;
642};
643template <> struct type_rank<unsigned char> {
644 static constexpr int value = 2;
645};
646template <> struct type_rank<char> {
647 static constexpr int value = 2;
648};
649template <> struct type_rank<short> {
650 static constexpr int value = 3;
651};
652template <> struct type_rank<unsigned short> {
653 static constexpr int value = 3;
654};
655template <> struct type_rank<int> {
656 static constexpr int value = 4;
657};
658template <> struct type_rank<unsigned int> {
659 static constexpr int value = 4;
660};
661template <> struct type_rank<long> {
662 static constexpr int value = 5;
663};
664template <> struct type_rank<unsigned long> {
665 static constexpr int value = 5;
666};
667template <> struct type_rank<long long> {
668 static constexpr int value = 6;
669};
670template <> struct type_rank<unsigned long long> {
671 static constexpr int value = 6;
672};
673template <> struct type_rank<float> {
674 static constexpr int value = 10;
675};
676template <> struct type_rank<double> {
677 static constexpr int value = 11;
678};
679template <> struct type_rank<long double> {
680 static constexpr int value = 12;
681};
682// Note: sized integer types (i8, i16, i32, i64) are typedefs
683// for the basic types above, so they automatically inherit these specializations
684
685//-------------------------------------------------------------------------------
686// Helper templates for integer type promotion logic
687//-------------------------------------------------------------------------------
688
689// Helper: Choose type based on size (larger wins)
690template <typename T, typename U>
692 using type = typename conditional<
693 (sizeof(T) > sizeof(U)), T,
694 typename conditional<
695 (sizeof(U) > sizeof(T)), U,
696 void // same size - handled elsewhere
697 >::type
698 >::type;
699};
700
701// Helper: Choose type based on type rank when same size (higher rank wins)
702template <typename T, typename U>
704 using type = typename conditional<
706 typename conditional<
708 void // same rank - handled elsewhere
709 >::type
710 >::type;
711};
712
713// Helper: Choose type based on signedness when same size and rank (signed wins)
714template <typename T, typename U>
716 static constexpr bool t_signed = is_signed<T>::value;
717 static constexpr bool u_signed = is_signed<U>::value;
718 static constexpr bool mixed_signedness = (t_signed != u_signed);
719
720 using type = typename conditional<
722 typename conditional<
724 T // same signedness - just pick first
725 >::type
726 >::type;
727};
728
729// Helper: Main integer promotion logic dispatcher
730template <typename T, typename U>
732 static constexpr bool same_size = (sizeof(T) == sizeof(U));
734
738
739 using type = typename conditional<
741 typename conditional<
743 by_signedness_result // same size and rank
744 >::type
745 >::type;
746};
747
748//-------------------------------------------------------------------------------
749// Common type trait for type promotion - now much cleaner!
750//-------------------------------------------------------------------------------
751
752// Primary template - fallback
753template <typename T, typename U, typename = void> struct common_type_impl {
754 using type = T;
755};
756
757// Same type specialization - handles all cases where T == U
758template <typename T> struct common_type_impl<T, T> {
759 using type = T;
760};
761
762// Float/double specializations - only exist when T is numeric but not the same type, otherwise compilation fails
763template <typename T>
764struct common_type_impl<T, float, typename enable_if<(is_integral<T>::value || is_floating_point<T>::value) && !is_same<T, float>::value>::type> {
765 using type = float;
766};
767
768template <typename T>
769struct common_type_impl<T, double, typename enable_if<(is_integral<T>::value || is_floating_point<T>::value) && !is_same<T, double>::value>::type> {
770 using type = double;
771};
772
773// Symmetric specializations - when first type is float/double and second is numeric but not the same type
774template <typename T>
775struct common_type_impl<float, T, typename enable_if<(is_integral<T>::value || is_floating_point<T>::value) && !is_same<T, float>::value>::type> {
776 using type = float;
777};
778
779template <typename T>
780struct common_type_impl<double, T, typename enable_if<(is_integral<T>::value || is_floating_point<T>::value) && !is_same<T, double>::value>::type> {
781 using type = double;
782};
783
784// Explicitly forbid i8 and u8 combinations
785// No type member = clear compilation error when accessed
786template <>
787struct common_type_impl<i8, u8, void> {
788 // Intentionally no 'type' member - will cause error:
789 // "no type named 'type' in 'struct fl::common_type_impl<signed char, unsigned char, void>'"
790};
791
792template <>
793struct common_type_impl<u8, i8, void> {
794 // Intentionally no 'type' member - will cause error:
795 // "no type named 'type' in 'struct fl::common_type_impl<unsigned char, signed char, void>'"
796};
797
798// Generic integer promotion logic - now much cleaner!
799template <typename T, typename U>
800struct common_type_impl<T, U, typename enable_if<
802 !is_same<T, U>::value &&
803 !((is_same<T, i8>::value && is_same<U, u8>::value) ||
804 (is_same<T, u8>::value && is_same<U, i8>::value))
805>::type> {
807};
808
809// Mixed floating point sizes - larger wins
810template <> struct common_type_impl<float, double> { using type = double; };
811template <> struct common_type_impl<double, float> { using type = double; };
812template <> struct common_type_impl<float, long double> { using type = long double; };
813template <> struct common_type_impl<long double, float> { using type = long double; };
814template <> struct common_type_impl<double, long double> { using type = long double; };
815template <> struct common_type_impl<long double, double> { using type = long double; };
816
817template <typename T, typename U> struct common_type {
819};
820
821template <typename T, typename U>
823
824// This uses template magic to maybe generate a type for the given condition. If
825// that type doesn't exist then a type will fail to be generated, and the
826// compiler will skip the consideration of a target function. This is useful for
827// enabling template constructors that only become available if the class can be
828// upcasted to the desired type.
829//
830// Example:
831// This is an optional upcasting constructor for a Ref<T>. If the type U is not
832// derived from T then the constructor will not be generated, and the compiler
833// will skip it.
834//
835// template <typename U, typename = fl::is_derived<T, U>>
836// Ref(const Ref<U>& refptr) : referent_(refptr.get());
837template <typename Base, typename Derived>
839
840//-----------------------------------------------------------------------------
841// detect whether T has a member void swap(T&)
842//-----------------------------------------------------------------------------
843template <typename T> struct has_member_swap {
844 private:
845 // must be 1 byte vs. >1 byte for sizeof test
846 typedef u8 yes;
847 typedef u16 no;
848
849 // helper<U, &U::swap> is only well-formed if U::swap(T&) exists with that
850 // signature
851 template <typename U, void (U::*M)(U &)> struct helper {};
852
853 // picks this overload if helper<U, &U::swap> is valid
854 template <typename U> static yes test(helper<U, &U::swap> *) FL_NO_EXCEPT;
855
856 // fallback otherwise
857 template <typename> static no test(...) FL_NO_EXCEPT;
858
859 public:
860 static constexpr bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
861};
862
863// primary template: dispatch on has_member_swap<T>::value
864template <typename T, bool = has_member_swap<T>::value> struct swap_impl;
865
866// POD case - now using move semantics for better performance
867template <typename T> struct swap_impl<T, false> {
868 static void apply(T &a, T &b) FL_NO_EXCEPT {
869 T tmp = fl::move(a);
870 a = fl::move(b);
871 b = fl::move(tmp);
872 }
873};
874
875// non‑POD case (requires T implements swap)
876template <typename T> struct swap_impl<T, true> {
877 static void apply(T &a, T &b) FL_NO_EXCEPT { a.swap(b); }
878};
879
880// single entry‑point
881//
882// Unlike the other generic templates (see fl/stl/adl_barrier.h), swap stays
883// directly in fl so unqualified `swap(a, b)` on fl types keeps finding it via
884// ADL. `b` is its own template parameter, constrained to the same type, which
885// makes this overload less specialized than std::swap(T&, T&). When std::swap
886// is visible (e.g. inside std::reverse / std::sort on CRGB, FastLED#4827) std
887// wins instead of the call being ambiguous; otherwise this one is used.
888template <typename T, typename U>
890 // if T is a POD, use use a simple data copy swap.
891 // if T is not a POD, use the T::Swap method.
893}
894
895
896template <typename T> void swap_by_copy(T &a, T &b) FL_NO_EXCEPT {
897 // Force copy semantics (for cases where move might not be safe)
898 T tmp = a;
899 a = b;
900 b = tmp;
901}
902
903// Container type checks.
904template <typename T, typename... Types> struct contains_type;
905
906template <typename T> struct contains_type<T> {
907 enum : bool { value = false };
908};
909
910template <typename T, typename U, typename... Rest>
911struct contains_type<T, U, Rest...> {
912 static constexpr bool value =
914};
915
916// Helper to get maximum size of types
917template <typename... Types> struct max_size;
918
919template <> struct max_size<> {
920 enum : fl::size { value = 0 };
921};
922
923template <typename T, typename... Rest> struct max_size<T, Rest...> {
924 enum : fl::size {
925 value = (sizeof(T) > max_size<Rest...>::value)
926 ? sizeof(T)
927 : max_size<Rest...>::value
928 };
929};
930
931// Helper to get maximum alignment of types
932template <typename... Types> struct max_align;
933
934template <> struct max_align<> {
935 enum : fl::size { value = 1 };
936};
937
938template <typename T, typename... Rest> struct max_align<T, Rest...> {
939 enum : fl::size {
940 value = (alignof(T) > max_align<Rest...>::value)
941 ? alignof(T)
942 : max_align<Rest...>::value
943 };
944};
945
946// alignment_of trait
947template <typename T>
949 enum : fl::size { value = alignof(T) };
950};
951
952//-------------------------------------------------------------------------------
953// is_enum trait - detects enum and enum class types
954//-------------------------------------------------------------------------------
955#if defined(__GNUC__) || defined(__clang__)
956// GCC and Clang support __is_enum intrinsic
957template <typename T> struct is_enum {
958 enum : bool { value = __is_enum(T) };
959};
960#elif defined(_MSC_VER)
961// MSVC also supports __is_enum
962template <typename T> struct is_enum {
963 enum : bool { value = __is_enum(T) };
964};
965#else
966// Fallback for other compilers - conservative approach
967// Assumes non-enum by default (safe but may need manual overloads)
968template <typename T> struct is_enum {
969 enum : bool { value = false };
970};
971#endif
972
973//-------------------------------------------------------------------------------
974// is_trivially_copyable trait - detects types safe for memcpy/realloc
975// Critical for determining if a type can be safely used with fl::realloc()
976//-------------------------------------------------------------------------------
977#if defined(__GNUC__) || defined(__clang__)
978// GCC and Clang support __is_trivially_copyable intrinsic
979template <typename T> struct is_trivially_copyable {
980 enum : bool { value = __is_trivially_copyable(T) };
981};
982#elif defined(_MSC_VER)
983// MSVC supports __is_trivially_copyable
984template <typename T> struct is_trivially_copyable {
985 enum : bool { value = __is_trivially_copyable(T) };
986};
987#else
988// Fallback for other compilers - use is_pod as conservative approximation
989// This is overly conservative but safe (may reject some valid types)
990template <typename T> struct is_trivially_copyable {
991 enum : bool { value = is_pod<T>::value };
992};
993#endif
994
995// Helper for variable template compatibility
996template <typename T> struct is_trivially_copyable_v_helper {
997 static constexpr bool value = is_trivially_copyable<T>::value;
998};
999
1000//-------------------------------------------------------------------------------
1001// underlying_type trait - gets underlying type of enum
1002//-------------------------------------------------------------------------------
1003#if defined(__GNUC__) || defined(__clang__)
1004// GCC and Clang support __underlying_type intrinsic
1005template <typename T> struct underlying_type {
1006 using type = __underlying_type(T);
1007};
1008#elif defined(_MSC_VER) && _MSC_VER >= 1900
1009// MSVC 2015+ supports __underlying_type
1010template <typename T> struct underlying_type {
1011 using type = __underlying_type(T);
1012};
1013#else
1014// Fallback - assume int for non-class enums (C++03 behavior)
1015template <typename T> struct underlying_type {
1016 using type = int;
1017};
1018#endif
1019
1020template <typename T>
1022
1023// C++11 requires out-of-class definitions for static constexpr members that are ODR-used
1024// These definitions are in src/fl/static_constexpr_defs.cpp to avoid duplicate symbols
1025
1026// For comparison operators that return bool against pod data. The class obj
1027// will need to supply the comparison operator for the pod type. This example
1028// will show how to define a comparison operator for a class that can be
1029// compared against a pod type.
1030// Example:
1031// FASTLED_DEFINE_POD_COMPARISON_OPERATOR(Myclass, >=) will allow MyClass to
1032// be compared MyClass obj; return obj >= 0;
1033#define FASTLED_DEFINE_POD_COMPARISON_OPERATOR(CLASS, OP) \
1034 template <typename T, typename U> \
1035 typename enable_if< \
1036 is_same<U, CLASS>::value && is_pod<T>::value, bool>::type \
1037 operator OP(const T &pod, const CLASS &obj) FL_NO_EXCEPT { \
1038 return pod OP obj; \
1039 } \
1040 template <typename T> \
1041 typename enable_if<is_pod<T>::value, bool>::type operator OP( \
1042 const CLASS &obj, const T &pod) FL_NO_EXCEPT { \
1043 return obj OP pod; \
1044 }
1045
1046//-------------------------------------------------------------------------------
1047// Integer type casting helper for string formatting
1048//-------------------------------------------------------------------------------
1049// Helper to determine target type for integer conversion based on size/signedness
1050// Used by both basic_string::append and sstream::operator<< for consistent formatting
1052 // Primary template: generic fallback using size/signedness
1053 // This handles any integer type by selecting the appropriate fl:: type based on its characteristics
1054 template<typename T, fl::size Size = sizeof(T), bool IsSigned = fl::is_signed<T>::value>
1056 // Generic implementation based on size and signedness
1057 // Selects appropriate fl:: integer type (i8/u8/i16/u16/i32/u32/i64/u64)
1058 using type = typename fl::conditional<
1059 IsSigned,
1060 // Signed integer path
1061 typename fl::conditional<Size == 1, fl::i8,
1062 typename fl::conditional<Size == 2, fl::i16,
1063 typename fl::conditional<Size == 4, fl::i32,
1064 typename fl::conditional<Size == 8, fl::i64,
1065 fl::i64 // Fallback to i64 for >8 byte types (rare)
1066 >::type
1067 >::type
1068 >::type
1069 >::type,
1070 // Unsigned integer path (note: 1-byte unsigned promotes to u16 for readability)
1071 typename fl::conditional<Size == 1, fl::u16, // u8 → u16 (avoid char display)
1072 typename fl::conditional<Size == 2, fl::u16,
1073 typename fl::conditional<Size == 4, fl::u32,
1074 typename fl::conditional<Size == 8, fl::u64,
1075 fl::u64 // Fallback to u64 for >8 byte types (rare)
1076 >::type
1077 >::type
1078 >::type
1079 >::type
1080 >::type;
1081 };
1082
1083 // Explicit specializations for common cases (optimization - avoids nested conditionals)
1084 // These provide the same mapping as the generic template but compile faster
1085
1086 // 1-byte unsigned → u16 (ergonomic: displays uint8_t as number, not char)
1087 template<typename T> struct cast_target<T, 1, false> { using type = fl::u16; };
1088
1089 // 1-byte signed → i8
1090 template<typename T> struct cast_target<T, 1, true> { using type = fl::i8; };
1091
1092 // 2-byte unsigned → u16
1093 template<typename T> struct cast_target<T, 2, false> { using type = fl::u16; };
1094
1095 // 2-byte signed → i16
1096 template<typename T> struct cast_target<T, 2, true> { using type = fl::i16; };
1097
1098 // 4-byte unsigned → u32
1099 template<typename T> struct cast_target<T, 4, false> { using type = fl::u32; };
1100
1101 // 4-byte signed → i32
1102 template<typename T> struct cast_target<T, 4, true> { using type = fl::i32; };
1103
1104 // 8-byte unsigned → u64
1105 template<typename T> struct cast_target<T, 8, false> { using type = fl::u64; };
1106
1107 // 8-byte signed → i64
1108 template<typename T> struct cast_target<T, 8, true> { using type = fl::i64; };
1109}
1110
1111//-------------------------------------------------------------------------------
1112// callable_traits - Extract signature from callable types (lambdas, functors, function pointers)
1113// Used for auto-deducing RPC method signatures from lambdas
1114// NOTE: args_tuple is NOT provided here due to header dependency issues (fl::tuple
1115// is defined in a header that includes this one). Use function_traits in typed_rpc.h
1116// if you need args_tuple.
1117//-------------------------------------------------------------------------------
1118
1119// Forward declaration
1120template <typename T, typename = void>
1122
1123// Member function pointer (non-const) - handles mutable lambdas and functors
1124template <typename C, typename R, typename... Args>
1125struct callable_traits<R(C::*)(Args...)> {
1126 using signature = R(Args...);
1127 using return_type = R;
1128 static constexpr fl::size arity = sizeof...(Args);
1129};
1130
1131// Member function pointer (const) - handles regular lambdas and const functors
1132template <typename C, typename R, typename... Args>
1133struct callable_traits<R(C::*)(Args...) const> {
1134 using signature = R(Args...);
1135 using return_type = R;
1136 static constexpr fl::size arity = sizeof...(Args);
1137};
1138
1139// Free function pointer
1140template <typename R, typename... Args>
1141struct callable_traits<R(*)(Args...)> {
1142 using signature = R(Args...);
1143 using return_type = R;
1144 static constexpr fl::size arity = sizeof...(Args);
1145};
1146
1147// Free function (not pointer)
1148template <typename R, typename... Args>
1149struct callable_traits<R(Args...)> {
1150 using signature = R(Args...);
1151 using return_type = R;
1152 static constexpr fl::size arity = sizeof...(Args);
1153};
1154
1155// Lambda/functor with operator() - delegates to member function pointer version
1156// Uses SFINAE to detect callable types with operator()
1157template <typename T>
1158struct callable_traits<T, typename enable_if<
1159 is_member_function_pointer<decltype(&T::operator())>::value
1160>::type> : callable_traits<decltype(&T::operator())> {};
1161
1162// =============================================================================
1163// Index sequence implementation for tuple unpacking (moved from typed_rpc.h)
1164// =============================================================================
1165
1166template <fl::size... Is>
1169 static constexpr fl::size size() FL_NO_EXCEPT { return sizeof...(Is); }
1170};
1171
1172// Log-depth construction: build the sequence for N/2 and concatenate it with
1173// itself shifted by N/2 (plus one extra index when N is odd). The naive
1174// N-1 recursion instantiated 256 nested templates for the gamma LUT, which
1175// cost ~50 ms of front-end time in every TU that included gamma_lut.h.
1176// No short names like S1/I1 here: board variants define pin names as
1177// macros (the SparkFun XRP's pins_arduino.h defines S1..S4), which would
1178// expand inside these declarations.
1179template <typename LhsSeq, typename RhsSeq>
1181
1182template <fl::size... Lhs, fl::size... Rhs>
1184 using type = index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>;
1185};
1186
1187template <fl::size N>
1189 using type = typename index_sequence_concat<
1190 typename make_index_sequence_impl<N / 2>::type,
1191 typename make_index_sequence_impl<N - N / 2>::type>::type;
1192};
1193
1194template <>
1198
1199template <>
1203
1204template <fl::size N>
1206
1207//-------------------------------------------------------------------------------
1208// is_fixed_point trait — default false; specialized in fl/stl/fixed_point.h
1209//-------------------------------------------------------------------------------
1210template <typename T> struct is_fixed_point : false_type {};
1211
1212} // namespace fl
Keeps fl's generic templates out of argument-dependent lookup (ADL).
#define constexpr
Declares that it is possible to evaluate a value at compile time, introduced in C++11.
Definition cpp_compat.h:15
constexpr T && forward(typename remove_reference< T >::type &t) FL_NO_EXCEPT
constexpr remove_reference< T >::type && move(T &&t) FL_NO_EXCEPT
Definition s16x16x4.h:30
signed char i8
Definition s16x16x4.h:131
typename fl::conditional< IsSigned, typename fl::conditional< Size==1, fl::i8, typename fl::conditional< Size==2, fl::i16, typename fl::conditional< Size==4, fl::i32, typename fl::conditional< Size==8, fl::i64, fl::i64 >::type >::type >::type >::type, typename fl::conditional< Size==1, fl::u16, typename fl::conditional< Size==2, fl::u16, typename fl::conditional< Size==4, fl::u32, typename fl::conditional< Size==8, fl::u64, fl::u64 >::type >::type >::type >::type >::type type
unsigned char u8
Definition stdint.h:131
void swap(array< T, N > &lhs, array< T, N > &rhs) FL_NO_EXCEPT
Definition array.h:209
typename common_type< T, U >::type common_type_t
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
typename underlying_type< T >::type underlying_type_t
constexpr int type_rank< T >::value
typename conditional< B, T, F >::type conditional_t
void swap_by_copy(T &a, T &b) FL_NO_EXCEPT
typename remove_cv< T >::type remove_cv_t
add_rvalue_reference< T >::type declval() FL_NO_EXCEPT
integral_constant< bool, false > false_type
Definition type_traits.h:29
integral_constant< bool, B > bool_constant
Definition type_traits.h:33
typename remove_pointer< T >::type remove_pointer_t
typename decay< T >::type decay_t
fl::i64 i64
Definition s16x16x4.h:222
enable_if_t< is_base_of< Base, Derived >::value > is_derived
typename add_pointer< T >::type add_pointer_t
signed char i8
Definition stdint.h:130
typename make_index_sequence_impl< N >::type make_index_sequence
fl::u64 u64
Definition s16x16x4.h:221
typename enable_if< Condition, T >::type enable_if_t
Definition type_traits.h:67
integral_constant< bool, true > true_type
Definition type_traits.h:28
Base definition for an LED controller.
typename conditional<(sizeof(T) > sizeof(U)), T, typename conditional<(sizeof(U) > sizeof(T)), U, void >::type >::type type
typename common_type_impl< T, U >::type type
typename conditional<(type_rank< T >::value > type_rank< U >::value), T, typename conditional<(type_rank< U >::value > type_rank< T >::value), U, void >::type >::type type
typename index_sequence_concat< typename make_index_sequence_impl< N/2 >::type, typename make_index_sequence_impl< N - N/2 >::type >::type type
#define FL_STATIC_ASSERT(...)
Portable compile-time assertion wrapper.
static constexpr fl::size arity
static constexpr fl::size arity
static constexpr fl::size arity
static constexpr bool t_signed
static constexpr bool u_signed
typename conditional< mixed_signedness &&t_signed, T, typename conditional< mixed_signedness &&u_signed, U, T >::type >::type type
static constexpr bool mixed_signedness
typename remove_reference< T >::type U
typename conditional< is_array< U >::value, typename remove_extent< U >::type *, typename conditional< is_function< U >::value, typename add_pointer< U >::type, typename remove_cv< U >::type >::type >::type type
static no test(...) FL_NO_EXCEPT
static constexpr bool value
static yes test(helper< U, &U::swap > *) FL_NO_EXCEPT
index_sequence type
static constexpr fl::size size() FL_NO_EXCEPT
typename conditional< !same_size, by_size_result, typename conditional< same_size &&!same_rank, by_rank_result, by_signedness_result >::type >::type type
typename choose_by_signedness< T, U >::type by_signedness_result
typename choose_by_size< T, U >::type by_size_result
static constexpr bool same_rank
typename choose_by_rank< T, U >::type by_rank_result
static constexpr bool same_size
integral_constant type
Definition type_traits.h:22
constexpr value_type operator()() const FL_NO_EXCEPT
Definition type_traits.h:24
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
Definition type_traits.h:88
static no test(...) FL_NO_EXCEPT
static yes test(Base *) FL_NO_EXCEPT
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
static constexpr bool value
FL_STATIC_ASSERT(is_integral< T >::value &&!is_same< typename remove_cv< T >::type, bool >::value, "make_unsigned requires a non-bool integral type")
static void apply(T &a, T &b) FL_NO_EXCEPT
static void apply(T &a, T &b) FL_NO_EXCEPT
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value
static constexpr int value