|
| 1 | +--- |
| 2 | +date: 2025-11-20 |
| 3 | +authors: |
| 4 | + - bdraco |
| 5 | +comments: true |
| 6 | +--- |
| 7 | + |
| 8 | +# Network get_use_address() Optimization |
| 9 | + |
| 10 | +The `network::get_use_address()` function has been optimized to return `const char*` instead of `const std::string&` to reduce memory overhead. This eliminates unnecessary string object storage when accessing the device's network address. |
| 11 | + |
| 12 | +This is a **breaking change** for external components that call `network::get_use_address()` in **ESPHome 2025.11.0 and later**. |
| 13 | + |
| 14 | +<!-- more --> |
| 15 | + |
| 16 | +## What needs to change |
| 17 | + |
| 18 | +External components must remove `.c_str()` calls when using `get_use_address()`: |
| 19 | + |
| 20 | +```cpp |
| 21 | +// ❌ Before 2025.11 |
| 22 | +ESP_LOGCONFIG(TAG, " Address: %s:%u", |
| 23 | + esphome::network::get_use_address().c_str(), this->port_); |
| 24 | + |
| 25 | +// ✅ After 2025.11 |
| 26 | +ESP_LOGCONFIG(TAG, " Address: %s:%u", |
| 27 | + esphome::network::get_use_address(), this->port_); |
| 28 | +``` |
| 29 | +
|
| 30 | +## Compilation errors |
| 31 | +
|
| 32 | +External components using `.c_str()` will fail with: |
| 33 | +
|
| 34 | +``` |
| 35 | +error: member reference base type 'const char *' is not a structure or union |
| 36 | + esphome::network::get_use_address().c_str() |
| 37 | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~ |
| 38 | +``` |
| 39 | +
|
| 40 | +The fix is simple: remove `.c_str()`. |
| 41 | +
|
| 42 | +## Common usage patterns |
| 43 | +
|
| 44 | +### Logging (most common) |
| 45 | +
|
| 46 | +```cpp |
| 47 | +// Before 2025.11 |
| 48 | +ESP_LOGD(TAG, "Connecting to %s", |
| 49 | + esphome::network::get_use_address().c_str()); |
| 50 | +
|
| 51 | +// After 2025.11 |
| 52 | +ESP_LOGD(TAG, "Connecting to %s", |
| 53 | + esphome::network::get_use_address()); |
| 54 | +``` |
| 55 | +
|
| 56 | +### Storing the address |
| 57 | +
|
| 58 | +If you need to store the address in a `std::string`: |
| 59 | +
|
| 60 | +```cpp |
| 61 | +// Before 2025.11 |
| 62 | +std::string address = esphome::network::get_use_address(); |
| 63 | +
|
| 64 | +// After 2025.11 (still works - implicit conversion) |
| 65 | +std::string address = esphome::network::get_use_address(); |
| 66 | +
|
| 67 | +// After 2025.11 (more efficient if you don't need mutation) |
| 68 | +const char *address = esphome::network::get_use_address(); |
| 69 | +``` |
| 70 | +
|
| 71 | +### Passing to functions |
| 72 | +
|
| 73 | +```cpp |
| 74 | +// Before 2025.11 |
| 75 | +this->client_->connect(esphome::network::get_use_address().c_str(), port); |
| 76 | +
|
| 77 | +// After 2025.11 |
| 78 | +this->client_->connect(esphome::network::get_use_address(), port); |
| 79 | +``` |
| 80 | +
|
| 81 | +## Supporting multiple ESPHome versions |
| 82 | +
|
| 83 | +If your external component needs to support both old and new ESPHome versions: |
| 84 | +
|
| 85 | +```cpp |
| 86 | +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) |
| 87 | + ESP_LOGCONFIG(TAG, " Address: %s:%u", |
| 88 | + esphome::network::get_use_address(), this->port_); |
| 89 | +#else |
| 90 | + ESP_LOGCONFIG(TAG, " Address: %s:%u", |
| 91 | + esphome::network::get_use_address().c_str(), this->port_); |
| 92 | +#endif |
| 93 | +``` |
| 94 | +
|
| 95 | +### Cleaner version with macro |
| 96 | +
|
| 97 | +For components with many uses: |
| 98 | +
|
| 99 | +```cpp |
| 100 | +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) |
| 101 | + #define GET_USE_ADDRESS() esphome::network::get_use_address() |
| 102 | +#else |
| 103 | + #define GET_USE_ADDRESS() esphome::network::get_use_address().c_str() |
| 104 | +#endif |
| 105 | +
|
| 106 | +// Then use throughout your code: |
| 107 | +ESP_LOGCONFIG(TAG, " Address: %s:%u", GET_USE_ADDRESS(), this->port_); |
| 108 | +``` |
| 109 | +
|
| 110 | +## Affected components |
| 111 | +
|
| 112 | +This change affects the following network components: |
| 113 | +
|
| 114 | +- `wifi` - `wifi::get_use_address()` |
| 115 | +- `ethernet` - `ethernet::get_use_address()` |
| 116 | +- `openthread` - `openthread::get_use_address()` |
| 117 | +
|
| 118 | +All three now return `const char*` instead of `const std::string&`. |
| 119 | +
|
| 120 | +> **Note:** `esphome::network::get_use_address()` is a wrapper function that delegates to the appropriate component-specific function (`wifi::get_use_address()`, `ethernet::get_use_address()`, or `openthread::get_use_address()`) depending on your network configuration. Most external components should use the `network::` wrapper for generic code. Only use component-specific functions if your code explicitly depends on a particular network type. |
| 121 | +
|
| 122 | +## Why this change |
| 123 | +
|
| 124 | +The previous implementation stored the address internally as a `std::string` and returned a reference to it. This meant: |
| 125 | +
|
| 126 | +1. **Unnecessary string object overhead** - The address was stored as a `std::string`, even though it is typically a static configuration value set once during setup |
| 127 | +2. **Extra memory usage** - The `std::string` object itself uses heap memory plus object overhead (typically 24-32 bytes) |
| 128 | +3. **API complexity** - Callers needed to use `.c_str()` to access the raw address for logging, even though no conversion or copy was performed |
| 129 | +
|
| 130 | +By returning `const char*` directly and storing the address as a pointer to flash memory (RODATA): |
| 131 | +
|
| 132 | +- **Zero string object overhead** - No `std::string` objects are created or stored for the address |
| 133 | +- **Simpler API** - The address can be used directly in logging and C-style string functions |
| 134 | +- **Memory savings** - Eliminates the `std::string` overhead, saving 32-72 bytes of RAM per network component |
| 135 | +
|
| 136 | +## Real-world example |
| 137 | +
|
| 138 | +Here's the fix applied to [esphome-stream-server](https://github.com/oxan/esphome-stream-server/pull/58): |
| 139 | +
|
| 140 | +```cpp |
| 141 | +void StreamServerComponent::dump_config() { |
| 142 | + ESP_LOGCONFIG(TAG, "Stream Server:"); |
| 143 | +#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0) |
| 144 | + ESP_LOGCONFIG(TAG, " Address: %s:%u", |
| 145 | + esphome::network::get_use_address(), this->port_); |
| 146 | +#else |
| 147 | + ESP_LOGCONFIG(TAG, " Address: %s:%u", |
| 148 | + esphome::network::get_use_address().c_str(), this->port_); |
| 149 | +#endif |
| 150 | + // ... rest of dump_config |
| 151 | +} |
| 152 | +``` |
| 153 | +
|
| 154 | +## Finding code that needs updates |
| 155 | +
|
| 156 | +```bash |
| 157 | +# Find uses of get_use_address().c_str() |
| 158 | +grep -rn "get_use_address()\.c_str()" your_component/ |
| 159 | +
|
| 160 | +# Check for all get_use_address() calls to review |
| 161 | +grep -rn "get_use_address()" your_component/ |
| 162 | +``` |
| 163 | +
|
| 164 | +## Reference Pull Request |
| 165 | +
|
| 166 | +- [esphome/esphome#11707](https://github.com/esphome/esphome/pull/11707) |
| 167 | +
|
| 168 | +## Questions? |
| 169 | +
|
| 170 | +If you have questions about migrating your external component, please ask in: |
| 171 | +
|
| 172 | +- [ESPHome Discord](https://discord.gg/KhAMKrd) - #devs channel |
| 173 | +- [ESPHome GitHub Discussions](https://github.com/esphome/esphome/discussions) |
0 commit comments