The template wrapper caused the compiler to make different
optimization decisions on setup(), negating the savings.
Calling register_component_ directly was -4 bytes.
Co-Authored-By: J. Nick Koston <nick@koston.org>
Using the original register_component name (just moved to protected)
produces identical compiled output to the baseline, avoiding the
symbol name length overhead from renaming.
Co-Authored-By: J. Nick Koston <nick@koston.org>
Rename the non-template to register_component_impl_ and add
a protected template register_component_ that wraps it. This
preserves the compiler optimization behavior (isra clones)
while keeping the method inaccessible to external components.
Co-Authored-By: J. Nick Koston <nick@koston.org>
Without the template wrapper, the compiler inlines the 24-byte
function at each of ~92 call sites in setup(), growing setup()
by ~284 bytes. Force a function call instead.
Co-Authored-By: J. Nick Koston <nick@koston.org>
Arduino.h on ESP8266 already declares void setup(void), so our
forward declaration triggers readability-redundant-declaration.
Co-Authored-By: J. Nick Koston <nick@koston.org>
The cpp test framework renames setup() to original_setup() and
replaces setup() with the gtest runner, so we need to friend both.
Co-Authored-By: J. Nick Koston <nick@koston.org>
Instead of runtime null, duplicate, and capacity checks in
register_component_, make the method unreachable from outside
codegen by removing the public template wrapper and granting
friend access to the codegen-generated ::setup() function.
Since ESPHOME_COMPONENT_COUNT is set to exactly
len(CORE.component_ids) at codegen time, the StaticVector is
always correctly sized and the runtime capacity check cannot
trigger from codegen. External components that bypassed codegen
to call App.register_component() directly will now get a
compile error, forcing them to properly declare their components
in their config schema.
Co-Authored-By: J. Nick Koston <nick@koston.org>
Move set_component_state_ from component.cpp to the header as
an inline method so it can be reused wherever Component state
needs to be updated. Replace the manual bit manipulation in
Application::enable_pending_loops_ with a call to the helper.
Co-Authored-By: J. Nick Koston <nick@koston.org>
The sen6x test YAML used identical sensor IDs (pm_1_0, pm_2_5,
pm_4_0, pm_10_0) as sen5x, causing ID redefinition errors when
both components are tested together in grouped builds. Prefix
the sen6x IDs with sen6x_ to make them unique.
Make set_mode(const char*, size_t) the real implementation using
ESPHOME_strncasecmp_P with length checks, instead of ignoring len
and delegating to the null-terminated overload.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Avoid implicit StringRef->std::string conversion when the protobuf
message fields are already StringRef. Use the (const char*, size_t)
overloads directly.
ESP-IDF SDK components reference fprintf(), printf(), and vprintf()
which pull in newlib's _vfprintf_r (~11 KB). This is a separate
implementation from _svfprintf_r (used by snprintf/vsnprintf) that
handles FILE* stream I/O with buffering and locking.
ESPHome replaces the ESP-IDF log handler via esp_log_set_vprintf_(),
so the SDK's vprintf() path is dead code at runtime. The fprintf()
and printf() calls in SDK components are only in debug/assert paths
that are either GC'd or never called.
These linker --wrap stubs redirect through vsnprintf() + fwrite(),
allowing the linker to dead-code eliminate _vfprintf_r.
An escape hatch is provided via enable_full_printf: true in the
esp32 advanced config section for external components that need
full FILE*-based fprintf.