Use leak-on-purpose raw pointers for Climate-owned vectors

Climate entities live for the entire program lifetime, so the custom
mode vectors never need to be freed. Use raw pointers (null by default,
allocated on first set_supported_custom_*() call) instead of inline
std::vector members.

This saves 48 bytes of RAM per Climate instance (two empty vectors)
for components that don't use custom modes (bang_bang, pid, etc.),
and avoids any copy overhead in get_traits().
This commit is contained in:
J. Nick Koston
2026-03-26 13:22:46 -10:00
parent ba57555b6f
commit 805bc4c9a8
2 changed files with 29 additions and 15 deletions
+7 -6
View File
@@ -485,10 +485,10 @@ void Climate::publish_state() {
ClimateTraits Climate::get_traits() {
auto traits = this->traits();
// Wire custom mode pointers from Climate-owned storage
if (!this->supported_custom_fan_modes_.empty())
traits.set_supported_custom_fan_modes(&this->supported_custom_fan_modes_);
if (!this->supported_custom_presets_.empty())
traits.set_supported_custom_presets(&this->supported_custom_presets_);
if (this->supported_custom_fan_modes_)
traits.set_supported_custom_fan_modes(this->supported_custom_fan_modes_);
if (this->supported_custom_presets_)
traits.set_supported_custom_presets(this->supported_custom_presets_);
#ifdef USE_CLIMATE_VISUAL_OVERRIDES
if (!std::isnan(this->visual_min_temperature_override_)) {
traits.set_visual_min_temperature(this->visual_min_temperature_override_);
@@ -706,7 +706,8 @@ const char *Climate::find_custom_fan_mode_(const char *custom_fan_mode) {
}
const char *Climate::find_custom_fan_mode_(const char *custom_fan_mode, size_t len) {
return vector_find(this->supported_custom_fan_modes_, custom_fan_mode, len);
return this->supported_custom_fan_modes_ ? vector_find(*this->supported_custom_fan_modes_, custom_fan_mode, len)
: nullptr;
}
const char *Climate::find_custom_preset_(const char *custom_preset) {
@@ -714,7 +715,7 @@ const char *Climate::find_custom_preset_(const char *custom_preset) {
}
const char *Climate::find_custom_preset_(const char *custom_preset, size_t len) {
return vector_find(this->supported_custom_presets_, custom_preset, len);
return this->supported_custom_presets_ ? vector_find(*this->supported_custom_presets_, custom_preset, len) : nullptr;
}
void Climate::dump_traits_(const char *tag) {
+22 -9
View File
@@ -236,24 +236,37 @@ class Climate : public EntityBase {
/// Set the supported custom fan modes (stored on Climate, referenced by ClimateTraits).
void set_supported_custom_fan_modes(std::initializer_list<const char *> modes) {
this->supported_custom_fan_modes_ = modes;
if (!this->supported_custom_fan_modes_)
this->supported_custom_fan_modes_ =
new std::vector<const char *>(); // NOLINT - intentional leak, entity lives forever
*this->supported_custom_fan_modes_ = modes;
}
void set_supported_custom_fan_modes(const std::vector<const char *> &modes) {
this->supported_custom_fan_modes_ = modes;
if (!this->supported_custom_fan_modes_)
this->supported_custom_fan_modes_ = new std::vector<const char *>(); // NOLINT
*this->supported_custom_fan_modes_ = modes;
}
template<size_t N> void set_supported_custom_fan_modes(const char *const (&modes)[N]) {
this->supported_custom_fan_modes_.assign(modes, modes + N);
if (!this->supported_custom_fan_modes_)
this->supported_custom_fan_modes_ = new std::vector<const char *>(); // NOLINT
this->supported_custom_fan_modes_->assign(modes, modes + N);
}
/// Set the supported custom presets (stored on Climate, referenced by ClimateTraits).
void set_supported_custom_presets(std::initializer_list<const char *> presets) {
this->supported_custom_presets_ = presets;
if (!this->supported_custom_presets_)
this->supported_custom_presets_ = new std::vector<const char *>(); // NOLINT
*this->supported_custom_presets_ = presets;
}
void set_supported_custom_presets(const std::vector<const char *> &presets) {
this->supported_custom_presets_ = presets;
if (!this->supported_custom_presets_)
this->supported_custom_presets_ = new std::vector<const char *>(); // NOLINT
*this->supported_custom_presets_ = presets;
}
template<size_t N> void set_supported_custom_presets(const char *const (&presets)[N]) {
this->supported_custom_presets_.assign(presets, presets + N);
if (!this->supported_custom_presets_)
this->supported_custom_presets_ = new std::vector<const char *>(); // NOLINT
this->supported_custom_presets_->assign(presets, presets + N);
}
/// Check if a custom fan mode is currently active.
@@ -366,11 +379,11 @@ class Climate : public EntityBase {
LazyCallbackManager<void(ClimateCall &)> control_callback_{};
ESPPreferenceObject rtc_;
/** Custom mode storage - owned by Climate, referenced by ClimateTraits via pointer.
/** Custom mode storage — allocated on first use, never freed (entity lives forever).
* Pointers in these vectors must point to string literals or static data.
*/
std::vector<const char *> supported_custom_fan_modes_;
std::vector<const char *> supported_custom_presets_;
std::vector<const char *> *supported_custom_fan_modes_{nullptr};
std::vector<const char *> *supported_custom_presets_{nullptr};
#ifdef USE_CLIMATE_VISUAL_OVERRIDES
float visual_min_temperature_override_{NAN};