WPILibC++ 2025.0.0-alpha-1-14-g3b6f38d
StructTopic.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <stdint.h>
8
9#include <atomic>
10#include <concepts>
11#include <functional>
12#include <span>
13#include <string_view>
14#include <tuple>
15#include <utility>
16#include <vector>
17
18#include <wpi/SmallVector.h>
19#include <wpi/json_fwd.h>
20#include <wpi/struct/Struct.h>
21
23#include "networktables/Topic.h"
24#include "ntcore_cpp.h"
25
26namespace nt {
27
28template <typename T, typename... I>
29 requires wpi::StructSerializable<T, I...>
30class StructTopic;
31
32/**
33 * NetworkTables struct-encoded value subscriber.
34 */
35template <typename T, typename... I>
36 requires wpi::StructSerializable<T, I...>
38 using S = wpi::Struct<T, I...>;
39
40 public:
41 using TopicType = StructTopic<T, I...>;
42 using ValueType = T;
43 using ParamType = const T&;
45
46 StructSubscriber() = default;
47
48 /**
49 * Construct from a subscriber handle; recommended to use
50 * StructTopic::Subscribe() instead.
51 *
52 * @param handle Native handle
53 * @param defaultValue Default value
54 * @param info optional struct type info
55 */
56 StructSubscriber(NT_Subscriber handle, T defaultValue, I... info)
57 : Subscriber{handle},
58 m_defaultValue{std::move(defaultValue)},
59 m_info{std::move(info)...} {}
60
61 /**
62 * Get the last published value.
63 * If no value has been published or the value cannot be unpacked, returns the
64 * stored default value.
65 *
66 * @return value
67 */
68 ValueType Get() const { return Get(m_defaultValue); }
69
70 /**
71 * Get the last published value.
72 * If no value has been published or the value cannot be unpacked, returns the
73 * passed defaultValue.
74 *
75 * @param defaultValue default value to return if no value has been published
76 * @return value
77 */
78 ValueType Get(const T& defaultValue) const {
79 return GetAtomic(defaultValue).value;
80 }
81
82 /**
83 * Get the last published value, replacing the contents in place of an
84 * existing object. If no value has been published or the value cannot be
85 * unpacked, does not replace the contents and returns false.
86 *
87 * @param[out] out object to replace contents of
88 * @return true if successful
89 */
90 bool GetInto(T* out) {
93 if (view.value.size() < std::apply(S::GetSize, m_info)) {
94 return false;
95 } else {
96 std::apply(
97 [&](const I&... info) {
98 wpi::UnpackStructInto(out, view.value, info...);
99 },
100 m_info);
101 return true;
102 }
103 }
104
105 /**
106 * Get the last published value along with its timestamp
107 * If no value has been published or the value cannot be unpacked, returns the
108 * stored default value and a timestamp of 0.
109 *
110 * @return timestamped value
111 */
112 TimestampedValueType GetAtomic() const { return GetAtomic(m_defaultValue); }
113
114 /**
115 * Get the last published value along with its timestamp.
116 * If no value has been published or the value cannot be unpacked, returns the
117 * passed defaultValue and a timestamp of 0.
118 *
119 * @param defaultValue default value to return if no value has been published
120 * @return timestamped value
121 */
122 TimestampedValueType GetAtomic(const T& defaultValue) const {
125 if (view.value.size() < std::apply(S::GetSize, m_info)) {
126 return {0, 0, defaultValue};
127 } else {
128 return {
129 view.time, view.serverTime,
130 std::apply(
131 [&](const I&... info) { return S::Unpack(view.value, info...); },
132 m_info)};
133 }
134 }
135
136 /**
137 * Get an array of all valid value changes since the last call to ReadQueue.
138 * Also provides a timestamp for each value. Values that cannot be unpacked
139 * are dropped.
140 *
141 * @note The "poll storage" subscribe option can be used to set the queue
142 * depth.
143 *
144 * @return Array of timestamped values; empty array if no valid new changes
145 * have been published since the previous call.
146 */
147 std::vector<TimestampedValueType> ReadQueue() {
149 std::vector<TimestampedValueType> rv;
150 rv.reserve(raw.size());
151 for (auto&& r : raw) {
152 if (r.value.size() < std::apply(S::GetSize, m_info)) {
153 continue;
154 } else {
155 std::apply(
156 [&](const I&... info) {
157 rv.emplace_back(
158 r.time, r.serverTime,
159 S::Unpack(std::span<const uint8_t>(r.value), info...));
160 },
161 m_info);
162 }
163 }
164 return rv;
165 }
166
167 /**
168 * Get the corresponding topic.
169 *
170 * @return Topic
171 */
173 return std::apply(
174 [&](const I&... info) {
176 info...};
177 },
178 m_info);
179 }
180
181 private:
182 ValueType m_defaultValue;
183 [[no_unique_address]]
184 std::tuple<I...> m_info;
185};
186
187/**
188 * NetworkTables struct-encoded value publisher.
189 */
190template <typename T, typename... I>
191 requires wpi::StructSerializable<T, I...>
193 using S = wpi::Struct<T, I...>;
194
195 public:
196 using TopicType = StructTopic<T, I...>;
197 using ValueType = T;
198 using ParamType = const T&;
199
201
202 StructPublisher() = default;
203
206
208 : Publisher{std::move(rhs)},
209 m_schemaPublished{
210 rhs.m_schemaPublished.load(std::memory_order_relaxed)},
211 m_info{std::move(rhs.m_info)} {}
212
214 Publisher::operator=(std::move(rhs));
215 m_schemaPublished.store(
216 rhs.m_schemaPublished.load(std::memory_order_relaxed),
217 std::memory_order_relaxed);
218 m_info = std::move(rhs.m_info);
219 return *this;
220 }
221
222 /**
223 * Construct from a publisher handle; recommended to use
224 * StructTopic::Publish() instead.
225 *
226 * @param handle Native handle
227 * @param info optional struct type info
228 */
229 explicit StructPublisher(NT_Publisher handle, I... info)
230 : Publisher{handle}, m_info{std::move(info)...} {}
231
232 /**
233 * Publish a new value.
234 *
235 * @param value value to publish
236 * @param time timestamp; 0 indicates current NT time should be used
237 */
238 void Set(const T& value, int64_t time = 0) {
239 if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
240 std::apply(
241 [&](const I&... info) {
242 GetTopic().GetInstance().template AddStructSchema<T>(info...);
243 },
244 m_info);
245 }
246 if constexpr (sizeof...(I) == 0) {
247 if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
248 uint8_t buf[S::GetSize()];
249 S::Pack(buf, value);
250 ::nt::SetRaw(m_pubHandle, buf, time);
251 return;
252 }
253 }
255 buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
256 std::apply([&](const I&... info) { S::Pack(buf, value, info...); }, m_info);
257 ::nt::SetRaw(m_pubHandle, buf, time);
258 }
259
260 /**
261 * Publish a default value.
262 * On reconnect, a default value will never be used in preference to a
263 * published value.
264 *
265 * @param value value
266 */
267 void SetDefault(const T& value) {
268 if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
269 std::apply(
270 [&](const I&... info) {
271 GetTopic().GetInstance().template AddStructSchema<T>(info...);
272 },
273 m_info);
274 }
275 if constexpr (sizeof...(I) == 0) {
276 if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
277 uint8_t buf[S::GetSize()];
278 S::Pack(buf, value);
280 return;
281 }
282 }
284 buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
285 std::apply([&](const I&... info) { S::Pack(buf, value, info...); }, m_info);
287 }
288
289 /**
290 * Get the corresponding topic.
291 *
292 * @return Topic
293 */
295 return std::apply(
296 [&](const I&... info) {
298 info...};
299 },
300 m_info);
301 }
302
303 private:
304 std::atomic_bool m_schemaPublished{false};
305 [[no_unique_address]]
306 std::tuple<I...> m_info;
307};
308
309/**
310 * NetworkTables struct-encoded value entry.
311 *
312 * @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
313 */
314template <typename T, typename... I>
315 requires wpi::StructSerializable<T, I...>
316class StructEntry final : public StructSubscriber<T, I...>,
317 public StructPublisher<T, I...> {
318 public:
321 using TopicType = StructTopic<T, I...>;
322 using ValueType = T;
323 using ParamType = const T&;
324
326
327 StructEntry() = default;
328
329 /**
330 * Construct from an entry handle; recommended to use
331 * StructTopic::GetEntry() instead.
332 *
333 * @param handle Native handle
334 * @param defaultValue Default value
335 * @param info optional struct type info
336 */
337 StructEntry(NT_Entry handle, T defaultValue, const I&... info)
338 : StructSubscriber<T, I...>{handle, std::move(defaultValue), info...},
339 StructPublisher<T, I...>{handle, info...} {}
340
341 /**
342 * Determines if the native handle is valid.
343 *
344 * @return True if the native handle is valid, false otherwise.
345 */
346 explicit operator bool() const { return this->m_subHandle != 0; }
347
348 /**
349 * Gets the native handle for the entry.
350 *
351 * @return Native handle
352 */
353 NT_Entry GetHandle() const { return this->m_subHandle; }
354
355 /**
356 * Get the corresponding topic.
357 *
358 * @return Topic
359 */
361
362 /**
363 * Stops publishing the entry if it's published.
364 */
366};
367
368/**
369 * NetworkTables struct-encoded value topic.
370 */
371template <typename T, typename... I>
372 requires wpi::StructSerializable<T, I...>
373class StructTopic final : public Topic {
374 public:
377 using EntryType = StructEntry<T, I...>;
378 using ValueType = T;
379 using ParamType = const T&;
381
382 StructTopic() = default;
383
384 /**
385 * Construct from a topic handle; recommended to use
386 * NetworkTableInstance::GetStructTopic() instead.
387 *
388 * @param handle Native handle
389 * @param info optional struct type info
390 */
391 explicit StructTopic(NT_Topic handle, I... info)
392 : Topic{handle}, m_info{std::move(info)...} {}
393
394 /**
395 * Construct from a generic topic.
396 *
397 * @param topic Topic
398 * @param info optional struct type info
399 */
400 explicit StructTopic(Topic topic, I... info)
401 : Topic{topic}, m_info{std::move(info)...} {}
402
403 /**
404 * Create a new subscriber to the topic.
405 *
406 * <p>The subscriber is only active as long as the returned object
407 * is not destroyed.
408 *
409 * @note Subscribers that do not match the published data type do not return
410 * any values. To determine if the data type matches, use the appropriate
411 * Topic functions.
412 *
413 * @param defaultValue default value used when a default is not provided to a
414 * getter function
415 * @param options subscribe options
416 * @return subscriber
417 */
418 [[nodiscard]]
420 T defaultValue, const PubSubOptions& options = kDefaultPubSubOptions) {
421 return std::apply(
422 [&](const I&... info) {
423 return StructSubscriber<T, I...>{
425 wpi::GetStructTypeString<T, I...>(info...),
426 options),
427 std::move(defaultValue), info...};
428 },
429 m_info);
430 }
431
432 /**
433 * Create a new publisher to the topic.
434 *
435 * The publisher is only active as long as the returned object
436 * is not destroyed.
437 *
438 * @note It is not possible to publish two different data types to the same
439 * topic. Conflicts between publishers are typically resolved by the
440 * server on a first-come, first-served basis. Any published values that
441 * do not match the topic's data type are dropped (ignored). To determine
442 * if the data type matches, use the appropriate Topic functions.
443 *
444 * @param options publish options
445 * @return publisher
446 */
447 [[nodiscard]]
449 return std::apply(
450 [&](const I&... info) {
451 return StructPublisher<T, I...>{
453 wpi::GetStructTypeString<T, I...>(info...),
454 options),
455 info...};
456 },
457 m_info);
458 }
459
460 /**
461 * Create a new publisher to the topic, with type string and initial
462 * properties.
463 *
464 * The publisher is only active as long as the returned object
465 * is not destroyed.
466 *
467 * @note It is not possible to publish two different data types to the same
468 * topic. Conflicts between publishers are typically resolved by the
469 * server on a first-come, first-served basis. Any published values that
470 * do not match the topic's data type are dropped (ignored). To determine
471 * if the data type matches, use the appropriate Topic functions.
472 *
473 * @param properties JSON properties
474 * @param options publish options
475 * @return publisher
476 */
477 [[nodiscard]]
479 const wpi::json& properties,
480 const PubSubOptions& options = kDefaultPubSubOptions) {
481 return std::apply(
482 [&](const I&... info) {
483 return StructPublisher<T, I...>{
485 wpi::GetStructTypeString<T, I...>(info...),
486 properties, options),
487 info...};
488 },
489 m_info);
490 }
491
492 /**
493 * Create a new entry for the topic.
494 *
495 * Entries act as a combination of a subscriber and a weak publisher. The
496 * subscriber is active as long as the entry is not destroyed. The publisher
497 * is created when the entry is first written to, and remains active until
498 * either Unpublish() is called or the entry is destroyed.
499 *
500 * @note It is not possible to use two different data types with the same
501 * topic. Conflicts between publishers are typically resolved by the
502 * server on a first-come, first-served basis. Any published values that
503 * do not match the topic's data type are dropped (ignored), and the entry
504 * will show no new values if the data type does not match. To determine
505 * if the data type matches, use the appropriate Topic functions.
506 *
507 * @param defaultValue default value used when a default is not provided to a
508 * getter function
509 * @param options publish and/or subscribe options
510 * @return entry
511 */
512 [[nodiscard]]
513 EntryType GetEntry(T defaultValue,
514 const PubSubOptions& options = kDefaultPubSubOptions) {
515 return std::apply(
516 [&](const I&... info) {
517 return StructEntry<T, I...>{
519 wpi::GetStructTypeString<T, I...>(info...),
520 options),
521 std::move(defaultValue), info...};
522 },
523 m_info);
524 }
525
526 private:
527 [[no_unique_address]]
528 std::tuple<I...> m_info;
529};
530
531} // namespace nt
This file defines the SmallVector class.
NetworkTables publisher.
Definition: Topic.h:364
Publisher & operator=(const Publisher &)=delete
NT_Publisher m_pubHandle
NetworkTables handle.
Definition: Topic.h:400
NetworkTables struct-encoded value entry.
Definition: StructTopic.h:317
T ValueType
Definition: StructTopic.h:322
TopicType GetTopic() const
Get the corresponding topic.
Definition: StructTopic.h:360
NT_Entry GetHandle() const
Gets the native handle for the entry.
Definition: StructTopic.h:353
StructEntry(NT_Entry handle, T defaultValue, const I &... info)
Construct from an entry handle; recommended to use StructTopic::GetEntry() instead.
Definition: StructTopic.h:337
const T & ParamType
Definition: StructTopic.h:323
void Unpublish()
Stops publishing the entry if it's published.
Definition: StructTopic.h:365
StructEntry()=default
NetworkTables struct-encoded value publisher.
Definition: StructTopic.h:192
StructPublisher(StructPublisher &&rhs)
Definition: StructTopic.h:207
StructPublisher()=default
StructPublisher & operator=(const StructPublisher &)=delete
StructPublisher(const StructPublisher &)=delete
T ValueType
Definition: StructTopic.h:197
void Set(const T &value, int64_t time=0)
Publish a new value.
Definition: StructTopic.h:238
StructPublisher(NT_Publisher handle, I... info)
Construct from a publisher handle; recommended to use StructTopic::Publish() instead.
Definition: StructTopic.h:229
StructPublisher & operator=(StructPublisher &&rhs)
Definition: StructTopic.h:213
const T & ParamType
Definition: StructTopic.h:198
TopicType GetTopic() const
Get the corresponding topic.
Definition: StructTopic.h:294
void SetDefault(const T &value)
Publish a default value.
Definition: StructTopic.h:267
NetworkTables struct-encoded value subscriber.
Definition: StructTopic.h:37
TimestampedValueType GetAtomic(const T &defaultValue) const
Get the last published value along with its timestamp.
Definition: StructTopic.h:122
TimestampedValueType GetAtomic() const
Get the last published value along with its timestamp If no value has been published or the value can...
Definition: StructTopic.h:112
TopicType GetTopic() const
Get the corresponding topic.
Definition: StructTopic.h:172
StructSubscriber(NT_Subscriber handle, T defaultValue, I... info)
Construct from a subscriber handle; recommended to use StructTopic::Subscribe() instead.
Definition: StructTopic.h:56
bool GetInto(T *out)
Get the last published value, replacing the contents in place of an existing object.
Definition: StructTopic.h:90
std::vector< TimestampedValueType > ReadQueue()
Get an array of all valid value changes since the last call to ReadQueue.
Definition: StructTopic.h:147
StructSubscriber()=default
ValueType Get(const T &defaultValue) const
Get the last published value.
Definition: StructTopic.h:78
T ValueType
Definition: StructTopic.h:42
ValueType Get() const
Get the last published value.
Definition: StructTopic.h:68
const T & ParamType
Definition: StructTopic.h:43
NetworkTables struct-encoded value topic.
Definition: StructTopic.h:373
const T & ParamType
Definition: StructTopic.h:379
PublisherType PublishEx(const wpi::json &properties, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new publisher to the topic, with type string and initial properties.
Definition: StructTopic.h:478
StructTopic()=default
T ValueType
Definition: StructTopic.h:378
SubscriberType Subscribe(T defaultValue, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new subscriber to the topic.
Definition: StructTopic.h:419
EntryType GetEntry(T defaultValue, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new entry for the topic.
Definition: StructTopic.h:513
PublisherType Publish(const PubSubOptions &options=kDefaultPubSubOptions)
Create a new publisher to the topic.
Definition: StructTopic.h:448
StructTopic(Topic topic, I... info)
Construct from a generic topic.
Definition: StructTopic.h:400
StructTopic(NT_Topic handle, I... info)
Construct from a topic handle; recommended to use NetworkTableInstance::GetStructTopic() instead.
Definition: StructTopic.h:391
NetworkTables subscriber.
Definition: Topic.h:309
NT_Subscriber m_subHandle
Definition: Topic.h:360
NetworkTables Topic.
Definition: Topic.h:28
NT_Topic m_handle
Definition: Topic.h:305
NetworkTableInstance GetInstance() const
Gets the instance for the topic.
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1211
void resize_for_overwrite(size_type N)
Like resize, but T is POD, the new values won't be initialized.
Definition: SmallVector.h:656
Specifies that a type is capable of raw struct serialization and deserialization.
Definition: Struct.h:68
bool SetDefaultRaw(NT_Handle pubentry, std::span< const uint8_t > defaultValue)
Publish a default value.
std::vector< TimestampedRaw > ReadQueueRaw(NT_Handle subentry)
Get an array of all value changes since the last call to ReadQueue.
bool SetRaw(NT_Handle pubentry, std::span< const uint8_t > value, int64_t time=0)
Publish a new value.
TimestampedRaw GetAtomicRaw(NT_Handle subentry, std::span< const uint8_t > defaultValue)
Get the last published value along with its timestamp.
NT_Handle NT_Topic
Definition: ntcore_c.h:42
NT_Handle NT_Subscriber
Definition: ntcore_c.h:43
NT_Handle NT_Publisher
Definition: ntcore_c.h:44
NT_Handle NT_Entry
Definition: ntcore_c.h:37
@ NT_RAW
Definition: ntcore_c.h:58
constexpr PubSubOptions kDefaultPubSubOptions
Default publish/subscribe options.
Definition: ntcore_cpp.h:390
NT_Entry GetEntry(NT_Inst inst, std::string_view name)
Get Entry Handle.
NT_Topic GetTopicFromHandle(NT_Handle pubsubentry)
Gets the topic handle from an entry/subscriber/publisher handle.
NT_Publisher PublishEx(NT_Topic topic, NT_Type type, std::string_view typeStr, const wpi::json &properties, const PubSubOptions &options=kDefaultPubSubOptions)
Creates a new publisher to a topic.
NT_Publisher Publish(NT_Topic topic, NT_Type type, std::string_view typeStr, const PubSubOptions &options=kDefaultPubSubOptions)
Creates a new publisher to a topic.
void Unpublish(NT_Handle pubentry)
Stops publisher.
NT_Subscriber Subscribe(NT_Topic topic, NT_Type type, std::string_view typeStr, const PubSubOptions &options=kDefaultPubSubOptions)
Creates a new subscriber to value changes on a topic.
NetworkTables (ntcore) namespace.
Definition: NetworkTableListener.inc:19
Implement std::hash so that hash_code can be used in STL containers.
Definition: array.h:89
void UnpackStructInto(T *out, std::span< const uint8_t > data, const I &... info)
Unpack a serialized struct into an existing object, overwriting its contents.
Definition: Struct.h:200
constexpr bool is_constexpr(Lambda)
Definition: type_traits.h:81
NetworkTables publish/subscribe options.
Definition: ntcore_cpp.h:305
Timestamped value.
Definition: ntcore_cpp_types.h:30
int64_t time
Time in local time base.
Definition: ntcore_cpp_types.h:38
int64_t serverTime
Time in server time base.
Definition: ntcore_cpp_types.h:43
T value
Value.
Definition: ntcore_cpp_types.h:48
Struct serialization template.
Definition: Struct.h:38