mirror of
https://github.com/esphome/esphome.git
synced 2026-09-12 15:57:33 +00:00
[core] Pre-compute looping component count at compile time via codegen
Emit a static constexpr in main.cpp that uses std::is_same_v to count components with overridden loop() at C++ compile time. Pre-init the FixedVector with the exact capacity so calculate_looping_components_() can skip its counting pass entirely.
This commit is contained in:
@@ -387,16 +387,8 @@ void Application::teardown_components(uint32_t timeout_ms) {
|
||||
}
|
||||
|
||||
void Application::calculate_looping_components_() {
|
||||
// Count total components that need looping
|
||||
size_t total_looping = 0;
|
||||
for (auto *obj : this->components_) {
|
||||
if (obj->has_overridden_loop()) {
|
||||
total_looping++;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize FixedVector with exact size - no reallocation possible
|
||||
this->looping_components_.init(total_looping);
|
||||
// FixedVector capacity was pre-initialized by codegen with the exact count
|
||||
// of components that override loop(), computed at C++ compile time.
|
||||
|
||||
// Add all components with loop override that aren't already LOOP_DONE
|
||||
// Some components (like logger) may call disable_loop() during initialization
|
||||
|
||||
@@ -504,6 +504,39 @@ async def _add_controller_registry_define() -> None:
|
||||
cg.add_define("CONTROLLER_REGISTRY_MAX", controller_count)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.FINAL)
|
||||
async def _add_looping_components() -> None:
|
||||
# Emit a constexpr that computes the looping component count at C++ compile time
|
||||
# and pre-init the FixedVector with the exact capacity. Uses std::is_same_v to
|
||||
# detect loop() overrides. The constexpr goes in main.cpp's global section where
|
||||
# all component types are in scope. calculate_looping_components_() then skips
|
||||
# the counting pass and only does the two population passes.
|
||||
entries = CORE.data.get("looping_component_entries", [])
|
||||
if not entries:
|
||||
return
|
||||
|
||||
# Build constexpr sum for the exact count
|
||||
terms = [
|
||||
f"(!std::is_same_v<decltype(&{cpp_type}::loop), decltype(&Component::loop)>)"
|
||||
for cpp_type in entries
|
||||
]
|
||||
constexpr_expr = " + \\\n ".join(terms)
|
||||
cg.add_global(
|
||||
cg.RawStatement(
|
||||
f"static constexpr size_t ESPHOME_LOOPING_COMPONENT_COUNT = \\\n"
|
||||
f" {constexpr_expr};"
|
||||
)
|
||||
)
|
||||
|
||||
# Pre-init FixedVector with exact capacity so calculate_looping_components_()
|
||||
# can skip the counting pass
|
||||
cg.add(
|
||||
cg.RawExpression(
|
||||
"App.looping_components_.init(ESPHOME_LOOPING_COMPONENT_COUNT)"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(CoroPriority.CORE)
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
cg.add_global(cg.global_ns.namespace("esphome").using)
|
||||
@@ -527,6 +560,7 @@ async def to_code(config: ConfigType) -> None:
|
||||
|
||||
CORE.add_job(_add_platform_defines)
|
||||
CORE.add_job(_add_controller_registry_define)
|
||||
CORE.add_job(_add_looping_components)
|
||||
|
||||
CORE.add_job(_add_automations, config)
|
||||
|
||||
|
||||
@@ -80,6 +80,11 @@ async def register_component(var, config):
|
||||
add(var.set_component_source(LogStringLiteral(name)))
|
||||
|
||||
add(App.register_component_(var))
|
||||
|
||||
# Collect C++ type for compile-time looping component count
|
||||
comp_entries = CORE.data.setdefault("looping_component_entries", [])
|
||||
comp_entries.append(str(var.base.type))
|
||||
|
||||
return var
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user