mirror of
https://github.com/esphome/esphome.git
synced 2026-09-17 18:18:43 +00:00
[core] Fix TemplatableValue union for non-trivially-constructible types
Add explicit Storage union with trivial ctor/dtor so that TemplatableValue works with non-trivially-constructible types like std::vector<uint8_t>. The union's value_ lifetime is managed externally via placement new and destroy_(). Reverts esp32_ble_server back to TEMPLATABLE_VALUE macro.
This commit is contained in:
+23
-16
@@ -105,11 +105,13 @@ template<typename T, typename... X> class TemplatableValue {
|
||||
|
||||
// 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)));
|
||||
new (&this->storage_.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; }
|
||||
template<typename F> TemplatableValue(F f) requires std::convertible_to<F, T (*)(X...)> : tag_(FN) {
|
||||
this->storage_.f_ = f;
|
||||
}
|
||||
|
||||
// Convertible return type (e.g., int -> uint8_t) — casting trampoline
|
||||
template<typename F>
|
||||
@@ -117,7 +119,7 @@ template<typename T, typename... X> class TemplatableValue {
|
||||
"codegen")]] TemplatableValue(F) requires(!std::convertible_to<F, T (*)(X...)>) &&
|
||||
std::invocable<F, X...> &&std::convertible_to<std::invoke_result_t<F, X...>, T> &&std::is_empty_v<F>
|
||||
&&std::default_initializable<F> : tag_(FN) {
|
||||
this->f_ = [](X... x) -> T { return static_cast<T>(F{}(x...)); };
|
||||
this->storage_.f_ = [](X... x) -> T { return static_cast<T>(F{}(x...)); };
|
||||
}
|
||||
|
||||
// Reject any callable that didn't match the above
|
||||
@@ -129,18 +131,18 @@ template<typename T, typename... X> class TemplatableValue {
|
||||
|
||||
TemplatableValue(const TemplatableValue &other) : tag_(other.tag_) {
|
||||
if (this->tag_ == VALUE) {
|
||||
new (&this->value_) T(other.value_);
|
||||
new (&this->storage_.value_) T(other.storage_.value_);
|
||||
} else if (this->tag_ == FN) {
|
||||
this->f_ = other.f_;
|
||||
this->storage_.f_ = other.storage_.f_;
|
||||
}
|
||||
}
|
||||
|
||||
TemplatableValue(TemplatableValue &&other) noexcept : tag_(other.tag_) {
|
||||
if (this->tag_ == VALUE) {
|
||||
new (&this->value_) T(std::move(other.value_));
|
||||
new (&this->storage_.value_) T(std::move(other.storage_.value_));
|
||||
other.destroy_();
|
||||
} else if (this->tag_ == FN) {
|
||||
this->f_ = other.f_;
|
||||
this->storage_.f_ = other.storage_.f_;
|
||||
}
|
||||
other.tag_ = NONE;
|
||||
}
|
||||
@@ -150,9 +152,9 @@ template<typename T, typename... X> class TemplatableValue {
|
||||
this->destroy_();
|
||||
this->tag_ = other.tag_;
|
||||
if (this->tag_ == VALUE) {
|
||||
new (&this->value_) T(other.value_);
|
||||
new (&this->storage_.value_) T(other.storage_.value_);
|
||||
} else if (this->tag_ == FN) {
|
||||
this->f_ = other.f_;
|
||||
this->storage_.f_ = other.storage_.f_;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
@@ -163,10 +165,10 @@ template<typename T, typename... X> class TemplatableValue {
|
||||
this->destroy_();
|
||||
this->tag_ = other.tag_;
|
||||
if (this->tag_ == VALUE) {
|
||||
new (&this->value_) T(std::move(other.value_));
|
||||
new (&this->storage_.value_) T(std::move(other.storage_.value_));
|
||||
other.destroy_();
|
||||
} else if (this->tag_ == FN) {
|
||||
this->f_ = other.f_;
|
||||
this->storage_.f_ = other.storage_.f_;
|
||||
}
|
||||
other.tag_ = NONE;
|
||||
}
|
||||
@@ -179,9 +181,9 @@ template<typename T, typename... X> class TemplatableValue {
|
||||
|
||||
T value(X... x) const {
|
||||
if (this->tag_ == FN)
|
||||
return this->f_(x...);
|
||||
return this->storage_.f_(x...);
|
||||
if (this->tag_ == VALUE)
|
||||
return this->value_;
|
||||
return this->storage_.value_;
|
||||
return T{};
|
||||
}
|
||||
|
||||
@@ -201,15 +203,20 @@ template<typename T, typename... X> class TemplatableValue {
|
||||
void destroy_() {
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
if (this->tag_ == VALUE)
|
||||
this->value_.~T();
|
||||
this->storage_.value_.~T();
|
||||
}
|
||||
}
|
||||
|
||||
enum Tag : uint8_t { NONE, VALUE, FN } tag_{NONE};
|
||||
union {
|
||||
// Union with explicit ctor/dtor to support non-trivially-constructible/destructible T
|
||||
// (e.g., std::vector<uint8_t>). Lifetime of value_ is managed externally via
|
||||
// placement new and destroy_().
|
||||
union Storage {
|
||||
constexpr Storage() : f_(nullptr) {}
|
||||
constexpr ~Storage() {}
|
||||
T value_;
|
||||
T (*f_)(X...);
|
||||
};
|
||||
} storage_;
|
||||
};
|
||||
|
||||
/// Specialization for std::string: supports VALUE, STATIC_STRING, FLASH_STRING,
|
||||
|
||||
Reference in New Issue
Block a user