You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Dec 29, 2022. It is now read-only.
Note that the cross-reference links in this page are only rendered in the Doxygen version of the documentation (see [Building](#building)).
14
16
You can also navigate the web version of the documentation hosted at https://google.github.io/detectorgraph/.
15
17
@@ -144,10 +146,15 @@ Some of its most unique features are:
144
146
- Flat design - the graph has only two types of constructs; Topics and Detectors.
145
147
- Small & Simple - it doesn't do anything else.
146
148
149
+
## Style & Tips
150
+
151
+
For an article containing a set of guidelines & rules of thumb that we accumulated after 3+ years of using DetectorGraph visit [Style Tips - Patterns, Anti-Patterns & Suggestions](@ref ssg-style_suggestions).
152
+
These are aimed at keeping software design constrained in a way that best takes advantage of the DetectorGraph framework, its expressibility and modeling power.
153
+
147
154
## Naming
148
155
149
156
The DetectorGraph library had a little naming problem growing up. From birth it came to replace nlDetectorGraph and so it pretended to be called that way. As an adolescent it decided it wanted to be called MarkII.. but no one cared - for years now the world continues to call it simply DetectorGraph. Now as it approaches adulthood it has finally accepted its popular name: DetectorGraph.
150
157
151
158
## In-depth Docs & API Reference
152
159
153
-
For in depth documentation of the library, [start here](@ref core_introduction) - these are provided by the [auto-generated docs](https://google.github.io/detectorgraph/).
160
+
For in depth documentation of the library, [start here](@ref core_introduction).
This page contains a set of guidelines & rules of thumb that we accumulated after 2+ years of using DetectorGraph.
8
+
This page contains a set of guidelines & rules of thumb that we accumulated after 3+ years of using DetectorGraph.
9
9
These are aimed at keeping software design constrained in a way that best takes advantage of the DetectorGraph framework, its expressibility and modeling power.
- `FooBarState`, produced by a `FooBarDetector`. Note that `FooBarDetectorState` is not encouraged as it ties the producer with it's product - but it can be ok in some very specific cases.
18
18
- `SomethingRequest` & `SomethingResponse` when implementing a RPC Request/Response pattern.
19
-
- `FooBarSettings` for settings that determine how `FooBarDetector` (and by extension `FooBarState`) behave. See also [Settings](@ref settings-topicstate).
19
+
- `FooBarSettings` for settings that determine how `FooBarDetector` (and by extension `FooBarState`) behave. See also [Settings](@ref ssg-settings-topicstate).
20
20
21
-
\section too-big-too-small-tss Too Big & Too Small TopicStates
21
+
\section ssg-too-big-too-small-tss Too Big vs. Too Small TopicStates
22
22
23
23
Hypothetically, the entirety of a system's state could be contained on a single TopicState that is Subscribed to & published to by every detector. Conversely, one could split every individual bit (pardon the pun) of data into separate TopicStates and have detectors only subscribe to the bits necessary to form the data it depends on.
24
24
Both are clearly poor separations-of-concern choices and are clear misuses of the DetectorGraph framework - obviously the sweet spot lies in between, but where? Below are a set of guidelines for partitioning Topics:
25
25
26
-
\subsection type-is-data Type is data
26
+
\subsection ssg-type-is-data Type is data
27
27
28
28
\par "A rose by any other name would smell as sweet" — Juliet
29
29
@@ -46,7 +46,7 @@ From [Beat Machine](@ref beatmachine.cpp):
46
46
47
47
Inheritance can save you some typing and be OK when literally all you're changing is the type 'name' and when you know the data structure will never change and when the 'is a' relationship holds strongly. Inheritance is the lazy approach but that you'll likely have to ditch (at great expense possibly) at some point in the future.
48
48
49
-
\subsection empty-topics Trivial/Empty Topics are sometimes fine.
49
+
\subsection ssg-empty-topics Trivial/Empty Topics are sometimes fine.
50
50
51
51
Sometimes all a topic needs to express is the fact that _it updated_, that it _happened_ or any other purely unary signals:
52
52
@@ -55,7 +55,7 @@ From [Trivial Vending Machine](@ref trivialvendingmachine.cpp):
55
55
From [Fancy Vending Machine](@ref fancyvendingmachine.cpp):
\subsection mutually-exclusive Mutually exclusive states also belong together.
68
+
\subsection ssg-mutually-exclusive Mutually exclusive states also belong together.
69
69
70
70
@code
71
71
struct DoorState : public TopicState
@@ -81,19 +81,19 @@ struct DoorState : public TopicState
81
81
82
82
This also includes Topics used to drive (or driven by) a finite state machine.
83
83
84
-
\subsection everything-is-a-service Topics are APIs
84
+
\subsection ssg-everything-is-a-service Topics are APIs
85
85
86
86
Another rule of thumb is to think of Topics as APIs themselves; they should be as general as possible. More general TopicStates tend to better isolate concerns and be more reusable in the future. In general, when creating a topic don't name it with respect to what it'll be used; instead try to name it according to what information it carries.
87
87
88
-
\section dependencies Detectors and Topics Dependencies
88
+
\section ssg-dependencies Detectors and Topics Dependencies
89
89
90
90
One of the main advantages of using DetectorGraph is a very clear isolation of dependencies & concerns. To maintain those advantages DetectorGraph code should comply with the following rules:
91
91
- Detectors should not have any dependencies on any other Detectors: no data-structure dependencies nor runtime/callgraph dependencies.
92
92
- Detectors can depend on any number of Topics; ideally only the Topics it Subscribes or Publishes.
93
93
- Topics can depend on other Topics for data-structures but should never inherit from other Topics.
94
94
- Detectors should do no I/O nor should they talk to other dynamic software modules; debug/text logging is probably the one notable exception. It's totally fine to use a math library or an algorithm-specific library but it's not OK to communicate across threads accessing a singleton outside the DetectorGraph.
95
95
96
-
\section caching-topicstate Caching/Saving TopicStates is the right solution!
96
+
\section ssg-caching-topicstate Caching/Saving TopicStates is the right solution!
97
97
98
98
Sometimes detectors only need a the information in `TopicStateA` when evaluating `TopicStateB`. It may seem like Subscribing to `TopicStateA` is overkill - but it's not. Remember that `Evaluate(TopicStateA)`s is only called when `TopicStateA` changes and thus there's no runtime penalty on having multiple `Evaluate()`s that are called rarely.
99
99
@@ -118,11 +118,11 @@ private:
118
118
};
119
119
@endcode
120
120
121
-
Note that most `TopicStates` are small bits of data and thus copying by value should always be efficient. In cases where a `TopicState` contains a lot of data, such that copy-by-value would be a problem, it is the responsibility of that `TopicState`s implementation to implement an appropriate shallow copies scheme. For an example of that see [Sharing Memory across TopicStates](@ref sharing-mem).
121
+
Note that most `TopicStates` are small bits of data and thus copying by value should always be efficient. In cases where a `TopicState` contains a lot of data, such that copy-by-value would be a problem, it is the responsibility of that `TopicState`s implementation to implement an appropriate shallow copies scheme. For an example of that see [Sharing Memory across TopicStates](@ref ex-pt-sharing-mem).
122
122
123
-
\section settings-topicstate Settings are also Topics
123
+
\section ssg-settings-topicstate Settings are also Topics
124
124
125
-
Topics should be used to convey settings as well, and caching them is the way to go (see @ref caching-topicstate)
125
+
Topics should be used to convey settings as well, and caching them is the way to go (see @ref ssg-caching-topicstate)
126
126
@code
127
127
class TooHotDetector : public SubscriberInterface<TooHotThreshold>, public SubscriberInterface<Temperature>, public Publisher<TooHot>
128
128
{
@@ -144,7 +144,7 @@ private:
144
144
};
145
145
@endcode
146
146
147
-
\section flat-evaluates Flat Evaluates vs. Indirect and/or nested member methods
147
+
\section ssg-flat-evaluates Flat Evaluates vs. Indirect and/or nested member methods
148
148
149
149
\par "Flat is better than nested" — Zen of Python
150
150
@@ -196,7 +196,7 @@ class TooHotDetector : public SubscriberInterface<Temperature>, public Publisher
196
196
197
197
In the first example a reader must follow & comprehend what PublishTooHotIfTooHot does before they can conclude that `TooHotDetector` will always publish 1 (and only one) `TooHot` state per input sample. In the second example that relationship is trivial. The intent is to have direct paths from an `Evaluate()` (or `BeginEvaluation()`, `CompleteEvaluation()`) to `Publish` (or `FuturePublish()`, `PublishOnTimeout()`).
In many cases detectors provide an accumulated/aggregated view of a set of TopicStates. The simplest & more readable way we found to do this was based on variations of the pattern below:
202
202
@@ -237,25 +237,25 @@ In many cases detectors provide an accumulated/aggregated view of a set of Topic
237
237
238
238
Note the use of CompleteEvaluation to make the general conditional check regardless what inputs have changed.
239
239
240
-
\section resuming-state Resuming State
240
+
\section ssg-resuming-state Resuming State
241
241
242
242
The best way we found to resume graph states is to use a specific `TopicState` (`ResumeFromSnapshotTopicState`) that contains a de-serialized version of the latest preserved StateSnapshot. Each detector that needs to resume its state then has a chance of inspecting the entire snapshot to reconstruct its state.
243
243
244
244
Initially it was thought that state resuming could be done simply be re-publishing the stored `TopicStates` but since most Detectors keep state they'd have to subscribe to their own outputs - in a way that the Resume operation would go in reverse order than the normal topological sort of the graph. That was deemed overly complex & contrived and would greatly increase code complexity.
245
245
246
246
Instead we opted of giving detectors this _know-all_ single chance to restore their state & re-publish any resumed state `TopicStates` as necessary.
247
247
248
-
For a full example see [Resuming Counter](@ref resuminggraph.cpp) (@copybrief resuminggraph.cpp)
248
+
For a full example see [Resuming Counter](@ref resuminggraph.cpp) (@copybrief resuminggraph.cpp)
249
249
250
-
\section explicit-calls-to-evaluate Explicit calls to Evaluate()?
250
+
\section ssg-explicit-calls-to-evaluate Explicit calls to Evaluate()?
251
251
252
252
Detector's `Evaluate()` method calls are central to DetectorGraph applications. They are called by the framework in a very specific and coordinated manner (`BeginEvaluation()->n*Evaluate()->CompleteEvaluation()`).
253
253
254
254
In some situations it may seem appropriate to explicitly/manually call `Evaluate(X)` from within another method of a Detector to process a specific (e.g. initial) version of X - that's sometimes an *anti-pattern*. When a reader encounters an `Evaluate(Z)` method he/she expects that to only be called when Z has changed - and most of the times that's also what log messages inside `Evaluate(Z)` will have log readers believe. By manually calling `Evaluate()` you break that expectation.
255
255
256
256
More than once this had led debugging along a completely wrong path and makes `Detector`s program flow less flat and more convoluted. If a single set of operations is necessary for multiple different inputs it is better to implement that as a separate function and have that called from both `Evaluate()`s.
257
257
258
-
\section initial-states Publishing Initial States
258
+
\section ssg-initial-states Publishing Initial States
259
259
260
260
Detectors do not have a specific mechanism for publishing their initial states/prime outputs.
261
261
@@ -267,11 +267,11 @@ Instead the suggested pattern going forward is to always rely on `ResumeFromSnap
267
267
268
268
In the past we have used a specific `TopicState` (say `DetectorGraphInitialized`) that detectors can subscribe to to be notified when the system boots and publish any new state they may need to. That solution is redundant and not as canonical as the solution described above so it's not recommended anymore.
269
269
270
-
For a full example see [Resuming Counter](@ref resuminggraph.cpp) (@copybrief resuminggraph.cpp)
270
+
For a full example see [Resuming Counter](@ref resuminggraph.cpp) (@copybrief resuminggraph.cpp)
271
271
272
-
\section namespaces Usage of Namespaces
272
+
\section ssg-namespaces Usage of Namespaces
273
273
274
-
All DetectorGraph names should be inside the `DetectorGraph` namespace. Within an application it is suggested to have a single namespace reserved for all your Detectors & TopicStates; that allows you to use short names & appropriate names for those without risk of collision. We have also used separate namespaces for TopicsStates & Detectors but that didn't yield much readability or expressibility. Things to keep in mind when making your decision:
274
+
All DetectorGraph names are inside the `DetectorGraph` namespace. Within an application it is suggested to have a single namespace reserved for all your Detectors & TopicStates; that allows you to use short names & appropriate names for those without risk of collision. We have also used separate namespaces for TopicsStates & Detectors but that didn't yield much readability or expressibility. Things to keep in mind when making your decision:
275
275
276
276
- Detectors' names are used not used often in code.
Copy file name to clipboardExpand all lines: doxygen/topic_inspection_apis.dox
+2-1Lines changed: 2 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,7 @@
1
1
/**
2
2
@page topic_inspection_apis New Topic Inspection APIs
3
-
3
+
@brief Two new APIs useful within Unit Tests
4
+
@par
4
5
As a part of the effort to enable the `DetectorGraph` `Lite` target we created new APIs for inspecting Topics that make it easier to inspect Topics during Testing (e.g. Unit Testing a Detector) and that work both on `Full` & `Lite` targets. These are:
0 commit comments