-
Notifications
You must be signed in to change notification settings - Fork 0
Notifications
serp::Notification<Args...> is a typed, multi-subscriber event broadcast primitive. When a service fires a notification, all connected subscribers are called with the provided arguments — each on their own EventLoop.
Notifications model transient events: things that happen at a point in time and carry no persistent state. For state that persists between events, use serp::Property<T> instead.
In SerpIDL, notifications are declared as broadcasts.
template<typename... Args>
class serp::Notification;Notifications are declared in the generated service interface:
serp::Notification<std::string, int> trackChanged;
serp::Notification<> shutdownRequested;
serp::Notification<ErrorCode, std::string> errorOccurred;Args... may be empty (for a signal with no payload), a single type, or a parameter pack of multiple types.
The owning service calls fire() to emit the notification to all current subscribers:
trackChanged.fire("song.mp3", 42);fire() is synchronous on the caller's thread — it enqueues the event to each subscriber's EventLoop and returns immediately. There is no blocking.
Any service or object may call connect() to receive the notification:
trackChanged.connect([this](std::string track, int id) {
updateNowPlaying(track, id);
});The lambda is called on the subscriber's EventLoop. Subscribers do not need to guard against data races on their own state.
connect() returns a connection handle. The subscription remains active as long as the handle is alive:
auto conn = trackChanged.connect([](std::string track, int id) { /* ... */ });
// Destroying conn → unsubscribesStore the handle as a member to keep the subscription for the lifetime of the subscriber:
class DisplayService : public DisplayServiceBase {
private:
serp::Connection m_trackConn;
};// Interface declaration (generated in IPlayerService)
serp::Notification<std::string, int> trackChanged;
// Publisher — PlayerService fires when the track changes
class PlayerService : public PlayerServiceBase {
void onTrackLoaded(std::string path, int id) {
// ... internal state update ...
trackChanged.fire(path, id);
}
};
// Subscriber — DisplayService listens
class DisplayService : public DisplayServiceBase {
protected:
void onInitialize() override {
m_trackConn = m_player.trackChanged.connect([this](std::string track, int id) {
// Called on DisplayService's EventLoop
showTrack(track, id);
});
}
private:
serp::Connection m_trackConn;
};| Feature | serp::Notification<Args...> |
serp::Property<T> |
|---|---|---|
| Carries current state | No | Yes — get() returns current value |
| Late subscribers see past events | No | Yes (current value) |
| Multiple arguments | Yes — variadic | No — single T
|
| Models | Transient event | Persistent state |
| SerpIDL keyword | broadcast |
property |
Rule of thumb: if a consumer needs to know "what is the current value?" use a Property. If they only need to react to "something just happened", use a Notification.
Notifications with no payload are valid and useful as pure signals:
// Declaration
serp::Notification<> shutdownRequested;
// Fire
shutdownRequested.fire();
// Subscribe
shutdownRequested.connect([this]() {
beginGracefulShutdown();
});When the publishing service is hosted in a remote process, the proxy object exposes the same serp::Notification API. The transport layer delivers fire() calls to all remote subscribers. Subscriber code requires no changes.
The SerpIDL code generator exposes notifications as typed members of the interface class:
// Generated IPlayerService
class IPlayerService {
public:
serp::Call<PlayerService, void, std::string> play;
serp::Property<bool> isPlaying;
serp::Notification<std::string, int> trackChanged; // broadcast
serp::Notification<ErrorCode> playbackError; // broadcast
};The generator derives the argument types from the IDL broadcast declaration. Subscribers get full type-checked access with no manual casting.
- Properties — persistent typed state with change notification
- Methods — typed inter-service calls
- Services and Lifecycle — EventLoop threading model
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