-
Notifications
You must be signed in to change notification settings - Fork 0
Properties
serp::Property<T> is a typed, observable state variable. It holds a single value of type T and notifies subscribers whenever that value changes. Properties model persistent state — unlike notifications, subscribers can always read the current value.
Properties are declared in the generated service interface and managed by the owning service. Cross-service and cross-process access uses the same API.
template<typename T>
class serp::Property;Properties are typically declared as members of the generated IService interface class:
serp::Property<double> temperature;
serp::Property<bool> isConnected;
serp::Property<std::vector<std::string>> playlist;get() returns the current value synchronously. It is safe to call from any thread:
double current = temperature.get();For complex types, prefer reading the value inside a subscription callback to avoid stale data.
Only the owning service may write to a property. Use mutate() to set a new value:
temperature.mutate(22.5);mutate() dispatches the write to the owning service's EventLoop and then notifies all subscribers. If called from the owner's own thread, the dispatch is immediate. If called from another thread, the write is queued.
Subscribers are notified in the order they connected. Each subscriber receives the callback on its own EventLoop.
Use connect() to register a lambda that is called whenever the property value changes:
temperature.connect([this](double val) {
updateDisplay(val);
});The lambda is called on the subscriber's EventLoop, not the owner's. This means:
- No cross-thread races in the subscriber's handler.
- The callback may be delayed if the subscriber's EventLoop is busy.
connect() returns a connection handle. Destroying the handle disconnects the subscription:
auto conn = temperature.connect([](double val) { /* ... */ });
// conn goes out of scope → subscription is removedTo keep a subscription alive for the lifetime of the service, store the handle as a member.
Properties declared as readonly in SerpIDL can only be written by the owning service. External callers can read and subscribe but cannot call mutate(). The generated interface enforces this at compile time:
// In IMyService (generated):
serp::Property<double> temperature; // readwrite — callers may mutate
serp::ReadonlyProperty<int> errorCount; // readonly — only owner writes// Interface (generated in IMyService)
serp::Property<double> temperature;
// Owner service — writing
class ThermostatService : public ThermostatServiceBase {
protected:
void onInitialize() override {
m_pollTimer = serp::Timer::create(eventLoop(), std::chrono::seconds(5), [this]() {
double reading = m_sensor.read();
temperature.mutate(reading); // dispatch write on owner's EventLoop
});
m_pollTimer->start();
}
};
// Consumer service — reading and subscribing
class DisplayService : public DisplayServiceBase {
protected:
void onInitialize() override {
// Read current value immediately
double initial = m_thermostat.temperature.get();
showTemperature(initial);
// Subscribe to future changes
m_tempConn = m_thermostat.temperature.connect([this](double val) {
// Called on DisplayService's EventLoop
showTemperature(val);
});
}
private:
serp::Connection m_tempConn;
std::shared_ptr<serp::Timer> m_pollTimer;
};| Feature | serp::Property<T> |
serp::Notification<Args...> |
|---|---|---|
| Holds current value | Yes — get() always works |
No — event only |
| Subscriber gets current value on connect | Implementation-defined | No |
| Can fire multiple times | Yes, on each change | Yes, each fire() call |
| Models | Persistent state | Transient event |
Use a Property when consumers need to know the current state at any time. Use a Notification for events that have no persistent representation.
When a service is hosted remotely, the proxy object exposes the same serp::Property<T> API. The transport layer synchronizes the value and delivers change notifications across the process boundary. No changes to consumer code are required.
- Services and Lifecycle — EventLoop binding and thread safety
- Notifications — one-shot event broadcasts
- Methods — typed inter-service calls
Getting Started
The Development Model
Architecture Language
Code Generator
- Generator Overview
- Generated Code Layout
- Deployment Configurations
- Lifecycle Backends
- CMake Integration
Framework Internals
- Core Concepts
- Services & Lifecycle
- Methods
- Properties
- Notifications
- Timers & Watchdog
- Promises & Async
- Streams
- Commands
- Logging
- Test Engine
- Transports
- Runtime & Debug Tools
VS Code Plugin
Examples