FastLED 3.9.3
Loading...
Searching...
No Matches
template_magic.h
1#pragma once
2
3/*
4Provides eanble_if and is_derived for compilers before C++14.
5*/
6
7#include <stdint.h>
8
9#include "namespace.h"
10
11namespace fl { // mandatory namespace to prevent name collision with std::enable_if.
12
13// Define enable_if for SFINAE
14template <bool Condition, typename T = void>
15struct enable_if {};
16
17// Specialization for true condition
18template <typename T>
19struct enable_if<true, T> {
20 using type = T;
21};
22
23// if enable_if<Condition, T> is true, then there will be a member type
24// called type. Otherwise it will not exist. This is (ab)used to enable
25// constructors and other functions based on template parameters. If there
26// is no member type, then the compiler will not fail to bind to the target
27// function or operation.
28template <bool Condition, typename T = void>
29using enable_if_t = typename enable_if<Condition, T>::type;
30
31// Define is_base_of to check inheritance relationship
32template <typename Base, typename Derived>
33struct is_base_of {
34private:
35 typedef uint8_t yes;
36 typedef uint16_t no;
37 static yes test(Base*); // Matches if Derived is convertible to Base*
38 static no test(...); // Fallback if not convertible
39 enum {
40 kSizeDerived = sizeof(test(static_cast<Derived*>(nullptr))),
41 };
42public:
43 static constexpr bool value = (kSizeDerived == sizeof(yes));
44};
45
46// Define is_base_of_v for compatibility with pre-C++14
47// Replaced variable template with a constant static member
48template <typename Base, typename Derived>
50 static constexpr bool value = is_base_of<Base, Derived>::value;
51};
52
53// Define is_same trait
54template <typename T, typename U>
55struct is_same {
56 static constexpr bool value = false;
57};
58
59// Specialization for when T and U are the same type
60template <typename T>
61struct is_same<T, T> {
62 static constexpr bool value = true;
63};
64
65// Define is_same_v for compatibility with variable templates
66template <typename T, typename U>
68 static constexpr bool value = is_same<T, U>::value;
69};
70
71
72// This uses template magic to maybe generate a type for the given condition. If that type
73// doesn't exist then a type will fail to be generated, and the compiler will skip the
74// consideration of a target function. This is useful for enabling template constructors
75// that only become available if the class can be upcasted to the desired type.
76//
77// Example:
78// This is an optional upcasting constructor for a Ref<T>. If the type U is not derived from T
79// then the constructor will not be generated, and the compiler will skip it.
80//
81// template <typename U, typename = fl::is_derived<T, U>>
82// Ref(const Ref<U>& refptr) : referent_(refptr.get());
83template <typename Base, typename Derived>
84using is_derived = enable_if_t<is_base_of<Base, Derived>::value>;
85
86} // namespace fl
87
88
89// Convienence macro to define an output operator for a class to make it compatible
90// with std::ostream. This is useful for debugging and logging. The operator will
91// be defined as "os" and the right hand object will be named "obj".
92//
93// Example:
94// FASTLED_DEFINE_OUTPUT_OPERATOR(CRGB) {
95// os <<("CRGB(");
96// os <<(static_cast<int>(obj.r));
97// os <<(", ");
98// os <<(static_cast<int>(obj.g));
99// os <<(", ");
100// os <<(static_cast<int>(obj.b));
101// os <<(")");
102// return os;
103// }
104//
105// This is needed because in C++ there is two phase lookup, in which ONLY the first
106// parameter will be considered if matched, even if the second argument is a non match.
107//
108// This macro get's around this issue.
109//
110// Consider the following templated operator definition:
111// template<typename OutputStream>
112// OutputStream &operator<<(OutputStream &os, const Str &str) {
113// os << str.c_str();
114// return os;
115// }
116//
117// You would think this would only match if the left hand side is an ostream and the
118// second parameter is "Str", but you would be wrong, because of two phase lookup
119// this function will be considered for any type of ostream and ANY type of second
120// parameter, even "float" or "int".
121//
122// This means that normally, this template will match std::stream << float
123// then fail because of ambiguity, even though the second template is not
124// a match. Therefore we use the enable_if which will generate a type if
125// and only if the the second condition is a match.
126//
127// This essentially forces two phase lookup in one pass. Making the compiler skip
128// the definition if the second parameter doesn't match.
129#define FASTLED_DEFINE_OUTPUT_OPERATOR(CLASS) \
130template <typename T, typename U> \
131typename fl::enable_if<fl::is_same<U, CLASS>::value, T&>::type \
132operator<<(T& os, const CLASS& obj)