FastLED 3.9.15
Loading...
Searching...
No Matches
not_null.h
Go to the documentation of this file.
1#pragma once
2
4// FastLED not_null<T> - Non-null pointer wrapper from C++ Core Guidelines GSL
5//
6// Provides a lightweight wrapper that enforces non-null pointer semantics at
7// compile-time and runtime (in debug builds). Based on the C++ Core Guidelines
8// Support Library (GSL).
9//
10// Key Features:
11// - Zero-overhead abstraction in release builds (assertions compiled out)
12// - Compile-time prevention of nullptr assignment via deleted constructors
13// - Runtime assertion of non-null invariant in debug builds
14// - Platform-aware assertions (desktop, ESP32, WASM, AVR)
15// - Works with raw pointers, smart pointers, and custom pointer types
16//
17// Platform-Specific Behavior:
18// - Desktop/Host: Full assertion support with detailed error messages
19// - ESP32: Serial output via fl::println() with assertion message
20// - WASM: Browser console warning + debugger breakpoint (emscripten_debugger)
21// - AVR: Lightweight assertion (FL_WARN_IF) due to memory constraints
22// - All platforms: Assertions only active in debug builds (FL_DEBUG defined)
23//
24// Usage Example:
25// // Function parameter - expresses non-null contract in type system
26// void setPixels(fl::not_null<CRGB*> leds, int count) {
27// // No null check needed - guaranteed by type
28// for (int i = 0; i < count; i++) {
29// leds[i] = CRGB::Red; // Safe dereference
30// }
31// }
32//
33// // Call site - compiler enforces non-null at construction
34// CRGB pixels[100];
35// setPixels(pixels, 100); // OK - implicit conversion
36// setPixels(nullptr, 100); // Compile error - deleted constructor
37//
38// // Works with smart pointers
39// auto uptr = fl::make_unique<int>(42);
40// fl::not_null<int*> nnptr(uptr.get()); // OK if uptr is non-null
41//
42// References:
43// - https://cpp-core-guidelines-docs.vercel.app/gsl
44// - https://github.com/microsoft/GSL
45// - https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
47
48#include "fl/stl/type_traits.h"
49#include "fl/stl/cstddef.h"
50#include "fl/stl/noexcept.h"
52
53// Forward declarations to break circular dependency with assert.h
54// not_null needs assertions, but assert.h might need not_null in the future
55namespace fl {
56namespace detail {
57 // Low-level assertion for not_null - implementation in not_null.cpp
58 // Provides platform-specific failure handling without depending on assert.h
59 void not_null_assert_failed(const char* message) FL_NOEXCEPT;
60} // namespace detail
61} // namespace fl
62
63namespace fl {
64
65// Forward declaration
66template<typename T> // IWYU pragma: keep
67class not_null; // IWYU pragma: keep
68
69namespace detail {
70
71// Type trait: is_reference
72// Detects if T is a reference type (lvalue or rvalue reference)
73template<typename T>
75
76template<typename T>
78
79template<typename T>
80struct is_reference<T&&> : fl::true_type {};
81
82// Type trait: is_comparable_to_nullptr
83// Detects if T can be compared to nullptr using SFINAE
84template<typename T>
86private:
87 template<typename U>
88 static auto test(int) FL_NOEXCEPT -> decltype(fl::declval<U>() == nullptr, fl::true_type{});
89
90 template<typename>
92
93public:
94 enum : bool { value = decltype(test<T>(0))::value };
95};
96
97// Type trait: is_dereferenceable
98// Detects if T supports operator* (dereference operator)
99template<typename T>
101private:
102 template<typename U>
103 static auto test(int) FL_NOEXCEPT -> decltype(*fl::declval<U>(), fl::true_type{});
104
105 template<typename>
107
108public:
109 enum : bool { value = decltype(test<T>(0))::value };
110};
111
112} // namespace detail
113
115// not_null<T> - Non-null pointer wrapper
116//
117// Template class that wraps a pointer-like type T and enforces non-null
118// guarantees at construction and assignment. Provides zero-overhead abstraction
119// in release builds (assertions compiled out).
120//
121// Assertion Strategy:
122// The implementation uses FL_ASSERT macro which provides platform-specific
123// assertion behavior:
124// - Debug builds (FL_DEBUG defined): Active runtime checks with error messages
125// - Release builds: Assertions are compiled out (zero overhead, undefined behavior if violated)
126// - Desktop platforms: Full assertion support via FL_WARN_IF
127// - ESP32: Serial output with panic behavior (fl::println + potential halt)
128// - WASM: Browser debugger breakpoint (emscripten_debugger())
129// - AVR: Lightweight warnings (memory constrained)
130//
131// The fail-fast approach catches null violations at assignment time (not dereference),
132// making bugs easier to diagnose. In release builds, the compiler can optimize
133// aggressively knowing the pointer is never null.
134//
135// Usage:
136// int value = 42;
137// fl::not_null<int*> ptr(&value); // OK - non-null
138// *ptr = 100; // OK - dereference guaranteed safe
139//
140// fl::not_null<int*> bad(nullptr); // FAIL - assertion in debug, UB in release
141//
142// Template requirements:
143// - T must be comparable to nullptr (T == nullptr is valid)
144// - T must not be a reference type (use pointers instead)
145// - T should support dereference operator* (for most use cases)
146//
147// @tparam T Pointer-like type (raw pointer, smart pointer, custom pointer type)
149template<typename T>
150class not_null {
151private:
152 T mPtr; // Underlying pointer (mCamelCase naming per FastLED standards)
153
154 // Static assertions to enforce template constraints
156 "not_null<T>: T must be comparable to nullptr");
157
159 "not_null<T>: T must not be a reference type");
160
161public:
163 // Construction
165
166 // Deleted default constructor - not_null must always be initialized
168
169 // Primary constructor - accepts non-null pointer
170 // Asserts non-null in debug builds, undefined behavior if null in release
171 // Note: Not explicit to allow implicit conversion from T (matching GSL behavior)
172 // Note: Not constexpr in C++11 due to FL_NOT_NULL_ASSERT limitation
173 not_null(T ptr) FL_NOEXCEPT : mPtr(ptr) {
174 if (mPtr == nullptr) {
175 detail::not_null_assert_failed("not_null constructed with nullptr");
176 }
177 }
178
179 // Copy constructor (defaulted)
180 constexpr not_null(const not_null& other) FL_NOEXCEPT = default;
181
182 // Move constructor (defaulted)
183 constexpr not_null(not_null&& other) FL_NOEXCEPT = default;
184
185 // Converting constructor - allows construction from compatible pointer types
186 // Example: not_null<Base*> from not_null<Derived*>
187 template<typename U>
188 not_null(const not_null<U>& other) FL_NOEXCEPT : mPtr(other.get()) {
189 // No assertion needed - other already guarantees non-null
190 }
191
192 // Deleted nullptr constructor - prevents construction with nullptr at compile-time
194
196 // Assignment
198
199 // Copy assignment (defaulted)
200 not_null& operator=(const not_null& other) FL_NOEXCEPT = default;
201
202 // Move assignment (defaulted)
204
205 // Assignment from raw pointer - asserts non-null in debug builds
207 if (ptr == nullptr) {
208 detail::not_null_assert_failed("not_null assigned nullptr");
209 }
210 mPtr = ptr;
211 return *this;
212 }
213
214 // Deleted nullptr assignment - prevents assignment of nullptr at compile-time
216
218 // Access operators
220
221 // Get underlying pointer (explicit)
222 // Returns by const reference to avoid inflating shared_ptr use_count
223 constexpr const T& get() const FL_NOEXCEPT {
224 return mPtr;
225 }
226
227 // Implicit conversion to underlying pointer type
228 // Allows seamless integration with existing APIs expecting raw pointers
229 constexpr operator const T&() const FL_NOEXCEPT {
230 return mPtr;
231 }
232
233 // Arrow operator - for member access
234 constexpr T operator->() const FL_NOEXCEPT {
235 return mPtr;
236 }
237
238 // Dereference operator - returns reference to pointed-to object
239 constexpr auto operator*() const FL_NOEXCEPT -> decltype(*fl::declval<T>()) {
240 return *mPtr;
241 }
242
243 // Array subscript operator - for pointer-to-array types
244 template<typename U = T>
245 constexpr auto operator[](fl::size_t index) const FL_NOEXCEPT
246 -> decltype(fl::declval<U>()[index]) {
247 return mPtr[index];
248 }
249
251 // Comparison operators
253
254 // Equality comparison with another not_null
255 template<typename U>
256 constexpr bool operator==(const not_null<U>& other) const FL_NOEXCEPT {
257 return mPtr == other.get();
258 }
259
260 // Inequality comparison with another not_null
261 template<typename U>
262 constexpr bool operator!=(const not_null<U>& other) const FL_NOEXCEPT {
263 return mPtr != other.get();
264 }
265
266 // Equality comparison with raw pointer
267 template<typename U>
268 constexpr bool operator==(U other) const FL_NOEXCEPT {
269 return mPtr == other;
270 }
271
272 // Inequality comparison with raw pointer
273 template<typename U>
274 constexpr bool operator!=(U other) const FL_NOEXCEPT {
275 return mPtr != other;
276 }
277
278 // Less-than comparison (for use in ordered containers)
279 template<typename U>
280 constexpr bool operator<(const not_null<U>& other) const FL_NOEXCEPT {
281 return mPtr < other.get();
282 }
283
284 // Less-than-or-equal comparison
285 template<typename U>
286 constexpr bool operator<=(const not_null<U>& other) const FL_NOEXCEPT {
287 return mPtr <= other.get();
288 }
289
290 // Greater-than comparison
291 template<typename U>
292 constexpr bool operator>(const not_null<U>& other) const FL_NOEXCEPT {
293 return mPtr > other.get();
294 }
295
296 // Greater-than-or-equal comparison
297 template<typename U>
298 constexpr bool operator>=(const not_null<U>& other) const FL_NOEXCEPT {
299 return mPtr >= other.get();
300 }
301};
302
304// Non-member comparison operators (for symmetry)
306
307// Allow comparison: raw_ptr == not_null
308template<typename T, typename U>
309constexpr bool operator==(T lhs, const not_null<U>& rhs) FL_NOEXCEPT {
310 return lhs == rhs.get();
311}
312
313// Allow comparison: raw_ptr != not_null
314template<typename T, typename U>
315constexpr bool operator!=(T lhs, const not_null<U>& rhs) FL_NOEXCEPT {
316 return lhs != rhs.get();
317}
318
319} // namespace fl
FL_STATIC_ASSERT(detail::is_comparable_to_nullptr< T >::value, "not_null<T>: T must be comparable to nullptr")
constexpr not_null(const not_null &other) FL_NOEXCEPT=default
constexpr bool operator<(const not_null< U > &other) const FL_NOEXCEPT
Definition not_null.h:280
constexpr const T & get() const FL_NOEXCEPT
Definition not_null.h:223
constexpr bool operator==(const not_null< U > &other) const FL_NOEXCEPT
Definition not_null.h:256
constexpr bool operator!=(const not_null< U > &other) const FL_NOEXCEPT
Definition not_null.h:262
constexpr auto operator[](fl::size_t index) const FL_NOEXCEPT -> decltype(fl::declval< U >()[index])
Definition not_null.h:245
not_null & operator=(fl::nullptr_t) FL_NOEXCEPT=delete
constexpr bool operator<=(const not_null< U > &other) const FL_NOEXCEPT
Definition not_null.h:286
not_null & operator=(T ptr) FL_NOEXCEPT
Definition not_null.h:206
not_null(fl::nullptr_t) FL_NOEXCEPT=delete
not_null(const not_null< U > &other) FL_NOEXCEPT
Definition not_null.h:188
constexpr bool operator!=(U other) const FL_NOEXCEPT
Definition not_null.h:274
constexpr T operator->() const FL_NOEXCEPT
Definition not_null.h:234
constexpr bool operator==(U other) const FL_NOEXCEPT
Definition not_null.h:268
not_null & operator=(not_null &&other) FL_NOEXCEPT=default
FL_STATIC_ASSERT(!detail::is_reference< T >::value, "not_null<T>: T must not be a reference type")
constexpr not_null(not_null &&other) FL_NOEXCEPT=default
not_null() FL_NOEXCEPT=delete
constexpr bool operator>=(const not_null< U > &other) const FL_NOEXCEPT
Definition not_null.h:298
constexpr auto operator*() const FL_NOEXCEPT -> decltype(*fl::declval< T >())
Definition not_null.h:239
constexpr bool operator>(const not_null< U > &other) const FL_NOEXCEPT
Definition not_null.h:292
not_null & operator=(const not_null &other) FL_NOEXCEPT=default
void not_null_assert_failed(const char *message)
Compile-time linker keep-alive hook for a single fl::Bus.
Definition bus_traits.h:48
decltype(nullptr) nullptr_t
Definition s16x16x4.h:13
integral_constant< bool, true > true_type
Definition s16x16x4.h:27
add_rvalue_reference< T >::type declval() FL_NOEXCEPT
integral_constant< bool, false > false_type
Definition s16x16x4.h:28
__SIZE_TYPE__ size_t
Definition s16x16x4.h:16
add_rvalue_reference< T >::type declval() FL_NOEXCEPT
FASTLED_FORCE_INLINE bool operator!=(const CRGB &lhs, const CRGB &rhs) FL_NOEXCEPT
Check if two CRGB objects do not have the same color data.
Definition crgb.h:739
FASTLED_FORCE_INLINE bool operator==(const CRGB &lhs, const CRGB &rhs) FL_NOEXCEPT
Check if two CRGB objects have the same color data.
Definition crgb.h:733
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
Portable compile-time assertion wrapper.
static fl::false_type test(...) FL_NOEXCEPT
static auto test(int) FL_NOEXCEPT -> decltype(fl::declval< U >()==nullptr, fl::true_type{})
static auto test(int) FL_NOEXCEPT -> decltype(*fl::declval< U >(), fl::true_type{})
static fl::false_type test(...) FL_NOEXCEPT