- Add std::launder to reinterpret_cast in inline callback path for
formal C++20 correctness (generates identical code)
- Fix static_assert comment: sizeof equality ensures round-trip through
uintptr_t, not absence of trap representations per se
- Document that call() is only valid on Callbacks from create()
Convert rotary_encoder register_listener, canbus add_callback,
sdl add_key_listener, and udp add_listener from std::function to
templates to avoid the heap-allocation fallback path.
MQTT backend set_on_* methods are virtual and cannot be templatized.
Pass wrapping lambdas directly to CallbackManager::add() instead of
first wrapping in std::function. The lambdas capture [this, callback]
(two pointers) so they hit the heap path either way, but this avoids
double-wrapping and the extra std::function overhead.
Also fix "deallaction" typo.
Convert haier, nextion, microphone, ezo, uart, speaker, zigbee,
and hlk_fm22x callback methods that were missed by the initial
conversion (they used add_*_callback instead of add_on_*_callback).
Use (*ptr)(args...) instead of ptr->operator()(args...) so the
inline path works correctly for function pointers, which are
trivially copyable and pointer-sized but lack operator().
Replace char-by-char loops with __builtin_memcpy in the inline
Callback storage path. Generates identical code but is cleaner
and avoids clang-analyzer false positives without NOLINT annotations.
Convert all remaining component add_on_*_callback methods from
std::function to templates so lambdas flow through without wrapping.
This avoids a heap allocation regression where passing std::function
through Callback::create would heap-allocate the std::function itself.
Also add NOLINT annotations for clang-analyzer false positive on
byte-wise copy in Callback::create.
Replace std::function with a lightweight type-erased Callback struct
(8 bytes on 32-bit vs 16 for std::function) in CallbackManager and
LazyCallbackManager. Using C++20 if constexpr, small trivially-copyable
callables like [this] lambdas (81% of all callback registrations) are
stored inline in the function pointer context without heap allocation.
This eliminates std::function null checks (and __throw_bad_function_call)
from all entity callback paths, and templatizes add_on_*_callback methods
across all entity types so lambdas flow through without being wrapped in
std::function first.
Also converts EntityStateBase from hand-rolled nullable CallbackManager
pointers to LazyCallbackManager for consistency.
Measured flash savings:
- ESP8266 simple: -160 B
- ESP8266 ratgdo: -592 B
- ESP32 IDF (large): -456 B
- ESP32 IDF (minimal): -212 B
Create per-platform logger_*.h headers with inline write_msg_()
for ESP32, ESP8266, RP2040, and LibreTiny. These are all one-liners
that benefit from inlining into write_to_console_(), eliminating
function call overhead on every log message.
Host and Zephyr implementations remain in .cpp files as they are
more complex (timestamp formatting, printk + uart loop).
Create per-platform logger_*.h headers with inline write_msg_()
for ESP32, ESP8266, RP2040, and LibreTiny. These are all one-liners
that benefit from inlining into write_to_console_(), eliminating
function call overhead on every log message.
Host and Zephyr implementations remain in .cpp files as they are
more complex (timestamp formatting, printk + uart loop).
Replace write_() call with direct char stores for the 4-byte
ANSI reset sequence ("\033[0m"). This eliminates function call
overhead (entry/retw, bounds check, memcpy setup) on every log
message and avoids a rodata string reference that consumes RAM
on ESP8266.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
On IDF 5.x, the ESP-IDF headers use CONFIG_ETH_SPI_ETHERNET_W5500 and
CONFIG_ETH_SPI_ETHERNET_DM9051 to conditionally expose the W5500/DM9051
types. Static analysis (clang-tidy) needs both the new USE_ETHERNET_*
defines and the old CONFIG_* defines to resolve all symbols.
On IDF 5.x these headers don't exist as standalone files — they are
included internally via esp_eth.h. On IDF 6.0 they moved to registry
components and need explicit includes.
LAN867x is unchanged (external component since IDF 5.3).