Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
cached_property.hpp
Go to the documentation of this file.
1#pragma once
2
3// CachedProperty — typed accessor for StateTree properties with local cache.
4// Avoids repeated variant extraction on every read. Updates cache on tree change.
5
7#include <string>
8#include <functional>
9
10namespace pulp::state {
11
14template<typename T>
16public:
17 CachedProperty() = default;
18
20 CachedProperty(StateTree::Ptr tree, std::string_view property_name, T default_value = T{})
21 : tree_(tree), property_name_(property_name), value_(default_value), default_(default_value) {
22 if (tree_) {
23 refresh();
24 listener_id_ = tree_->add_listener(
25 [this](StateTree&, std::string_view prop, const PropertyValue&, const PropertyValue& new_val) {
26 if (prop == property_name_)
27 update_from_variant(new_val);
28 });
29 }
30 }
31
33 if (tree_ && listener_id_ >= 0)
34 tree_->remove_listener(listener_id_);
35 }
36
38 const T& get() const { return value_; }
39 operator const T&() const { return value_; }
40
49 void set(const T& new_value) {
50 if (tree_ && value_ == new_value && tree_->has(property_name_))
51 return;
52 value_ = new_value;
53 if (tree_)
54 tree_->set(property_name_, PropertyValue(new_value));
55 }
56
57 CachedProperty& operator=(const T& new_value) { set(new_value); return *this; }
58
60 void refresh() {
61 if (!tree_) return;
62 auto val = tree_->get(property_name_);
63 update_from_variant(val);
64 }
65
67 bool is_bound() const { return tree_ != nullptr; }
68
69 // Move only (listener management)
73 : tree_(std::move(other.tree_)), property_name_(std::move(other.property_name_)),
74 value_(std::move(other.value_)), default_(std::move(other.default_)) {
75 // Remove old listener from the source and re-register on this object
76 if (tree_ && other.listener_id_ >= 0) {
77 tree_->remove_listener(other.listener_id_);
78 other.listener_id_ = -1;
79 listener_id_ = tree_->add_listener(
80 [this](StateTree&, std::string_view prop, const PropertyValue&, const PropertyValue& new_val) {
81 if (prop == property_name_)
82 update_from_variant(new_val);
83 });
84 }
85 }
86
87private:
88 StateTree::Ptr tree_;
89 std::string property_name_;
90 T value_{};
91 T default_{};
92 int listener_id_ = -1;
93
94 void update_from_variant(const PropertyValue& val);
95};
96
97// Whether the incoming variant means "the property is absent" — i.e. it was
98// removed from the tree (StateTree::remove notifies with an empty
99// PropertyValue{}) or never existed (StateTree::get of an unknown key returns
100// std::monostate). This is the ONLY signal for absence: a property that is
101// still present but holds an unexpected/uncoercible type (e.g. a string where
102// this cache wants a bool) is NOT absent, and the cache must keep its prior
103// value rather than reverting to the default.
104inline bool property_is_absent(const PropertyValue& val) {
105 return std::holds_alternative<std::monostate>(val);
106}
107
108// Specializations for common types. Each reverts to `default_` when the
109// property is absent (removed), applies the value on a type match (with the
110// documented numeric coercion for double), and otherwise leaves the cache
111// unchanged so a present-but-mismatched value does not clobber it.
112template<> inline void CachedProperty<bool>::update_from_variant(const PropertyValue& val) {
113 if (property_is_absent(val)) value_ = default_;
114 else if (auto* b = std::get_if<bool>(&val)) value_ = *b;
115}
116template<> inline void CachedProperty<int64_t>::update_from_variant(const PropertyValue& val) {
117 if (property_is_absent(val)) value_ = default_;
118 else if (auto* i = std::get_if<int64_t>(&val)) value_ = *i;
119}
120template<> inline void CachedProperty<double>::update_from_variant(const PropertyValue& val) {
121 if (property_is_absent(val)) value_ = default_;
122 else if (auto* d = std::get_if<double>(&val)) value_ = *d;
123 else if (auto* i = std::get_if<int64_t>(&val)) value_ = static_cast<double>(*i);
124}
125template<> inline void CachedProperty<std::string>::update_from_variant(const PropertyValue& val) {
126 if (property_is_absent(val)) value_ = default_;
127 else if (auto* s = std::get_if<std::string>(&val)) value_ = *s;
128}
129
130} // namespace pulp::state
void refresh()
Force refresh from the tree.
Definition cached_property.hpp:60
CachedProperty & operator=(const CachedProperty &)=delete
const T & get() const
Get the cached value (no variant extraction — fast).
Definition cached_property.hpp:38
~CachedProperty()
Definition cached_property.hpp:32
bool is_bound() const
Whether bound to a tree.
Definition cached_property.hpp:67
CachedProperty(CachedProperty &&other) noexcept
Definition cached_property.hpp:72
CachedProperty(StateTree::Ptr tree, std::string_view property_name, T default_value=T{})
Bind to a tree node and property name.
Definition cached_property.hpp:20
CachedProperty(const CachedProperty &)=delete
CachedProperty & operator=(const T &new_value)
Definition cached_property.hpp:57
void set(const T &new_value)
Set the value (updates both cache and tree).
Definition cached_property.hpp:49
Reactive tree node — each node has a type name, properties, and children.
Definition state_tree.hpp:293
std::shared_ptr< StateTree > Ptr
Definition state_tree.hpp:295
@ d
Definition pitch.hpp:22
SequencerStateChannel is the audio-safe transport for a plugin's structured NON-parameter state: the ...
Definition audio_device_manager.hpp:48
std::variant< std::monostate, bool, int64_t, double, std::string, PropertyArrayValue, PropertyObjectValue > PropertyValue
Property value — can hold scalar values plus immutable recursive arrays and string-keyed objects.
Definition state_tree.hpp:66
bool property_is_absent(const PropertyValue &val)
Definition cached_property.hpp:104
@ s
Definition input_events.hpp:41