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