[automation] Use std::array in And/Or/Xor conditions

Template AndCondition, OrCondition, and XorCondition on N so the
condition pointer list is stored in std::array<Condition<Ts...>*, N>
instead of FixedVector. N is set by code generation to match the
exact number of conditions configured in YAML.

Add init_array_from() helper for optimal codegen when initializing
std::array from initializer_list.
This commit is contained in:
J. Nick Koston
2026-03-28 09:09:53 -10:00
parent b6abfec82e
commit 669b90258c
3 changed files with 50 additions and 14 deletions
+15 -5
View File
@@ -250,7 +250,9 @@ async def and_condition_to_code(
args: TemplateArgsType,
) -> MockObj:
conditions = await build_condition_list(config, template_arg, args)
return cg.new_Pvariable(condition_id, template_arg, conditions)
return cg.new_Pvariable(
condition_id, cg.TemplateArguments(len(conditions), *template_arg), conditions
)
@register_condition("or", OrCondition, validate_condition_list)
@@ -261,7 +263,9 @@ async def or_condition_to_code(
args: TemplateArgsType,
) -> MockObj:
conditions = await build_condition_list(config, template_arg, args)
return cg.new_Pvariable(condition_id, template_arg, conditions)
return cg.new_Pvariable(
condition_id, cg.TemplateArguments(len(conditions), *template_arg), conditions
)
@register_condition("all", AndCondition, validate_condition_list)
@@ -272,7 +276,9 @@ async def all_condition_to_code(
args: TemplateArgsType,
) -> MockObj:
conditions = await build_condition_list(config, template_arg, args)
return cg.new_Pvariable(condition_id, template_arg, conditions)
return cg.new_Pvariable(
condition_id, cg.TemplateArguments(len(conditions), *template_arg), conditions
)
@register_condition("any", OrCondition, validate_condition_list)
@@ -283,7 +289,9 @@ async def any_condition_to_code(
args: TemplateArgsType,
) -> MockObj:
conditions = await build_condition_list(config, template_arg, args)
return cg.new_Pvariable(condition_id, template_arg, conditions)
return cg.new_Pvariable(
condition_id, cg.TemplateArguments(len(conditions), *template_arg), conditions
)
@register_condition("not", NotCondition, validate_potentially_and_condition)
@@ -305,7 +313,9 @@ async def xor_condition_to_code(
args: TemplateArgsType,
) -> MockObj:
conditions = await build_condition_list(config, template_arg, args)
return cg.new_Pvariable(condition_id, template_arg, conditions)
return cg.new_Pvariable(
condition_id, cg.TemplateArguments(len(conditions), *template_arg), conditions
)
@register_condition("lambda", LambdaCondition, cv.returning_lambda)
+16 -9
View File
@@ -9,14 +9,17 @@
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include <array>
#include <list>
#include <vector>
namespace esphome {
template<typename... Ts> class AndCondition : public Condition<Ts...> {
template<size_t N, typename... Ts> class AndCondition : public Condition<Ts...> {
public:
explicit AndCondition(std::initializer_list<Condition<Ts...> *> conditions) : conditions_(conditions) {}
explicit AndCondition(std::initializer_list<Condition<Ts...> *> conditions) {
init_array_from(this->conditions_, conditions);
}
bool check(const Ts &...x) override {
for (auto *condition : this->conditions_) {
if (!condition->check(x...))
@@ -27,12 +30,14 @@ template<typename... Ts> class AndCondition : public Condition<Ts...> {
}
protected:
FixedVector<Condition<Ts...> *> conditions_;
std::array<Condition<Ts...> *, N> conditions_{};
};
template<typename... Ts> class OrCondition : public Condition<Ts...> {
template<size_t N, typename... Ts> class OrCondition : public Condition<Ts...> {
public:
explicit OrCondition(std::initializer_list<Condition<Ts...> *> conditions) : conditions_(conditions) {}
explicit OrCondition(std::initializer_list<Condition<Ts...> *> conditions) {
init_array_from(this->conditions_, conditions);
}
bool check(const Ts &...x) override {
for (auto *condition : this->conditions_) {
if (condition->check(x...))
@@ -43,7 +48,7 @@ template<typename... Ts> class OrCondition : public Condition<Ts...> {
}
protected:
FixedVector<Condition<Ts...> *> conditions_;
std::array<Condition<Ts...> *, N> conditions_{};
};
template<typename... Ts> class NotCondition : public Condition<Ts...> {
@@ -55,9 +60,11 @@ template<typename... Ts> class NotCondition : public Condition<Ts...> {
Condition<Ts...> *condition_;
};
template<typename... Ts> class XorCondition : public Condition<Ts...> {
template<size_t N, typename... Ts> class XorCondition : public Condition<Ts...> {
public:
explicit XorCondition(std::initializer_list<Condition<Ts...> *> conditions) : conditions_(conditions) {}
explicit XorCondition(std::initializer_list<Condition<Ts...> *> conditions) {
init_array_from(this->conditions_, conditions);
}
bool check(const Ts &...x) override {
size_t result = 0;
for (auto *condition : this->conditions_) {
@@ -68,7 +75,7 @@ template<typename... Ts> class XorCondition : public Condition<Ts...> {
}
protected:
FixedVector<Condition<Ts...> *> conditions_;
std::array<Condition<Ts...> *, N> conditions_{};
};
template<typename... Ts> class LambdaCondition : public Condition<Ts...> {
+19
View File
@@ -2,6 +2,7 @@
#include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <cstdarg>
#include <cstdint>
@@ -497,6 +498,24 @@ template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()>
index_type capacity_{0};
};
/// Initialize a std::array from an initializer_list. Uses memcpy for trivially copyable types (optimal codegen),
/// falls back to element-wise copy for non-trivially copyable types (e.g. TemplatableValue).
/// N is always set by code generation — the caller is responsible for ensuring src.size() == N.
/// The debug assert is a safety net for development, not a runtime check.
template<typename T, size_t N> inline void init_array_from(std::array<T, N> &dest, std::initializer_list<T> src) {
#ifdef ESPHOME_DEBUG
assert(src.size() == N);
#endif
if constexpr (std::is_trivially_copyable_v<T>) {
__builtin_memcpy(dest.data(), src.begin(), N * sizeof(T));
} else {
size_t i = 0;
for (const auto &v : src) {
dest[i++] = v;
}
}
}
/// Fixed-capacity vector - allocates once at runtime, never reallocates
/// This avoids std::vector template overhead (_M_realloc_insert, _M_default_append)
/// when size is known at initialization but not at compile time