Compare commits

..

2 Commits

Author SHA1 Message Date
J. Nick Koston
4293f8fe89 [core] Eliminate global constructor overhead for component vectors 2026-01-19 22:09:08 -10:00
Jonathan Swoboda
ed4ebffa74 [x9c] Fix potentiometer unable to decrement (#13382)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:57:54 -05:00
7 changed files with 17 additions and 56 deletions

View File

@@ -25,9 +25,7 @@ template<typename... X> class TemplatableStringValue : public TemplatableValue<s
private:
// Helper to convert value to string - handles the case where value is already a string
template<typename T> static std::string value_to_string(T &&val) {
return to_string(std::forward<T>(val)); // NOLINT
}
template<typename T> static std::string value_to_string(T &&val) { return to_string(std::forward<T>(val)); }
// Overloads for string types - needed because std::to_string doesn't support them
static std::string value_to_string(char *val) {

View File

@@ -48,7 +48,7 @@ class ESPBTUUID {
// Remove before 2026.8.0
ESPDEPRECATED("Use to_str() instead. Removed in 2026.8.0", "2026.2.0")
std::string to_string() const; // NOLINT
std::string to_string() const;
const char *to_str(std::span<char, UUID_STR_LEN> output) const;
protected:

View File

@@ -83,7 +83,7 @@ struct Timer {
}
// Remove before 2026.8.0
ESPDEPRECATED("Use to_str() instead. Removed in 2026.8.0", "2026.2.0")
std::string to_string() const { // NOLINT
std::string to_string() const {
char buffer[TO_STR_BUFFER_SIZE];
return this->to_str(buffer);
}

View File

@@ -6,7 +6,7 @@ namespace x9c {
static const char *const TAG = "x9c.output";
void X9cOutput::trim_value(int change_amount) {
void X9cOutput::trim_value(int32_t change_amount) {
if (change_amount == 0) {
return;
}
@@ -47,17 +47,17 @@ void X9cOutput::setup() {
if (this->initial_value_ <= 0.50) {
this->trim_value(-101); // Set min value (beyond 0)
this->trim_value(static_cast<uint32_t>(roundf(this->initial_value_ * 100)));
this->trim_value(lroundf(this->initial_value_ * 100));
} else {
this->trim_value(101); // Set max value (beyond 100)
this->trim_value(static_cast<uint32_t>(roundf(this->initial_value_ * 100) - 100));
this->trim_value(lroundf(this->initial_value_ * 100) - 100);
}
this->pot_value_ = this->initial_value_;
this->write_state(this->initial_value_);
}
void X9cOutput::write_state(float state) {
this->trim_value(static_cast<uint32_t>(roundf((state - this->pot_value_) * 100)));
this->trim_value(lroundf((state - this->pot_value_) * 100));
this->pot_value_ = state;
}

View File

@@ -18,7 +18,7 @@ class X9cOutput : public output::FloatOutput, public Component {
void setup() override;
void dump_config() override;
void trim_value(int change_amount);
void trim_value(int32_t change_amount);
protected:
void write_state(float state) override;

View File

@@ -47,18 +47,21 @@ struct ComponentPriorityOverride {
};
// Error messages for failed components
// Using raw pointer instead of unique_ptr to avoid global constructor/destructor overhead
// This is never freed as error messages persist for the lifetime of the device
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::unique_ptr<std::vector<ComponentErrorMessage>> component_error_messages;
std::vector<ComponentErrorMessage> *component_error_messages = nullptr;
// Setup priority overrides - freed after setup completes
// Using raw pointer instead of unique_ptr to avoid global constructor/destructor overhead
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::unique_ptr<std::vector<ComponentPriorityOverride>> setup_priority_overrides;
std::vector<ComponentPriorityOverride> *setup_priority_overrides = nullptr;
// Helper to store error messages - reduces duplication between deprecated and new API
// Remove before 2026.6.0 when deprecated const char* API is removed
void store_component_error_message(const Component *component, const char *message, bool is_flash_ptr) {
// Lazy allocate the error messages vector if needed
if (!component_error_messages) {
component_error_messages = std::make_unique<std::vector<ComponentErrorMessage>>();
component_error_messages = new std::vector<ComponentErrorMessage>();
}
// Check if this component already has an error message
for (auto &entry : *component_error_messages) {
@@ -467,7 +470,7 @@ float Component::get_actual_setup_priority() const {
void Component::set_setup_priority(float priority) {
// Lazy allocate the vector if needed
if (!setup_priority_overrides) {
setup_priority_overrides = std::make_unique<std::vector<ComponentPriorityOverride>>();
setup_priority_overrides = new std::vector<ComponentPriorityOverride>();
// Reserve some space to avoid reallocations (most configs have < 10 overrides)
setup_priority_overrides->reserve(10);
}
@@ -553,7 +556,8 @@ WarnIfComponentBlockingGuard::~WarnIfComponentBlockingGuard() {}
void clear_setup_priority_overrides() {
// Free the setup priority map completely
setup_priority_overrides.reset();
delete setup_priority_overrides;
setup_priority_overrides = nullptr;
}
} // namespace esphome

View File

@@ -732,47 +732,6 @@ def lint_no_heap_allocating_helpers(fname, match):
)
@lint_re_check(
# Match std::to_string() or unqualified to_string() calls
# The esphome namespace has "using std::to_string;" so unqualified calls resolve to std::to_string
# Use negative lookbehind to avoid matching:
# - Function definitions: "const char *to_string(" or "std::string to_string("
# - Method definitions: "Class::to_string("
# - Method calls: ".to_string(" or "->to_string("
# - Other identifiers: "_to_string("
r"(?<![*&.\w>:])to_string\s*\(" + CPP_RE_EOL,
include=cpp_include,
exclude=[
# Vendored library
"esphome/components/http_request/httplib.h",
# Deprecated helpers that return std::string
"esphome/core/helpers.cpp",
# The using declaration itself
"esphome/core/helpers.h",
# Test fixtures - not production embedded code
"tests/integration/fixtures/*",
],
)
def lint_no_std_to_string(fname, match):
return (
f"{highlight('std::to_string()')} allocates heap memory. On long-running embedded "
f"devices, repeated heap allocations fragment memory over time.\n"
f"Please use {highlight('snprintf()')} with a stack buffer instead.\n"
f"\n"
f"Buffer sizes and format specifiers:\n"
f" uint8_t/int8_t: 4 chars - %u / %d (or PRIu8/PRId8)\n"
f" uint16_t/int16_t: 6 chars - %u / %d (or PRIu16/PRId16)\n"
f" uint32_t/int32_t: 11 chars - %" + "PRIu32 / %" + "PRId32\n"
" uint64_t/int64_t: 21 chars - %" + "PRIu64 / %" + "PRId64\n"
f" float/double: 24 chars - %.8g (15 digits + sign + decimal + e+XXX)\n"
f" 317 chars - %f (for DBL_MAX: 309 int digits + decimal + 6 frac + sign)\n"
f"\n"
f"For sensor values, use value_accuracy_to_buf() from helpers.h.\n"
f'Example: char buf[11]; snprintf(buf, sizeof(buf), "%" PRIu32, value);\n'
f"(If strictly necessary, add `{highlight('// NOLINT')}` to the end of the line)"
)
@lint_content_find_check(
"ESP_LOG",
include=["*.h", "*.tcc"],