Skip to content

Commit 517968a

Browse files
committed
Added BasicObservers tutorial.
1 parent 1826650 commit 517968a

3 files changed

Lines changed: 162 additions & 2 deletions

File tree

tutorials/BasicEvents.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ EventSourceT<string> mySource = MakeEventSource<D,string>();
3232
{% endhighlight %}
3333
`EventSource` and `Events` are the respective counterparts of `VarSignal` and `Signal`.
3434

35-
Analogously to VarSignalT<S>, `EventSourceT<E>` is an alias for `EventSource<D,E>` defined by `USING_REACTIVE_DOMAIN`.
35+
Analogously to `VarSignalT<S>`, `EventSourceT<E>` is an alias for `EventSource<D,E>` defined by `USING_REACTIVE_DOMAIN`.
3636

3737
Unlike signals, event streams are purely push-based.
3838
They forward values to be processed, but don't hold on to them.

tutorials/BasicObservers.md

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
layout: default
3+
title: Observer basics
4+
groups:
5+
- {name: Home, url: ''}
6+
- {name: Tutorials , url: 'tutorials/'}
7+
---
8+
9+
- [Creating subject-bound observers](#creating-subject-bound-observers)
10+
- [Detaching observers manually](#detach-observers-manually)
11+
- [Using scoped observers](#using-scoped-observers)
12+
- [Observing temporary reactives](#observing-temporary-reactives)
13+
14+
## Creating subject-bound observers
15+
16+
As mentioned in the previous examples, by default observers are bound to the lifetime of the observed subject.
17+
Here's another demonstration of this behaviour:
18+
{% highlight C++ %}
19+
#include "react/Domain.h"
20+
#include "react/Event.h"
21+
#include "react/Observer.h"
22+
23+
REACTIVE_DOMAIN(D, sequential)
24+
USING_REACTIVE_DOMAIN(D)
25+
26+
void testFunc()
27+
{
28+
auto trigger = MakeEventSource<D>();
29+
30+
Observe(trigger, [] {
31+
// ...
32+
});
33+
}
34+
{% endhighlight %}
35+
After leaving `testFunc`, `trigger` is destroyed.
36+
This automatically detaches and destroys the observer as well.
37+
We don't have to worry about resource leaks or that the existence of an observer prevents subject from being destroyed.
38+
39+
40+
## Detaching observers manually
41+
42+
Alternatively, observers can be detached explicitly before the lifetime of their subject ends.
43+
We can either do this externally, or from inside the observer function.
44+
45+
The former is demonstrated here:
46+
{% highlight C++ %}
47+
void testFunc()
48+
{
49+
auto trigger = MakeEventSource<D>();
50+
51+
ObserverT obs = Observe(trigger, [] (Token) {
52+
cout << "Triggered" << endl;
53+
});
54+
55+
trigger.Emit(); // output: Triggered
56+
57+
obs.Detach(); // Remove the observer
58+
59+
trigger.Emit(); // no output
60+
}
61+
{% endhighlight %}
62+
63+
`ObserverT` is an alias for `Observer<D>` defined by `USING_REACTIVE_DOMAIN(D)`.
64+
The observer handle returned by `Observe` is stored and by calling its `Detach` member function, the underlying observer node is destroyed and the handle becomes invalid.
65+
66+
While it exists and has not been invalidated, an observer handle also takes shared ownership of the observed subject, i.e. by saving it, we state our continued interest in subject.
67+
This behaviour comes in handy later.
68+
69+
To detach an observer from the inside, the return value of the observer function is changed from `void` to `ObserverAction`:
70+
{% highlight C++ %}
71+
void testFunc()
72+
{
73+
auto source = MakeEventSource<D,int>();
74+
75+
Observe(trigger, [] (int v) -> ObserverAction {
76+
if (v != 0)
77+
{
78+
cout << v << endl;
79+
return ObserverAction::next;
80+
}
81+
else
82+
{
83+
cout << "Detached" << endl;
84+
return ObserverAction::stop_and_detach;
85+
}
86+
});
87+
88+
source << 3 << 2 << 1 << 0;
89+
// output: 3 2 1 Detached
90+
91+
source << 4;
92+
// no output
93+
}
94+
{% endhighlight %}
95+
96+
97+
## Using scoped observers
98+
99+
Instead of calling `Detach` manually, we can utilize the common RAII idiom and transfer ownership of the observer handle to a scope guard:
100+
101+
{% highlight C++ %}
102+
void testFunc()
103+
{
104+
auto trigger = MakeEventSource<D>();
105+
106+
// Start inner scope
107+
{
108+
ScopedObserverT scopedObs
109+
(
110+
Observe(trigger, [] (Token) {
111+
cout << "Triggered" << endl;
112+
})
113+
);
114+
115+
trigger.Emit();
116+
// output: Triggered
117+
}
118+
// End inner scope
119+
120+
trigger.Emit();
121+
// no output
122+
}
123+
{% endhighlight %}
124+
125+
The observer is detached in the destructor of `ScopedObserverT`.
126+
127+
## Observing temporary reactives
128+
129+
One problem of tying the lifetime of the observer to its subject manifests when attempting to do the following:
130+
{% highlight C++ %}
131+
void testFunc()
132+
{
133+
auto e1 = MakeEventSource<D>();
134+
auto e2 = MakeEventSource<D>();
135+
136+
Observe(Merge(e1, e2), [] (Token) {
137+
cout << "Triggered!" << endl;
138+
});
139+
140+
e1.Emit(); // no output
141+
e2.Emit(); // no output
142+
}
143+
{% endhighlight %}
144+
The reason this produces no output is that the result of `Merge(e1, e2)` is destroyed after the call, as nobody takes ownership of it.
145+
Consequently, the observer is destroyed as well.
146+
147+
To prevent this, the merged event stream could be kept in variable.
148+
Alternatively, we can make use of the fact that the observer handle takes ownership of its subject:
149+
{% highlight C++ %}
150+
auto obs = Observe(Merge(e1, e2), [] (Token) {
151+
cout << "Triggered" << endl;
152+
});
153+
154+
e1.Emit(); // output: Triggered
155+
e2.Emit(); // output: Triggered
156+
{% endhighlight %}

tutorials/index.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@ groups:
66
---
77
## Basics
88

9-
### [Signals]({{ site.baseurl }}/tutorials/BasicSignals.html)
9+
### [Signals]({{ site.baseurl }}/tutorials/BasicSignals.html)
10+
11+
### [Event streams]({{ site.baseurl }}/tutorials/BasicEvents.html)
12+
13+
### [Observers]({{ site.baseurl }}/tutorials/BasicObservers.html)

0 commit comments

Comments
 (0)