mirror of
https://github.com/esphome/esphome.git
synced 2026-07-11 01:15:33 +00:00
[core] Add TemplatableFn and TemplatableValue (value+fn) dual storage
- TemplatableFn<T, X...>: 4-byte function-pointer-only storage, used by TEMPLATABLE_VALUE macro for codegen-managed fields - TemplatableValue<T, X...>: 8-byte value-or-function-pointer storage, backward compatible with raw constant init and assignment - Revert api/user_services.h to original (uses TemplatableValue) - Optimize sensor/number min_/max_ to TemplatableFn - Optimize mdns port to TemplatableFn with lambda wrappers
This commit is contained in:
@@ -275,7 +275,7 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
|
||||
|
||||
protected:
|
||||
APIServer *parent_;
|
||||
TemplatableValue<bool, Ts...> success_{[](Ts...) -> bool { return true; }};
|
||||
TemplatableValue<bool, Ts...> success_{true};
|
||||
TemplatableValue<std::string, Ts...> error_message_{""};
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
|
||||
std::function<void(Ts..., JsonObject)> json_builder_;
|
||||
|
||||
@@ -57,7 +57,7 @@ void MDNSComponent::compile_records_(StaticVector<MDNSService, MDNS_SERVICE_COUN
|
||||
auto &service = services.emplace_next();
|
||||
service.service_type = MDNS_STR(SERVICE_ESPHOMELIB);
|
||||
service.proto = MDNS_STR(SERVICE_TCP);
|
||||
service.port = api::global_api_server->get_port();
|
||||
service.port = []() -> uint16_t { return api::global_api_server->get_port(); };
|
||||
|
||||
const auto &friendly_name = App.get_friendly_name();
|
||||
bool friendly_name_empty = friendly_name.empty();
|
||||
@@ -151,7 +151,7 @@ void MDNSComponent::compile_records_(StaticVector<MDNSService, MDNS_SERVICE_COUN
|
||||
auto &prom_service = services.emplace_next();
|
||||
prom_service.service_type = MDNS_STR(SERVICE_PROMETHEUS);
|
||||
prom_service.proto = MDNS_STR(SERVICE_TCP);
|
||||
prom_service.port = USE_WEBSERVER_PORT;
|
||||
prom_service.port = []() -> uint16_t { return USE_WEBSERVER_PORT; };
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENDSPIN
|
||||
@@ -162,7 +162,7 @@ void MDNSComponent::compile_records_(StaticVector<MDNSService, MDNS_SERVICE_COUN
|
||||
auto &sendspin_service = services.emplace_next();
|
||||
sendspin_service.service_type = MDNS_STR(SERVICE_SENDSPIN);
|
||||
sendspin_service.proto = MDNS_STR(SERVICE_TCP);
|
||||
sendspin_service.port = USE_SENDSPIN_PORT;
|
||||
sendspin_service.port = []() -> uint16_t { return USE_SENDSPIN_PORT; };
|
||||
sendspin_service.txt_records = {{MDNS_STR(TXT_SENDSPIN_PATH), MDNS_STR(VALUE_SENDSPIN_PATH)}};
|
||||
#endif
|
||||
|
||||
@@ -172,7 +172,7 @@ void MDNSComponent::compile_records_(StaticVector<MDNSService, MDNS_SERVICE_COUN
|
||||
auto &web_service = services.emplace_next();
|
||||
web_service.service_type = MDNS_STR(SERVICE_HTTP);
|
||||
web_service.proto = MDNS_STR(SERVICE_TCP);
|
||||
web_service.port = USE_WEBSERVER_PORT;
|
||||
web_service.port = []() -> uint16_t { return USE_WEBSERVER_PORT; };
|
||||
#endif
|
||||
|
||||
#if !defined(USE_API) && !defined(USE_PROMETHEUS) && !defined(USE_SENDSPIN) && !defined(USE_WEBSERVER) && \
|
||||
@@ -185,7 +185,7 @@ void MDNSComponent::compile_records_(StaticVector<MDNSService, MDNS_SERVICE_COUN
|
||||
auto &fallback_service = services.emplace_next();
|
||||
fallback_service.service_type = MDNS_STR(SERVICE_HTTP);
|
||||
fallback_service.proto = MDNS_STR(SERVICE_TCP);
|
||||
fallback_service.port = USE_WEBSERVER_PORT;
|
||||
fallback_service.port = []() -> uint16_t { return USE_WEBSERVER_PORT; };
|
||||
fallback_service.txt_records = {{MDNS_STR(TXT_VERSION), MDNS_STR(VALUE_VERSION)}};
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ struct MDNSService {
|
||||
// second label indicating protocol _including_ underscore character prefix
|
||||
// as defined in RFC6763 Section 7, like "_tcp" or "_udp"
|
||||
const MDNSString *proto;
|
||||
TemplatableValue<uint16_t> port;
|
||||
TemplatableFn<uint16_t> port;
|
||||
FixedVector<MDNSTXTRecord> txt_records;
|
||||
};
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ class ValueRangeTrigger : public Trigger<float>, public Component {
|
||||
Number *parent_;
|
||||
ESPPreferenceObject rtc_;
|
||||
bool previous_in_range_{false};
|
||||
TemplatableValue<float, float> min_{[](float) -> float { return NAN; }}; // NAN = no bound
|
||||
TemplatableValue<float, float> max_{[](float) -> float { return NAN; }}; // NAN = no bound
|
||||
TemplatableFn<float, float> min_{[](float) -> float { return NAN; }};
|
||||
TemplatableFn<float, float> max_{[](float) -> float { return NAN; }};
|
||||
};
|
||||
|
||||
template<typename... Ts> class NumberInRangeCondition : public Condition<Ts...> {
|
||||
|
||||
@@ -79,8 +79,8 @@ class ValueRangeTrigger : public Trigger<float>, public Component {
|
||||
Sensor *parent_;
|
||||
ESPPreferenceObject rtc_;
|
||||
bool previous_in_range_{false};
|
||||
TemplatableValue<float, float> min_{[](float) -> float { return NAN; }};
|
||||
TemplatableValue<float, float> max_{[](float) -> float { return NAN; }};
|
||||
TemplatableFn<float, float> min_{[](float) -> float { return NAN; }};
|
||||
TemplatableFn<float, float> max_{[](float) -> float { return NAN; }};
|
||||
};
|
||||
|
||||
template<typename... Ts> class SensorInRangeCondition : public Condition<Ts...> {
|
||||
|
||||
+117
-17
@@ -34,28 +34,17 @@ template<int... S> struct gens<0, S...> { using type = seq<S...>; };
|
||||
#endif
|
||||
// NOLINTEND(readability-identifier-naming)
|
||||
|
||||
#define TEMPLATABLE_VALUE_(type, name) \
|
||||
protected: \
|
||||
TemplatableValue<type, Ts...> name##_{}; \
|
||||
\
|
||||
public: \
|
||||
template<typename V> void set_##name(V name) { this->name##_ = name; }
|
||||
|
||||
#define TEMPLATABLE_VALUE(type, name) TEMPLATABLE_VALUE_(type, name)
|
||||
|
||||
/// Primary template: function-pointer-only storage (4 bytes on 32-bit).
|
||||
/// Function-pointer-only templatable storage (4 bytes on 32-bit).
|
||||
/// Used by the TEMPLATABLE_VALUE macro for codegen-managed fields.
|
||||
/// Codegen wraps constants in stateless lambdas so only a function pointer is needed.
|
||||
/// Stateful lambdas (std::function) are rejected at compile time.
|
||||
template<typename T, typename... X> class TemplatableValue {
|
||||
template<typename T, typename... X> class TemplatableFn {
|
||||
public:
|
||||
TemplatableValue() = default;
|
||||
TemplatableFn() = default;
|
||||
|
||||
// Accept stateless lambdas (convertible to function pointer)
|
||||
template<typename F> TemplatableValue(F f) requires std::convertible_to<F, T (*)(X...)> : f_(f) {}
|
||||
template<typename F> TemplatableFn(F f) requires std::convertible_to<F, T (*)(X...)> : f_(f) {}
|
||||
|
||||
// Reject stateful lambdas at compile time
|
||||
template<typename F>
|
||||
TemplatableValue(F) requires std::invocable<F, X...> &&(!std::convertible_to<F, T (*)(X...)>) = delete;
|
||||
TemplatableFn(F) requires std::invocable<F, X...> &&(!std::convertible_to<F, T (*)(X...)>) = delete;
|
||||
|
||||
bool has_value() const { return this->f_ != nullptr; }
|
||||
|
||||
@@ -73,6 +62,117 @@ template<typename T, typename... X> class TemplatableValue {
|
||||
T (*f_)(X...){nullptr};
|
||||
};
|
||||
|
||||
#define TEMPLATABLE_VALUE_(type, name) \
|
||||
protected: \
|
||||
TemplatableFn<type, Ts...> name##_{}; \
|
||||
\
|
||||
public: \
|
||||
template<typename V> void set_##name(V name) { this->name##_ = name; }
|
||||
|
||||
#define TEMPLATABLE_VALUE(type, name) TEMPLATABLE_VALUE_(type, name)
|
||||
|
||||
/// Primary TemplatableValue: stores either a constant value or a function pointer.
|
||||
/// No std::function, no string-specific paths. 8 bytes on 32-bit.
|
||||
/// Accepts raw constants for backward compatibility with direct C++ usage.
|
||||
template<typename T, typename... X> class TemplatableValue {
|
||||
public:
|
||||
TemplatableValue() = default;
|
||||
|
||||
// Accept raw constants
|
||||
template<typename V> TemplatableValue(V value) requires(!std::invocable<V, X...>) : tag_(VALUE) {
|
||||
new (&this->value_) T(static_cast<T>(std::move(value)));
|
||||
}
|
||||
|
||||
// Accept stateless lambdas (convertible to function pointer)
|
||||
template<typename F> TemplatableValue(F f) requires std::convertible_to<F, T (*)(X...)> : tag_(FN) { this->f_ = f; }
|
||||
|
||||
// Reject stateful lambdas at compile time
|
||||
template<typename F>
|
||||
TemplatableValue(F) requires std::invocable<F, X...> &&(!std::convertible_to<F, T (*)(X...)>) = delete;
|
||||
|
||||
TemplatableValue(const TemplatableValue &other) : tag_(other.tag_) {
|
||||
if (this->tag_ == VALUE) {
|
||||
new (&this->value_) T(other.value_);
|
||||
} else if (this->tag_ == FN) {
|
||||
this->f_ = other.f_;
|
||||
}
|
||||
}
|
||||
|
||||
TemplatableValue(TemplatableValue &&other) noexcept : tag_(other.tag_) {
|
||||
if (this->tag_ == VALUE) {
|
||||
new (&this->value_) T(std::move(other.value_));
|
||||
} else if (this->tag_ == FN) {
|
||||
this->f_ = other.f_;
|
||||
}
|
||||
other.tag_ = NONE;
|
||||
}
|
||||
|
||||
TemplatableValue &operator=(const TemplatableValue &other) {
|
||||
if (this != &other) {
|
||||
this->destroy_();
|
||||
this->tag_ = other.tag_;
|
||||
if (this->tag_ == VALUE) {
|
||||
new (&this->value_) T(other.value_);
|
||||
} else if (this->tag_ == FN) {
|
||||
this->f_ = other.f_;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
TemplatableValue &operator=(TemplatableValue &&other) noexcept {
|
||||
if (this != &other) {
|
||||
this->destroy_();
|
||||
this->tag_ = other.tag_;
|
||||
if (this->tag_ == VALUE) {
|
||||
new (&this->value_) T(std::move(other.value_));
|
||||
} else if (this->tag_ == FN) {
|
||||
this->f_ = other.f_;
|
||||
}
|
||||
other.tag_ = NONE;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~TemplatableValue() { this->destroy_(); }
|
||||
|
||||
bool has_value() const { return this->tag_ != NONE; }
|
||||
|
||||
T value(X... x) const {
|
||||
if (this->tag_ == FN)
|
||||
return this->f_(x...);
|
||||
if (this->tag_ == VALUE)
|
||||
return this->value_;
|
||||
return T{};
|
||||
}
|
||||
|
||||
optional<T> optional_value(X... x) const {
|
||||
if (this->tag_ == NONE)
|
||||
return {};
|
||||
return this->value(x...);
|
||||
}
|
||||
|
||||
T value_or(X... x, T default_value) const {
|
||||
if (this->tag_ == NONE)
|
||||
return default_value;
|
||||
return this->value(x...);
|
||||
}
|
||||
|
||||
protected:
|
||||
void destroy_() {
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
if (this->tag_ == VALUE)
|
||||
this->value_.~T();
|
||||
}
|
||||
}
|
||||
|
||||
enum Tag : uint8_t { NONE, VALUE, FN } tag_{NONE};
|
||||
union {
|
||||
T value_;
|
||||
T (*f_)(X...);
|
||||
};
|
||||
};
|
||||
|
||||
/// Specialization for std::string: supports VALUE, STATIC_STRING, FLASH_STRING,
|
||||
/// stateless lambdas, and stateful lambdas (std::function).
|
||||
template<typename... X> class TemplatableValue<std::string, X...> {
|
||||
|
||||
Reference in New Issue
Block a user