VLink  2.0.0
A high-performance communication middleware
getter.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file getter.h
26  * @brief Read-side primitive of the VLink field communication model.
27  *
28  * @details
29  * @c Getter<ValueT, SecT> mirrors the latest value produced by a matching
30  * @c Setter on the same URL. Unlike @c Subscriber it retains only the most
31  * recent payload rather than queuing every update, and it offers a blocking
32  * @c wait_for_value() entry point in addition to the standard event callback.
33  *
34  * The class is a thin, header-only template wrapper around @c GetterImpl and
35  * owns its own cached @c value_, change-tracking buffer, and condition
36  * variable.
37  *
38  * @par Field-model Data Flow
39  * @verbatim
40  * Setter<ValueT> Transport Back-end Getter<ValueT>
41  * -------------- ------------------ ---------------
42  * | set(v) | |
43  * |----------------------------->| serialised latest value |
44  * | |--- overwrites previous ----->|
45  * | | |
46  * | | | Serializer::
47  * | | | deserialize
48  * | | |--> listen cb (optional)
49  * | | |--> value_ cached
50  * | | |--> wait_for_value
51  * | | | returns true
52  * | | |--> get() => value
53  * @endverbatim
54  *
55  * @par Getter vs Subscriber
56  * | Feature | @c Getter<T> | @c Subscriber<T> |
57  * | ------------------------ | --------------------------------------- | ------------------------------ |
58  * | Value retention | Latest value cached | None |
59  * | Transport role tag | @c kGetter | @c kSubscriber |
60  * | Blocking read | @c wait_for_value() | N/A |
61  * | Duplicate suppression | @c set_change_reporting(true) | N/A |
62  * | Cross-role bridge | @c mark_as_subscriber() | @c mark_as_getter() |
63  *
64  * @par Read-Mode Variants
65  * | Method | Blocking | Result type | Notes |
66  * | ------------------------------------- | -------- | ----------------------- | ---------------------------- |
67  * | @c get() | No | @c std::optional | Returns @c nullopt initially |
68  * | @c wait_for_value(timeout) | Yes | @c bool (then @c get()) | Default infinite if @c 0 |
69  * | @c listen(callback) | No | @c void(const ValueT&) | Fires on every update |
70  * | @c listen(cb) + @c change_reporting | No | @c void(const ValueT&) | Suppress identical bytes |
71  *
72  * @par Usage Patterns
73  * @code
74  * vlink::Getter<int> g("shm://vehicle/gear");
75  *
76  * if (auto v = g.get()) { use(*v); }
77  *
78  * if (g.wait_for_value(std::chrono::milliseconds(500))) {
79  * use(*g.get());
80  * }
81  *
82  * g.listen([](const int& v) { on_update(v); });
83  *
84  * g.set_change_reporting(true);
85  * g.listen([](const int& v) { on_changed(v); });
86  * @endcode
87  *
88  * @note Until a @c Setter writes for the first time, @c get() returns
89  * @c std::nullopt and @c wait_for_value() blocks.
90  *
91  * @see setter.h, subscriber.h, node.h, serializer.h
92  */
93 
94 #pragma once
95 
96 #include <memory>
97 #include <mutex>
98 #include <optional>
99 #include <string>
100 #include <type_traits>
101 
103 #include "./base/functional.h"
104 #include "./impl/getter_impl.h"
105 #include "./node.h"
106 
107 namespace vlink {
108 
109 /**
110  * @class Getter
111  * @brief Type-safe latest-value reader for the VLink field communication model.
112  *
113  * @details
114  * Inherits the full @c Node API and adds field-specific operations: cached
115  * @c get(), blocking @c wait_for_value(), single-shot @c listen() callback,
116  * change-reporting filter, and latency / sample-loss tracking.
117  *
118  * @tparam ValueT C++ value type. Must satisfy @c Serializer::is_supported().
119  * @tparam SecT Security mode; defaults to @c SecurityType::kWithoutSecurity.
120  */
121 template <typename ValueT, SecurityType SecT = SecurityType::kWithoutSecurity>
122 class Getter : public Node<GetterImpl, SecT> {
123  public:
124  using UniquePtr = std::unique_ptr<Getter<ValueT, SecT>>; ///< Owning unique-pointer alias.
125  using SharedPtr = std::shared_ptr<Getter<ValueT, SecT>>; ///< Owning shared-pointer alias.
126  using MsgCallback = Function<void(const ValueT&)>; ///< User callback signature for value updates.
127 
128  static constexpr ImplType kImplType = kGetter; ///< Node role tag (@c kGetter).
129  static constexpr Serializer::Type kValueType = Serializer::get_type_of<ValueT>(); ///< Codec resolved from @c ValueT.
130 
131  static_assert(Serializer::is_supported(kValueType), "<ValueT> is not a supported Serializer type.");
132 
133  /**
134  * @brief Heap-allocates a @c Getter and wraps it in a @c std::unique_ptr.
135  *
136  * @param url_str Field URL such as @c "shm://vehicle/gear".
137  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
138  * @return Owning @c UniquePtr to the new getter.
139  */
140  [[nodiscard]] static UniquePtr create_unique(const std::string& url_str, InitType type = InitType::kWithInit);
141 
142  /**
143  * @brief Heap-allocates a @c Getter and wraps it in a @c std::shared_ptr.
144  *
145  * @param url_str Field URL string.
146  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
147  * @return Owning @c SharedPtr to the new getter.
148  */
149  [[nodiscard]] static SharedPtr create_shared(const std::string& url_str, InitType type = InitType::kWithInit);
150 
151  /**
152  * @brief Constructs a getter from a typed transport configuration object.
153  *
154  * @tparam ConfT Concrete configuration type derived from @c Conf.
155  * @param conf Populated configuration aggregate.
156  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
157  */
158  // NOLINTNEXTLINE(modernize-use-constraints)
159  template <typename ConfT, typename = std::enable_if_t<std::is_base_of_v<Conf, ConfT>>>
160  explicit Getter(const ConfT& conf, InitType type = InitType::kWithInit);
161 
162  /**
163  * @brief Constructs a getter from a URL string.
164  *
165  * @param url_str Field URL such as @c "shm://vehicle/gear".
166  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
167  */
168  explicit Getter(const std::string& url_str, InitType type = InitType::kWithInit);
169 
170  /**
171  * @brief Destroys the getter and drains in-flight transport callbacks.
172  *
173  * @details
174  * Required so the internal delivery callback installed by @c init() cannot
175  * fire on the transport thread after this getter's mutex and value have
176  * already been destroyed.
177  */
178  ~Getter() override;
179 
180  /**
181  * @brief Returns the most recent cached value, if any has been received.
182  *
183  * @details
184  * Thread-safe. Returns @c std::nullopt before the first matching @c Setter
185  * write reaches this getter.
186  *
187  * @return @c std::optional<ValueT> -- the latest value, or empty.
188  */
189  [[nodiscard]] std::optional<ValueT> get() const;
190 
191  /**
192  * @brief Blocks until a value is received or @p timeout expires.
193  *
194  * @details
195  * Returns immediately if a value is already cached. A @p timeout of @c 0
196  * is treated as infinite (with a warning). @c interrupt() causes the wait
197  * to abort and return @c false. Use @c get() afterwards to retrieve the
198  * value when this method returns @c true.
199  *
200  * @param timeout Maximum wait duration. Default: @c Timeout::kDefaultInterval.
201  * @return @c true if a value was received; @c false on timeout or interrupt.
202  */
203  bool wait_for_value(std::chrono::milliseconds timeout = Timeout::kDefaultInterval);
204 
205  /**
206  * @brief Installs a callback invoked whenever a new value arrives.
207  *
208  * @details
209  * The callback receives a deserialised @c ValueT reference. It runs for
210  * every setter write unless @c set_change_reporting(true) is active, in
211  * which case duplicate consecutive payloads are filtered out. Internally
212  * the getter first fires the callback, then updates @c value_ and wakes
213  * @c wait_for_value().
214  *
215  * @note Calling @c listen() more than once is fatal.
216  *
217  * @param callback @c void(const ValueT&) invoked on each new value.
218  * @return @c true on successful installation; a second call is
219  * fatal and never returns.
220  */
221  bool listen(MsgCallback&& callback);
222 
223  /**
224  * @brief Toggles change-only reporting (duplicate suppression).
225  *
226  * @details
227  * When enabled, incoming payloads whose raw serialised bytes match the
228  * previous delivery are dropped before any caching or callback dispatch.
229  * Useful when a @c Setter repeatedly writes the same value.
230  *
231  * @param enable @c true to suppress duplicates; @c false (default) to deliver all.
232  */
233  void set_change_reporting(bool enable);
234 
235  /**
236  * @brief Enables or disables manual-unloan mode for zero-copy receives.
237  *
238  * @param manual_unloan @c true to enable manual return; @c false for auto-return.
239  */
240  void set_manual_unloan(bool manual_unloan) override;
241 
242  /**
243  * @brief Toggles per-value latency and sample-loss measurement.
244  *
245  * @param enable @c true to begin tracking; @c false to stop.
246  */
247  void set_latency_and_lost_enabled(bool enable);
248 
249  /**
250  * @brief Reports whether latency and sample-loss tracking is currently active.
251  *
252  * @return @c true if @c set_latency_and_lost_enabled(true) was invoked.
253  */
254  [[nodiscard]] bool is_latency_and_lost_enabled() const;
255 
256  /**
257  * @brief Returns the most recent end-to-end latency measurement.
258  *
259  * @return Latency in nanoseconds; @c 0 if tracking is disabled.
260  */
261  [[nodiscard]] int64_t get_latency() const;
262 
263  /**
264  * @brief Returns cumulative sample-delivery statistics.
265  *
266  * @return @c SampleLostInfo with total expected and total lost counts.
267  */
268  [[nodiscard]] SampleLostInfo get_lost() const;
269 
270  /**
271  * @brief Reports whether change-only reporting is currently active.
272  *
273  * @return @c true if duplicate suppression is enabled.
274  */
275  [[nodiscard]] bool get_change_reporting() const;
276 
277  /**
278  * @brief Initialises the getter and installs the internal delivery hook.
279  *
280  * @details
281  * Overrides @c Node::init() to additionally register a bytes-level callback
282  * that deserialises each delivery, invokes the user @c listen() callback
283  * (if installed), then updates @c value_ and wakes the condition variable
284  * used by @c wait_for_value().
285  *
286  * @return @c true on first successful initialisation; @c false otherwise.
287  */
288  bool init() override;
289 
290  /**
291  * @brief Aborts any blocking @c wait_for_value() call.
292  *
293  * @details
294  * Overrides @c Node::interrupt() to additionally notify the local
295  * condition variable so that the blocking wait returns @c false promptly.
296  */
297  void interrupt() override;
298 
299  /**
300  * @brief Promotes this getter to behave as a @c Subscriber at the transport layer.
301  *
302  * @details
303  * Switches @c impl_type from @c kGetter to @c kSubscriber so that
304  * event-style semantics (no latest-value retention) are applied.
305  * Reinitialises the transport extension if called post-@c init(). Used
306  * when bridging a getter through event-only transports.
307  */
308  void mark_as_subscriber();
309 
310  private:
311  void listen_bytes(NodeImpl::MsgCallback&& callback);
312 
313  std::optional<ValueT> value_;
314  mutable std::mutex mtx_;
315  ConditionVariable cv_;
316  MsgCallback callback_;
317  Bytes last_cache_;
318  bool has_value_notification_{false};
319  bool change_reporting_{false};
320 };
321 
322 /**
323  * @class SecurityGetter
324  * @brief Convenience alias of @c Getter with per-message decryption enabled.
325  *
326  * @details
327  * Equivalent to @c Getter<ValueT, SecurityType::kWithSecurity>. Each
328  * incoming payload is decrypted before codec dispatch and caching.
329  *
330  * @tparam ValueT Value type to read.
331  */
332 template <typename ValueT>
333 class SecurityGetter : public Getter<ValueT, SecurityType::kWithSecurity> {
334  public:
335  using UniquePtr = std::unique_ptr<SecurityGetter<ValueT>>; ///< Owning unique-pointer alias.
336  using SharedPtr = std::shared_ptr<SecurityGetter<ValueT>>; ///< Owning shared-pointer alias.
337 
338  /**
339  * @brief Heap-allocates a @c SecurityGetter and wraps it in a @c std::unique_ptr.
340  *
341  * @tparam SecurityConfigT Forwardable @c Security::Config compatible type.
342  * @param url_str Field URL string.
343  * @param sec_cfg Security configuration; empty uses the default symmetric slot.
344  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
345  * @return Owning @c UniquePtr to the new secure getter.
346  */
347  // NOLINTNEXTLINE(modernize-use-constraints)
348  template <typename SecurityConfigT = Security::Config>
349  [[nodiscard]] static UniquePtr create_unique(const std::string& url_str, SecurityConfigT&& sec_cfg = {},
351 
352  /**
353  * @brief Heap-allocates a @c SecurityGetter and wraps it in a @c std::shared_ptr.
354  *
355  * @tparam SecurityConfigT Forwardable @c Security::Config compatible type.
356  * @param url_str Field URL string.
357  * @param sec_cfg Security configuration; empty uses the default symmetric slot.
358  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
359  * @return Owning @c SharedPtr to the new secure getter.
360  */
361  // NOLINTNEXTLINE(modernize-use-constraints)
362  template <typename SecurityConfigT = Security::Config>
363  [[nodiscard]] static SharedPtr create_shared(const std::string& url_str, SecurityConfigT&& sec_cfg = {},
365 
366  /**
367  * @brief Constructs a @c SecurityGetter from a typed configuration object.
368  *
369  * @tparam ConfT Configuration type derived from @c Conf.
370  * @tparam SecurityConfigT Forwardable @c Security::Config compatible type.
371  * @param conf Populated configuration aggregate.
372  * @param sec_cfg Security configuration; empty uses the default symmetric slot.
373  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
374  */
375  // NOLINTNEXTLINE(modernize-use-constraints)
376  template <typename ConfT, typename SecurityConfigT = Security::Config,
377  typename = std::enable_if_t<std::is_base_of_v<Conf, ConfT>>>
378  explicit SecurityGetter(const ConfT& conf, SecurityConfigT&& sec_cfg = {}, InitType type = InitType::kWithInit);
379 
380  /**
381  * @brief Constructs a @c SecurityGetter from a URL string and installs the security configuration.
382  *
383  * @details
384  * Builds the base @c Getter in @c kWithoutInit mode, installs @p sec_cfg
385  * via @c enable_security(), then calls @c init() unless deferred. When
386  * @c enable_security() fails to produce a usable @c NodeImpl::security the
387  * subsequent @c init() will fail.
388  *
389  * @tparam SecurityConfigT Forwardable @c Security::Config compatible type.
390  * @param url_str Field URL string.
391  * @param sec_cfg Security configuration; empty uses the default symmetric slot.
392  * @param type Whether to call @c init() inline; default is @c InitType::kWithInit.
393  */
394  // NOLINTNEXTLINE(modernize-use-constraints)
395  template <typename SecurityConfigT = Security::Config>
396  explicit SecurityGetter(const std::string& url_str, SecurityConfigT&& sec_cfg = {},
398 };
399 
400 } // namespace vlink
401 
402 #include "./internal/getter-inl.h"
Monotonic-clock condition variable replacement immune to system clock jumps.
Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction.
Transport-neutral base for field-model getter (latest-value reader) implementations.
Common CRTP base for every VLink communication primitive.