Pass the global's declared C++ type as the lambda return type so
TemplatableFn stores a direct function pointer instead of hitting the
deprecated converting trampoline when the value expression deduces to a
different type (e.g. an int literal or int-returning lambda assigned to
a float global).
Both are trivial bit operations on a uint16_t (one or/and on flags_),
but without ESPHOME_ALWAYS_INLINE each call costs a call0/call8 plus
the return/register-window rotation — more work than the body itself.
validate_() invokes these ~8 times and transform_parameters_() adds
more, so runtime benchmarks should reflect the elimination of the
call overhead.
Note: a previous attempt at this (#15402) was closed based on flash
size deltas alone. This reopens the question with CodSpeed in-loop
to measure actual runtime impact.
Since validate_() now accesses unit_fields_[] directly via the union
alias, there's no offsetof arithmetic to guard. The FieldFlags bit
layout assert is all that's needed.
Replaces the previous `float *p = &this->brightness_; p[bit]` pattern
(flagged by Copilot as pointer-into-scalar UB) with a shared anonymous
union that exposes brightness_..warm_white_ as unit_fields_[8] in both
LightCall and LightColorValues. validate_() now indexes the real array
directly — defined behavior — while the named members stay accessible
to getters/setters. Code generation is unchanged.
The union is declared once via ESPHOME_LIGHT_UNIT_FIELDS_UNION() in
light_color_values.h and expanded in both structs. The per-field
offsetof static_asserts collapse to a single FieldFlags bit-layout
assert since the union guarantees member ↔ array-index alignment.
Caught by Copilot on PR review.
-0.0f has bit pattern 0x80000000 which exceeds ONE_F_BITS as unsigned,
so the check previously flagged it and emitted a spurious out-of-range
warning. Add an explicit compare against NEG_ZERO_F_BITS (declared as
a named constexpr for readability) so -0.0f takes the in-range fast
path. clamp_unit_float() already returned 0.0f for it via the sign-bit
branch, so behavior on clamp is unchanged.
Caught by Copilot on PR review.
The PROGMEM_STRING_TABLE expansion is entirely constexpr-initialized, so it doesn't require the per-static thread-safe-init guard variables that the previous `static const LogString *const FIELD_NAMES[8] PROGMEM = { LOG_STR(...) }` form emitted inside validate_() (LOG_STR contains a GCC statement-expression, which is not a constant expression, so GCC fell back to dynamic init with an 8-byte guard).
Combined the 8 clamp-field names and "Color temperature" into a single ValidateFieldNames table; index 8 is reserved for CT. log_value_out_of_range_() now takes the resolved `const LogString *` directly, so the helper no longer needs its internal progmem_read_ptr dereference.
Impact vs. prior commit (isolated light build):
ESP8266 .irom0.text: 267912 -> 267784 (-128 B)
ESP8266 .bss guard vars on light symbols: 16 -> 0 (-16 B RAM)
ESP32-IDF .flash.text: 136988 -> 136996 (+8 B, marginal)