Line | Branch | Exec | Source |
---|---|---|---|
1 | // This file is part of INSTINCT, the INS Toolkit for Integrated | ||
2 | // Navigation Concepts and Training by the Institute of Navigation of | ||
3 | // the University of Stuttgart, Germany. | ||
4 | // | ||
5 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
6 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
7 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
8 | |||
9 | /// @file Node.hpp | ||
10 | /// @brief Node Class | ||
11 | /// @author T. Topp (topp@ins.uni-stuttgart.de) | ||
12 | /// @date 2020-12-14 | ||
13 | |||
14 | #pragma once | ||
15 | |||
16 | // <boost/asio.hpp> needs to be included before <winsock.h> (even though not used in this file) | ||
17 | // https://stackoverflow.com/questions/9750344/boostasio-winsock-and-winsock-2-compatibility-issue | ||
18 | #ifdef _WIN32 | ||
19 | // Set the proper SDK version before including boost/Asio | ||
20 | #include <SDKDDKVer.h> | ||
21 | // Note boost/ASIO includes Windows.h. | ||
22 | #include <boost/asio.hpp> | ||
23 | #endif //_WIN32 | ||
24 | |||
25 | #include <imgui.h> | ||
26 | #include <imgui_node_editor.h> | ||
27 | #include <imgui_stdlib.h> | ||
28 | |||
29 | #include "internal/Node/Pin.hpp" | ||
30 | #include "Navigation/Time/InsTime.hpp" | ||
31 | |||
32 | #include <string> | ||
33 | #include <vector> | ||
34 | #include <thread> | ||
35 | #include <mutex> | ||
36 | #include <condition_variable> | ||
37 | #include <atomic> | ||
38 | #include <chrono> | ||
39 | #include <map> | ||
40 | |||
41 | #include <nlohmann/json.hpp> | ||
42 | using json = nlohmann::json; ///< json namespace | ||
43 | |||
44 | namespace NAV | ||
45 | { | ||
46 | class Node; | ||
47 | class NodeData; | ||
48 | class GroupBox; | ||
49 | |||
50 | namespace NodeRegistry | ||
51 | { | ||
52 | |||
53 | void RegisterNodeTypes(); // NOLINT(readability-redundant-declaration) - false warning. This is needed for the friend declaration below | ||
54 | |||
55 | } // namespace NodeRegistry | ||
56 | |||
57 | namespace FlowExecutor | ||
58 | { | ||
59 | |||
60 | /// @brief Main task of the FlowExecutor thread | ||
61 | void execute(); // NOLINT(readability-redundant-declaration) - false warning. This is needed for the friend declaration below | ||
62 | |||
63 | /// @brief Deinitialize all Nodes | ||
64 | void deinitialize(); // NOLINT(readability-redundant-declaration) - false warning. This is needed for the friend declaration below | ||
65 | |||
66 | } // namespace FlowExecutor | ||
67 | |||
68 | namespace gui | ||
69 | { | ||
70 | class NodeEditorApplication; | ||
71 | |||
72 | namespace menus | ||
73 | { | ||
74 | |||
75 | void ShowRunMenu(); | ||
76 | |||
77 | } // namespace menus | ||
78 | |||
79 | } // namespace gui | ||
80 | |||
81 | /// @brief Converts the provided node into a json object | ||
82 | /// @param[out] j Json object which gets filled with the info | ||
83 | /// @param[in] node Node to convert into json | ||
84 | void to_json(json& j, const Node& node); | ||
85 | /// @brief Converts the provided json object into a node object | ||
86 | /// @param[in] j Json object with the needed values | ||
87 | /// @param[out] node Object to fill from the json | ||
88 | void from_json(const json& j, Node& node); | ||
89 | |||
90 | /// @brief Abstract parent class for all nodes | ||
91 | class Node | ||
92 | { | ||
93 | public: | ||
94 | /// Kind information class | ||
95 | struct Kind | ||
96 | { | ||
97 | /// Possible kinds of Nodes | ||
98 | enum Value : uint8_t | ||
99 | { | ||
100 | Blueprint, ///< Node with header | ||
101 | Simple, ///< Node without header, which displays its name in the center of the content | ||
102 | GroupBox, ///< Group box which can group other nodes and drag them together | ||
103 | }; | ||
104 | |||
105 | /// @brief Default Constructor | ||
106 | Kind() = default; | ||
107 | |||
108 | /// @brief Implicit Constructor from Value type | ||
109 | /// @param[in] kind Value type to construct from | ||
110 | 6612 | constexpr Kind(Value kind) // NOLINT(hicpp-explicit-conversions, google-explicit-constructor) | |
111 | 6612 | : value(kind) | |
112 | 6612 | {} | |
113 | |||
114 | /// @brief Constructor from std::string | ||
115 | /// @param[in] string String representation of the type | ||
116 | 680 | explicit Kind(const std::string& string) | |
117 | 680 | { | |
118 |
2/2✓ Branch 1 taken 580 times.
✓ Branch 2 taken 100 times.
|
680 | if (string == "Blueprint") |
119 | { | ||
120 | 580 | value = Kind::Blueprint; | |
121 | } | ||
122 |
1/2✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
|
100 | else if (string == "Simple") |
123 | { | ||
124 | 100 | value = Kind::Simple; | |
125 | } | ||
126 | ✗ | else if (string == "GroupBox") | |
127 | { | ||
128 | ✗ | value = Kind::GroupBox; | |
129 | } | ||
130 | 680 | } | |
131 | |||
132 | /// @brief Allow switch(Node::Value(kind)) and comparisons | ||
133 | explicit operator Value() const { return value; } | ||
134 | /// @brief Prevent usage: if(node) | ||
135 | explicit operator bool() = delete; | ||
136 | /// @brief Assignment operator from Value type | ||
137 | /// @param[in] v Value type to construct from | ||
138 | /// @return The Kind type from the value type | ||
139 | 387 | Kind& operator=(Value v) | |
140 | { | ||
141 | 387 | value = v; | |
142 | 387 | return *this; | |
143 | } | ||
144 | |||
145 | friend constexpr bool operator==(const Node::Kind& lhs, const Node::Kind& rhs); | ||
146 | friend constexpr bool operator!=(const Node::Kind& lhs, const Node::Kind& rhs); | ||
147 | |||
148 | friend constexpr bool operator==(const Node::Kind& lhs, const Node::Kind::Value& rhs); | ||
149 | friend constexpr bool operator==(const Node::Kind::Value& lhs, const Node::Kind& rhs); | ||
150 | friend constexpr bool operator!=(const Node::Kind& lhs, const Node::Kind::Value& rhs); | ||
151 | friend constexpr bool operator!=(const Node::Kind::Value& lhs, const Node::Kind& rhs); | ||
152 | |||
153 | /// @brief std::string conversion operator | ||
154 | /// @return A std::string representation of the node kind | ||
155 | ✗ | explicit operator std::string() const | |
156 | { | ||
157 | ✗ | switch (value) | |
158 | { | ||
159 | ✗ | case Kind::Blueprint: | |
160 | ✗ | return "Blueprint"; | |
161 | ✗ | case Kind::Simple: | |
162 | ✗ | return "Simple"; | |
163 | ✗ | case Kind::GroupBox: | |
164 | ✗ | return "GroupBox"; | |
165 | } | ||
166 | ✗ | return ""; | |
167 | } | ||
168 | |||
169 | private: | ||
170 | /// @brief Value of the node kind | ||
171 | Value value; | ||
172 | }; | ||
173 | |||
174 | /// @brief Possible states of the node | ||
175 | enum class State : uint8_t | ||
176 | { | ||
177 | Disabled, ///< Node is disabled and won't be initialized | ||
178 | Deinitialized, ///< Node is deinitialized (red) | ||
179 | DoInitialize, ///< Node should be initialized | ||
180 | Initializing, ///< Node is currently initializing | ||
181 | Initialized, ///< Node is initialized (green) | ||
182 | DoDeinitialize, ///< Node should be deinitialized | ||
183 | Deinitializing, ///< Node is currently deinitializing | ||
184 | DoShutdown, ///< Node should shut down | ||
185 | Shutdown, ///< Node is shutting down | ||
186 | }; | ||
187 | |||
188 | /// @brief Different Modes the Node can work in | ||
189 | enum class Mode : uint8_t | ||
190 | { | ||
191 | REAL_TIME, ///< Node running in real-time mode | ||
192 | POST_PROCESSING, ///< Node running in post-processing mode | ||
193 | }; | ||
194 | |||
195 | /// @brief Constructor | ||
196 | /// @param[in] name Name of the node | ||
197 | explicit Node(std::string name); | ||
198 | /// @brief Destructor | ||
199 | virtual ~Node(); | ||
200 | /// @brief Copy constructor | ||
201 | Node(const Node&) = delete; | ||
202 | /// @brief Move constructor | ||
203 | Node(Node&&) = delete; | ||
204 | /// @brief Copy assignment operator | ||
205 | Node& operator=(const Node&) = delete; | ||
206 | /// @brief Move assignment operator | ||
207 | Node& operator=(Node&&) = delete; | ||
208 | |||
209 | /* -------------------------------------------------------------------------------------------------------- */ | ||
210 | /* Interface */ | ||
211 | /* -------------------------------------------------------------------------------------------------------- */ | ||
212 | |||
213 | /// @brief String representation of the Class Type | ||
214 | [[nodiscard]] virtual std::string type() const = 0; | ||
215 | |||
216 | /// @brief ImGui config window which is shown on double click | ||
217 | /// @attention Don't forget to set hasConfig to true | ||
218 | virtual void guiConfig(); | ||
219 | |||
220 | /// @brief Saves the node into a json object | ||
221 | [[nodiscard]] virtual json save() const; | ||
222 | |||
223 | /// @brief Restores the node from a json object | ||
224 | /// @param[in] j Json object with the node state | ||
225 | virtual void restore(const json& j); | ||
226 | |||
227 | /// @brief Restores link related properties of the node from a json object | ||
228 | /// @param[in] j Json object with the node state | ||
229 | virtual void restoreAtferLink(const json& j); | ||
230 | |||
231 | /// @brief Initialize the Node | ||
232 | /// | ||
233 | /// Here a node can do things like | ||
234 | /// - connecting to a sensor | ||
235 | /// - read a static data file which needs to be read completely (e.g. RINEX nav files) | ||
236 | /// - reset variables in case the NAV::Node::resetNode() is not used | ||
237 | /// @attention This function is called in the default implementation of the NAV::Node::resetNode() function. | ||
238 | /// If the initialize function should not be called more than once, make sure to override the NAV::Node::resetNode() function. | ||
239 | virtual bool initialize(); | ||
240 | |||
241 | /// @brief Deinitialize the Node | ||
242 | virtual void deinitialize(); | ||
243 | |||
244 | /// @brief Resets the node. It is guaranteed that the node is initialized when this is called. | ||
245 | virtual bool resetNode(); | ||
246 | |||
247 | /// @brief Called when a new link is to be established | ||
248 | /// @param[in] startPin Pin where the link starts | ||
249 | /// @param[in] endPin Pin where the link ends | ||
250 | /// @return True if link is allowed, false if link is rejected | ||
251 | virtual bool onCreateLink(OutputPin& startPin, InputPin& endPin); | ||
252 | |||
253 | /// @brief Called when a link is to be deleted | ||
254 | /// @param[in] startPin Pin where the link starts | ||
255 | /// @param[in] endPin Pin where the link ends | ||
256 | virtual void onDeleteLink(OutputPin& startPin, InputPin& endPin); | ||
257 | |||
258 | /// @brief Called when a new link was established | ||
259 | /// @param[in] startPin Pin where the link starts | ||
260 | /// @param[in] endPin Pin where the link ends | ||
261 | virtual void afterCreateLink(OutputPin& startPin, InputPin& endPin); | ||
262 | |||
263 | /// @brief Called when a link was deleted | ||
264 | /// @param[in] startPin Pin where the link starts | ||
265 | /// @param[in] endPin Pin where the link ends | ||
266 | virtual void afterDeleteLink(OutputPin& startPin, InputPin& endPin); | ||
267 | |||
268 | /// @brief Function called by the flow executer after finishing to flush out remaining data | ||
269 | virtual void flush(); | ||
270 | |||
271 | /* -------------------------------------------------------------------------------------------------------- */ | ||
272 | /* Member functions */ | ||
273 | /* -------------------------------------------------------------------------------------------------------- */ | ||
274 | |||
275 | /// @brief Notifies connected nodes about the change | ||
276 | /// @param[in] pinIdx Output Port index where to set the value | ||
277 | /// @param[in] insTime Time the value was generated | ||
278 | /// @param[in] guard Lock guard of the output data | ||
279 | void notifyOutputValueChanged(size_t pinIdx, const InsTime& insTime, const std::scoped_lock<std::mutex>&& guard); | ||
280 | |||
281 | /// @brief Blocks the thread till the output values was read by all connected nodes | ||
282 | /// @param[in] pinIdx Output Pin index where to request the lock | ||
283 | [[nodiscard]] std::scoped_lock<std::mutex> requestOutputValueLock(size_t pinIdx); | ||
284 | |||
285 | /// @brief Get Input Value connected on the pin. Only const data types. | ||
286 | /// @tparam T Type of the connected object | ||
287 | /// @param[in] portIndex Input port where to retrieve the data from | ||
288 | /// @return Pointer to the object | ||
289 | template<typename T> | ||
290 | 637 | [[nodiscard]] std::optional<InputPin::IncomingLink::ValueWrapper<T>> getInputValue(size_t portIndex) const | |
291 | { | ||
292 | 637 | return inputPins.at(portIndex).link.getValue<T>(); | |
293 | } | ||
294 | |||
295 | /// @brief Unblocks the connected node. Has to be called when the input value should be released and getInputValue was not called. | ||
296 | /// @param[in] portIndex Input port where the data should be released | ||
297 | void releaseInputValue(size_t portIndex); | ||
298 | |||
299 | /// @brief Checks wether there is an input pin with the same time | ||
300 | /// @param[in] insTime Time to check | ||
301 | bool hasInputPinWithSameTime(const InsTime& insTime) const; | ||
302 | |||
303 | /// @brief Calls all registered callbacks on the specified output port | ||
304 | /// @param[in] portIndex Output port where to call the callbacks | ||
305 | /// @param[in] data The data to pass to the callback targets | ||
306 | void invokeCallbacks(size_t portIndex, const std::shared_ptr<const NodeData>& data); | ||
307 | |||
308 | /// @brief Returns the pin with the given id | ||
309 | /// @param[in] pinId Id of the Pin | ||
310 | /// @return The input pin | ||
311 | [[nodiscard]] InputPin& inputPinFromId(ax::NodeEditor::PinId pinId); | ||
312 | |||
313 | /// @brief Returns the pin with the given id | ||
314 | /// @param[in] pinId Id of the Pin | ||
315 | /// @return The output pin | ||
316 | [[nodiscard]] OutputPin& outputPinFromId(ax::NodeEditor::PinId pinId); | ||
317 | |||
318 | /// @brief Returns the index of the pin | ||
319 | /// @param[in] pinId Id of the Pin | ||
320 | /// @return The index of the pin | ||
321 | [[nodiscard]] size_t inputPinIndexFromId(ax::NodeEditor::PinId pinId) const; | ||
322 | |||
323 | /// @brief Returns the index of the pin | ||
324 | /// @param[in] pinId Id of the Pin | ||
325 | /// @return The index of the pin | ||
326 | [[nodiscard]] size_t outputPinIndexFromId(ax::NodeEditor::PinId pinId) const; | ||
327 | |||
328 | /// @brief Node name and id | ||
329 | [[nodiscard]] std::string nameId() const; | ||
330 | |||
331 | /// @brief Get the size of the node | ||
332 | [[nodiscard]] const ImVec2& getSize() const; | ||
333 | |||
334 | // ------------------------------------------ State handling --------------------------------------------- | ||
335 | |||
336 | /// @brief Converts the state into a printable text | ||
337 | /// @param[in] state State to convert | ||
338 | /// @return String representation of the state | ||
339 | static std::string toString(State state); | ||
340 | |||
341 | /// @brief Get the current state of the node | ||
342 | [[nodiscard]] State getState() const; | ||
343 | |||
344 | /// @brief Get the current mode of the node | ||
345 | [[nodiscard]] Mode getMode() const; | ||
346 | |||
347 | /// @brief Asks the node worker to initialize the node | ||
348 | /// @param[in] wait Wait for the worker to complete the request | ||
349 | /// @return True if not waiting and the worker accepted the request otherwise if waiting only true if the node initialized correctly | ||
350 | bool doInitialize(bool wait = false); | ||
351 | |||
352 | /// @brief Asks the node worker to reinitialize the node | ||
353 | /// @param[in] wait Wait for the worker to complete the request | ||
354 | /// @return True if not waiting and the worker accepted the request otherwise if waiting only true if the node initialized correctly | ||
355 | bool doReinitialize(bool wait = false); | ||
356 | |||
357 | /// @brief Asks the node worker to deinitialize the node | ||
358 | /// @param[in] wait Wait for the worker to complete the request | ||
359 | /// @return True if the worker accepted the request | ||
360 | bool doDeinitialize(bool wait = false); | ||
361 | |||
362 | /// @brief Asks the node worker to disable the node | ||
363 | /// @param[in] wait Wait for the worker to complete the request | ||
364 | /// @return True if the worker accepted the request | ||
365 | bool doDisable(bool wait = false); | ||
366 | |||
367 | /// @brief Enable the node | ||
368 | /// @return True if enabling was successful | ||
369 | bool doEnable(); | ||
370 | |||
371 | /// Wakes the worker thread | ||
372 | void wakeWorker(); | ||
373 | |||
374 | /// @brief Checks if the node is disabled | ||
375 | [[nodiscard]] bool isDisabled() const; | ||
376 | |||
377 | /// @brief Checks if the node is initialized | ||
378 | [[nodiscard]] bool isInitialized() const; | ||
379 | |||
380 | /// @brief Checks if the node is changing its state currently | ||
381 | [[nodiscard]] bool isTransient() const; | ||
382 | |||
383 | /// @brief Checks if the node is only working in real time (sensors, network interfaces, ...) | ||
384 | [[nodiscard]] bool isOnlyRealtime() const; | ||
385 | |||
386 | /* -------------------------------------------------------------------------------------------------------- */ | ||
387 | /* Member variables */ | ||
388 | /* -------------------------------------------------------------------------------------------------------- */ | ||
389 | |||
390 | /// Unique Id of the Node | ||
391 | ax::NodeEditor::NodeId id = 0; | ||
392 | /// Kind of the Node | ||
393 | Kind kind = Kind::Blueprint; | ||
394 | /// Name of the Node | ||
395 | std::string name; | ||
396 | /// List of input pins | ||
397 | std::vector<InputPin> inputPins; | ||
398 | /// List of output pins | ||
399 | std::vector<OutputPin> outputPins; | ||
400 | |||
401 | /// Enables the callbacks | ||
402 | bool callbacksEnabled = false; | ||
403 | |||
404 | /// Map with callback events (sorted by time) | ||
405 | std::multimap<InsTime, std::pair<OutputPin*, size_t>> pollEvents; | ||
406 | |||
407 | protected: | ||
408 | /// The Default Window size for new config windows. | ||
409 | /// Only set the variable if the object/window has no persistently saved data (no entry in .ini file) | ||
410 | ImVec2 _guiConfigDefaultWindowSize{ 500.0F, 400.0F }; | ||
411 | |||
412 | /// Flag if the config window should be shown | ||
413 | bool _hasConfig = false; | ||
414 | |||
415 | /// Lock the config when executing post-processing | ||
416 | bool _lockConfigDuringRun = true; | ||
417 | |||
418 | /// Whether the node can run in post-processing or only real-time | ||
419 | bool _onlyRealTime = false; | ||
420 | |||
421 | private: | ||
422 | State _state = State::Deinitialized; ///< Current state of the node | ||
423 | mutable std::mutex _stateMutex; ///< Mutex to interact with the worker state variable | ||
424 | |||
425 | /// Mode the node is currently running in | ||
426 | std::atomic<Mode> _mode = Mode::REAL_TIME; | ||
427 | |||
428 | /// Flag if the node should be reinitialize after deinitializing | ||
429 | bool _reinitialize = false; | ||
430 | |||
431 | /// Flag if the node should be disabled after deinitializing | ||
432 | bool _disable = false; | ||
433 | |||
434 | /// Flag if the config window is shown | ||
435 | bool _showConfig = false; | ||
436 | |||
437 | /// Mutex to show the config window (prevents initialization to modify values within the config window) | ||
438 | std::mutex _configWindowMutex; | ||
439 | /// Flag if the config window should be forced collapsed | ||
440 | bool _configWindowForceCollapse = false; | ||
441 | /// Flag if the config window is collapsed | ||
442 | bool _configWindowIsCollapsed = false; | ||
443 | |||
444 | /// Flag if the config window should be focused | ||
445 | bool _configWindowFocus = false; | ||
446 | |||
447 | /// Size of the node in pixels | ||
448 | ImVec2 _size{ 0, 0 }; | ||
449 | |||
450 | /// Flag which prevents the worker to be autostarted if false | ||
451 | static inline bool _autostartWorker = true; | ||
452 | |||
453 | 6612 | std::chrono::duration<int64_t> _workerTimeout = std::chrono::minutes(1); ///< Periodic timeout of the worker to check if new data available | |
454 | std::thread _worker; ///< Worker handling initialization and processing of data | ||
455 | std::mutex _workerMutex; ///< Mutex to interact with the worker condition variable | ||
456 | std::condition_variable _workerConditionVariable; ///< Condition variable to signal the worker thread to do something | ||
457 | bool _workerWakeup = false; ///< Variable to prevent the worker from sleeping | ||
458 | |||
459 | /// @brief Worker thread | ||
460 | /// @param[in, out] node The node where the thread belongs to | ||
461 | static void workerThread(Node* node); | ||
462 | |||
463 | /// Handler which gets triggered if the worker runs into a periodic timeout | ||
464 | virtual void workerTimeoutHandler(); | ||
465 | |||
466 | /// @brief Called by the worker to initialize the node | ||
467 | /// @return True if the initialization was successful | ||
468 | bool workerInitializeNode(); | ||
469 | |||
470 | /// @brief Called by the worker to deinitialize the node | ||
471 | /// @return True if the deinitialization was successful | ||
472 | bool workerDeinitializeNode(); | ||
473 | |||
474 | friend class gui::NodeEditorApplication; | ||
475 | friend class NAV::GroupBox; | ||
476 | |||
477 | /// @brief Main task of the FlowExecutor thread | ||
478 | friend void NAV::FlowExecutor::execute(); | ||
479 | /// @brief Deinitialize all Nodes | ||
480 | friend void NAV::FlowExecutor::deinitialize(); | ||
481 | /// @brief Register all available Node types for the program | ||
482 | friend void NAV::NodeRegistry::RegisterNodeTypes(); | ||
483 | |||
484 | /// @brief Converts the provided node into a json object | ||
485 | /// @param[out] j Json object which gets filled with the info | ||
486 | /// @param[in] node Node to convert into json | ||
487 | friend void NAV::to_json(json& j, const Node& node); | ||
488 | /// @brief Converts the provided json object into a node object | ||
489 | /// @param[in] j Json object with the needed values | ||
490 | /// @param[out] node Object to fill from the json | ||
491 | friend void NAV::from_json(const json& j, Node& node); | ||
492 | |||
493 | /// @brief Show the run menu dropdown | ||
494 | friend void gui::menus::ShowRunMenu(); | ||
495 | }; | ||
496 | |||
497 | /// @brief Equal compares Node::Kind values | ||
498 | /// @param[in] lhs Left-hand side of the operator | ||
499 | /// @param[in] rhs Right-hand side of the operator | ||
500 | /// @return Whether the comparison was successful | ||
501 | constexpr bool operator==(const Node::Kind& lhs, const Node::Kind& rhs) { return lhs.value == rhs.value; } | ||
502 | /// @brief Inequal compares Node::Kind values | ||
503 | /// @param[in] lhs Left-hand side of the operator | ||
504 | /// @param[in] rhs Right-hand side of the operator | ||
505 | /// @return Whether the comparison was successful | ||
506 | constexpr bool operator!=(const Node::Kind& lhs, const Node::Kind& rhs) { return !(lhs == rhs); } | ||
507 | |||
508 | /// @brief Equal compares Node::Kind values | ||
509 | /// @param[in] lhs Left-hand side of the operator | ||
510 | /// @param[in] rhs Right-hand side of the operator | ||
511 | /// @return Whether the comparison was successful | ||
512 | 3429 | constexpr bool operator==(const Node::Kind& lhs, const Node::Kind::Value& rhs) { return lhs.value == rhs; } | |
513 | /// @brief Equal compares Node::Kind values | ||
514 | /// @param[in] lhs Left-hand side of the operator | ||
515 | /// @param[in] rhs Right-hand side of the operator | ||
516 | /// @return Whether the comparison was successful | ||
517 | constexpr bool operator==(const Node::Kind::Value& lhs, const Node::Kind& rhs) { return lhs == rhs.value; } | ||
518 | /// @brief Inequal compares Node::Kind values | ||
519 | /// @param[in] lhs Left-hand side of the operator | ||
520 | /// @param[in] rhs Right-hand side of the operator | ||
521 | /// @return Whether the comparison was successful | ||
522 | 2031 | constexpr bool operator!=(const Node::Kind& lhs, const Node::Kind::Value& rhs) { return !(lhs == rhs); } | |
523 | /// @brief Inequal compares Node::Kind values | ||
524 | /// @param[in] lhs Left-hand side of the operator | ||
525 | /// @param[in] rhs Right-hand side of the operator | ||
526 | /// @return Whether the comparison was successful | ||
527 | constexpr bool operator!=(const Node::Kind::Value& lhs, const Node::Kind& rhs) { return !(lhs == rhs); } | ||
528 | |||
529 | } // namespace NAV | ||
530 |