[wpiutil] Struct: Add info template parameter pack (#6086)

This allows using Struct in a dynamically typed environment by passing
additional information to the Struct serialization functions.
This commit is contained in:
Peter Johnson
2023-12-27 07:52:18 -08:00
committed by GitHub
parent e07de37e64
commit 6a2d3c30a6
9 changed files with 995 additions and 316 deletions

View File

@@ -37,9 +37,11 @@ class ProtobufTopic;
class RawTopic;
class StringArrayTopic;
class StringTopic;
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructArrayTopic;
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructTopic;
class Topic;

View File

@@ -37,9 +37,11 @@ class ProtobufTopic;
class RawTopic;
class StringArrayTopic;
class StringTopic;
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructArrayTopic;
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructTopic;
class Subscriber;
class Topic;
@@ -260,19 +262,24 @@ class NetworkTableInstance final {
* Gets a raw struct serialized value topic.
*
* @param name topic name
* @param info optional struct type info
* @return Topic
*/
template <wpi::StructSerializable T>
StructTopic<T> GetStructTopic(std::string_view name) const;
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
StructTopic<T, I...> GetStructTopic(std::string_view name, I... info) const;
/**
* Gets a raw struct serialized array topic.
*
* @param name topic name
* @param info optional struct type info
* @return Topic
*/
template <wpi::StructSerializable T>
StructArrayTopic<T> GetStructArrayTopic(std::string_view name) const;
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
StructArrayTopic<T, I...> GetStructArrayTopic(std::string_view name,
I... info) const;
/**
* Get Published Topics.
@@ -818,10 +825,12 @@ class NetworkTableInstance final {
* Registers a struct schema. Duplicate calls to this function with the same
* name are silently ignored.
*
* @param T struct serializable type
* @tparam T struct serializable type
* @param info optional struct type info
*/
template <wpi::StructSerializable T>
void AddStructSchema();
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
void AddStructSchema(const I&... info);
/**
* Equality operator. Returns true if both instances refer to the same

View File

@@ -44,16 +44,18 @@ inline ProtobufTopic<T> NetworkTableInstance::GetProtobufTopic(
return ProtobufTopic<T>{GetTopic(name)};
}
template <wpi::StructSerializable T>
inline StructTopic<T> NetworkTableInstance::GetStructTopic(
std::string_view name) const {
return StructTopic<T>{GetTopic(name)};
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
inline StructTopic<T, I...> NetworkTableInstance::GetStructTopic(
std::string_view name, I... info) const {
return StructTopic<T, I...>{GetTopic(name), std::move(info)...};
}
template <wpi::StructSerializable T>
inline StructArrayTopic<T> NetworkTableInstance::GetStructArrayTopic(
std::string_view name) const {
return StructArrayTopic<T>{GetTopic(name)};
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
inline StructArrayTopic<T, I...> NetworkTableInstance::GetStructArrayTopic(
std::string_view name, I... info) const {
return StructArrayTopic<T, I...>{GetTopic(name), std::move(info)...};
}
inline std::vector<Topic> NetworkTableInstance::GetTopics() {
@@ -272,11 +274,14 @@ void NetworkTableInstance::AddProtobufSchema(wpi::ProtobufMessage<T>& msg) {
});
}
template <wpi::StructSerializable T>
void NetworkTableInstance::AddStructSchema() {
wpi::ForEachStructSchema<T>([this](auto typeString, auto schema) {
AddSchema(typeString, "structschema", schema);
});
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
void NetworkTableInstance::AddStructSchema(const I&... info) {
wpi::ForEachStructSchema<T>(
[this](auto typeString, auto schema) {
AddSchema(typeString, "structschema", schema);
},
info...);
}
#ifdef __clang__

View File

@@ -7,9 +7,11 @@
#include <stdint.h>
#include <atomic>
#include <functional>
#include <memory>
#include <ranges>
#include <span>
#include <tuple>
#include <utility>
#include <vector>
@@ -24,18 +26,20 @@
namespace nt {
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructArrayTopic;
/**
* NetworkTables struct-encoded value array subscriber.
*/
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructArraySubscriber : public Subscriber {
using S = wpi::Struct<T>;
using S = wpi::Struct<T, I...>;
public:
using TopicType = StructArrayTopic<T>;
using TopicType = StructArrayTopic<T, I...>;
using ValueType = std::vector<T>;
using ParamType = std::span<const T>;
using TimestampedValueType = Timestamped<ValueType>;
@@ -48,15 +52,17 @@ class StructArraySubscriber : public Subscriber {
*
* @param handle Native handle
* @param defaultValue Default value
* @param info optional struct type info
*/
template <typename U>
#if __cpp_lib_ranges >= 201911L
requires std::ranges::range<U> &&
std::convertible_to<std::ranges::range_value_t<U>, T>
#endif
StructArraySubscriber(NT_Subscriber handle, U&& defaultValue)
StructArraySubscriber(NT_Subscriber handle, U&& defaultValue, I... info)
: Subscriber{handle},
m_defaultValue{defaultValue.begin(), defaultValue.end()} {
m_defaultValue{defaultValue.begin(), defaultValue.end()},
m_info{std::move(info)...} {
}
/**
@@ -121,7 +127,7 @@ class StructArraySubscriber : public Subscriber {
#endif
TimestampedValueType GetAtomic(U&& defaultValue) const {
wpi::SmallVector<uint8_t, 128> buf;
size_t size = S::GetSize();
size_t size = std::apply(S::GetSize, m_info);
TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
if (view.value.size() == 0 || (view.value.size() % size) != 0) {
return {0, 0, std::forward<U>(defaultValue)};
@@ -130,8 +136,12 @@ class StructArraySubscriber : public Subscriber {
rv.value.reserve(view.value.size() / size);
for (auto in = view.value.begin(), end = view.value.end(); in != end;
in += size) {
rv.value.emplace_back(
S::Unpack(std::span<const uint8_t>{std::to_address(in), size}));
std::apply(
[&](const I&... info) {
rv.value.emplace_back(S::Unpack(
std::span<const uint8_t>{std::to_address(in), size}, info...));
},
m_info);
}
return rv;
}
@@ -146,7 +156,7 @@ class StructArraySubscriber : public Subscriber {
*/
TimestampedValueType GetAtomic(std::span<const T> defaultValue) const {
wpi::SmallVector<uint8_t, 128> buf;
size_t size = S::GetSize();
size_t size = std::apply(S::GetSize, m_info);
TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
if (view.value.size() == 0 || (view.value.size() % size) != 0) {
return {0, 0, {defaultValue.begin(), defaultValue.end()}};
@@ -155,8 +165,12 @@ class StructArraySubscriber : public Subscriber {
rv.value.reserve(view.value.size() / size);
for (auto in = view.value.begin(), end = view.value.end(); in != end;
in += size) {
rv.value.emplace_back(
S::Unpack(std::span<const uint8_t>{std::to_address(in), size}));
std::apply(
[&](const I&... info) {
rv.value.emplace_back(S::Unpack(
std::span<const uint8_t>{std::to_address(in), size}, info...));
},
m_info);
}
return rv;
}
@@ -176,7 +190,7 @@ class StructArraySubscriber : public Subscriber {
auto raw = ::nt::ReadQueueRaw(m_subHandle);
std::vector<TimestampedValueType> rv;
rv.reserve(raw.size());
size_t size = S::GetSize();
size_t size = std::apply(S::GetSize, m_info);
for (auto&& r : raw) {
if (r.value.size() == 0 || (r.value.size() % size) != 0) {
continue;
@@ -185,8 +199,13 @@ class StructArraySubscriber : public Subscriber {
values.reserve(r.value.size() / size);
for (auto in = r.value.begin(), end = r.value.end(); in != end;
in += size) {
values.emplace_back(
S::Unpack(std::span<const uint8_t>{std::to_address(in), size}));
std::apply(
[&](const I&... info) {
values.emplace_back(
S::Unpack(std::span<const uint8_t>{std::to_address(in), size},
info...));
},
m_info);
}
rv.emplace_back(r.time, r.serverTime, std::move(values));
}
@@ -199,22 +218,29 @@ class StructArraySubscriber : public Subscriber {
* @return Topic
*/
TopicType GetTopic() const {
return StructArrayTopic<T>{::nt::GetTopicFromHandle(m_subHandle)};
return std::apply(
[&](const I&... info) {
return StructArrayTopic<T, I...>{
::nt::GetTopicFromHandle(m_subHandle), info...};
},
m_info);
}
private:
ValueType m_defaultValue;
[[no_unique_address]] std::tuple<I...> m_info;
};
/**
* NetworkTables struct-encoded value array publisher.
*/
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructArrayPublisher : public Publisher {
using S = wpi::Struct<T>;
using S = wpi::Struct<T, I...>;
public:
using TopicType = StructArrayTopic<T>;
using TopicType = StructArrayTopic<T, I...>;
using ValueType = std::vector<T>;
using ParamType = std::span<const T>;
@@ -227,8 +253,10 @@ class StructArrayPublisher : public Publisher {
* StructTopic::Publish() instead.
*
* @param handle Native handle
* @param info optional struct type info
*/
explicit StructArrayPublisher(NT_Publisher handle) : Publisher{handle} {}
explicit StructArrayPublisher(NT_Publisher handle, I... info)
: Publisher{handle}, m_info{std::move(info)...} {}
StructArrayPublisher(const StructArrayPublisher&) = delete;
StructArrayPublisher& operator=(const StructArrayPublisher&) = delete;
@@ -236,7 +264,9 @@ class StructArrayPublisher : public Publisher {
StructArrayPublisher(StructArrayPublisher&& rhs)
: Publisher{std::move(rhs)},
m_buf{std::move(rhs.m_buf)},
m_schemaPublished{rhs.m_schemaPublished} {}
m_schemaPublished{
rhs.m_schemaPublished.load(std::memory_order_relaxed)},
m_info{std::move(rhs.m_info)} {}
StructArrayPublisher& operator=(StructArrayPublisher&& rhs) {
Publisher::operator=(std::move(rhs));
@@ -244,6 +274,7 @@ class StructArrayPublisher : public Publisher {
m_schemaPublished.store(
rhs.m_schemaPublished.load(std::memory_order_relaxed),
std::memory_order_relaxed);
m_info = std::move(rhs.m_info);
return *this;
}
@@ -259,11 +290,17 @@ class StructArrayPublisher : public Publisher {
std::convertible_to<std::ranges::range_value_t<U>, T>
#endif
void Set(U&& value, int64_t time = 0) {
if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
GetTopic().GetInstance().template AddStructSchema<T>();
}
m_buf.Write(std::forward<U>(value),
[&](auto bytes) { ::nt::SetRaw(m_pubHandle, bytes, time); });
std::apply(
[&](const I&... info) {
if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
GetTopic().GetInstance().template AddStructSchema<T>(info...);
}
m_buf.Write(
std::forward<U>(value),
[&](auto bytes) { ::nt::SetRaw(m_pubHandle, bytes, time); },
info...);
},
m_info);
}
/**
@@ -273,8 +310,14 @@ class StructArrayPublisher : public Publisher {
* @param time timestamp; 0 indicates current NT time should be used
*/
void Set(std::span<const T> value, int64_t time = 0) {
m_buf.Write(value,
[&](auto bytes) { ::nt::SetRaw(m_pubHandle, bytes, time); });
std::apply(
[&](const I&... info) {
m_buf.Write(
value,
[&](auto bytes) { ::nt::SetRaw(m_pubHandle, bytes, time); },
info...);
},
m_info);
}
/**
@@ -290,11 +333,17 @@ class StructArrayPublisher : public Publisher {
std::convertible_to<std::ranges::range_value_t<U>, T>
#endif
void SetDefault(U&& value) {
if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
GetTopic().GetInstance().template AddStructSchema<T>();
}
m_buf.Write(std::forward<U>(value),
[&](auto bytes) { ::nt::SetDefaultRaw(m_pubHandle, bytes); });
std::apply(
[&](const I&... info) {
if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
GetTopic().GetInstance().template AddStructSchema<T>(info...);
}
m_buf.Write(
std::forward<U>(value),
[&](auto bytes) { ::nt::SetDefaultRaw(m_pubHandle, bytes); },
info...);
},
m_info);
}
/**
@@ -305,8 +354,14 @@ class StructArrayPublisher : public Publisher {
* @param value value
*/
void SetDefault(std::span<const T> value) {
m_buf.Write(value,
[&](auto bytes) { ::nt::SetDefaultRaw(m_pubHandle, bytes); });
std::apply(
[&](const I&... info) {
m_buf.Write(
value,
[&](auto bytes) { ::nt::SetDefaultRaw(m_pubHandle, bytes); },
info...);
},
m_info);
}
/**
@@ -315,12 +370,18 @@ class StructArrayPublisher : public Publisher {
* @return Topic
*/
TopicType GetTopic() const {
return StructArrayTopic<T>{::nt::GetTopicFromHandle(m_pubHandle)};
return std::apply(
[&](const I&... info) {
return StructArrayTopic<T, I...>{
::nt::GetTopicFromHandle(m_pubHandle), info...};
},
m_info);
}
private:
wpi::StructArrayBuffer<T> m_buf;
wpi::StructArrayBuffer<T, I...> m_buf;
std::atomic_bool m_schemaPublished{false};
[[no_unique_address]] std::tuple<I...> m_info;
};
/**
@@ -328,13 +389,14 @@ class StructArrayPublisher : public Publisher {
*
* @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
*/
template <wpi::StructSerializable T>
class StructArrayEntry final : public StructArraySubscriber<T>,
public StructArrayPublisher<T> {
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructArrayEntry final : public StructArraySubscriber<T, I...>,
public StructArrayPublisher<T, I...> {
public:
using SubscriberType = StructArraySubscriber<T>;
using PublisherType = StructArrayPublisher<T>;
using TopicType = StructArrayTopic<T>;
using SubscriberType = StructArraySubscriber<T, I...>;
using PublisherType = StructArrayPublisher<T, I...>;
using TopicType = StructArrayTopic<T, I...>;
using ValueType = std::vector<T>;
using ParamType = std::span<const T>;
@@ -348,15 +410,16 @@ class StructArrayEntry final : public StructArraySubscriber<T>,
*
* @param handle Native handle
* @param defaultValue Default value
* @param info optional struct type info
*/
template <typename U>
#if __cpp_lib_ranges >= 201911L
requires std::ranges::range<U> &&
std::convertible_to<std::ranges::range_value_t<U>, T>
#endif
StructArrayEntry(NT_Entry handle, U&& defaultValue)
: StructArraySubscriber<T>{handle, defaultValue},
StructArrayPublisher<T>{handle} {
StructArrayEntry(NT_Entry handle, U&& defaultValue, const I&... info)
: StructArraySubscriber<T, I...>{handle, defaultValue, info...},
StructArrayPublisher<T, I...>{handle, info...} {
}
/**
@@ -379,7 +442,7 @@ class StructArrayEntry final : public StructArraySubscriber<T>,
* @return Topic
*/
TopicType GetTopic() const {
return StructArrayTopic<T>{::nt::GetTopicFromHandle(this->m_subHandle)};
return StructArraySubscriber<T, I...>::GetTopic();
}
/**
@@ -391,12 +454,13 @@ class StructArrayEntry final : public StructArraySubscriber<T>,
/**
* NetworkTables struct-encoded value array topic.
*/
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructArrayTopic final : public Topic {
public:
using SubscriberType = StructArraySubscriber<T>;
using PublisherType = StructArrayPublisher<T>;
using EntryType = StructArrayEntry<T>;
using SubscriberType = StructArraySubscriber<T, I...>;
using PublisherType = StructArrayPublisher<T, I...>;
using EntryType = StructArrayEntry<T, I...>;
using ValueType = std::vector<T>;
using ParamType = std::span<const T>;
using TimestampedValueType = Timestamped<ValueType>;
@@ -408,15 +472,19 @@ class StructArrayTopic final : public Topic {
* NetworkTableInstance::GetStructTopic() instead.
*
* @param handle Native handle
* @param info optional struct type info
*/
explicit StructArrayTopic(NT_Topic handle) : Topic{handle} {}
explicit StructArrayTopic(NT_Topic handle, I... info)
: Topic{handle}, m_info{std::move(info)...} {}
/**
* Construct from a generic topic.
*
* @param topic Topic
* @param info optional struct type info
*/
explicit StructArrayTopic(Topic topic) : Topic{topic} {}
explicit StructArrayTopic(Topic topic, I... info)
: Topic{topic}, m_info{std::move(info)...} {}
/**
* Create a new subscriber to the topic.
@@ -441,11 +509,17 @@ class StructArrayTopic final : public Topic {
[[nodiscard]]
SubscriberType Subscribe(
U&& defaultValue, const PubSubOptions& options = kDefaultPubSubOptions) {
return StructArraySubscriber<T>{
::nt::Subscribe(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(), options),
defaultValue};
return std::apply(
[&](const I&... info) {
return StructArraySubscriber<T, I...>{
::nt::Subscribe(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(
info...),
options),
defaultValue, info...};
},
m_info);
}
/**
@@ -467,11 +541,17 @@ class StructArrayTopic final : public Topic {
SubscriberType Subscribe(
std::span<const T> defaultValue,
const PubSubOptions& options = kDefaultPubSubOptions) {
return StructArraySubscriber<T>{
::nt::Subscribe(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(), options),
defaultValue};
return std::apply(
[&](const I&... info) {
return StructArraySubscriber<T, I...>{
::nt::Subscribe(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(
info...),
options),
defaultValue, info...};
},
m_info);
}
/**
@@ -491,9 +571,17 @@ class StructArrayTopic final : public Topic {
*/
[[nodiscard]]
PublisherType Publish(const PubSubOptions& options = kDefaultPubSubOptions) {
return StructArrayPublisher<T>{::nt::Publish(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(), options)};
return std::apply(
[&](const I&... info) {
return StructArrayPublisher<T, I...>{
::nt::Publish(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(
info...),
options),
info...};
},
m_info);
}
/**
@@ -517,10 +605,17 @@ class StructArrayTopic final : public Topic {
PublisherType PublishEx(
const wpi::json& properties,
const PubSubOptions& options = kDefaultPubSubOptions) {
return StructArrayPublisher<T>{::nt::PublishEx(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(), properties,
options)};
return std::apply(
[&](const I&... info) {
return StructArrayPublisher<T, I...>{
::nt::PublishEx(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(
info...),
properties, options),
info...};
},
m_info);
}
/**
@@ -551,11 +646,17 @@ class StructArrayTopic final : public Topic {
[[nodiscard]]
EntryType GetEntry(U&& defaultValue,
const PubSubOptions& options = kDefaultPubSubOptions) {
return StructArrayEntry<T>{
::nt::GetEntry(m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(),
options),
defaultValue};
return std::apply(
[&](const I&... info) {
return StructArrayEntry<T, I...>{
::nt::GetEntry(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(
info...),
options),
defaultValue, info...};
},
m_info);
}
/**
@@ -581,12 +682,21 @@ class StructArrayTopic final : public Topic {
[[nodiscard]]
EntryType GetEntry(std::span<const T> defaultValue,
const PubSubOptions& options = kDefaultPubSubOptions) {
return StructArrayEntry<T>{
::nt::GetEntry(m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(),
options),
defaultValue};
return std::apply(
[&](const I&... info) {
return StructArrayEntry<T, I...>{
::nt::GetEntry(
m_handle, NT_RAW,
wpi::MakeStructArrayTypeString<T, std::dynamic_extent>(
info...),
options),
defaultValue, info...};
},
m_info);
}
private:
[[no_unique_address]] std::tuple<I...> m_info;
};
} // namespace nt

View File

@@ -8,8 +8,10 @@
#include <atomic>
#include <concepts>
#include <functional>
#include <span>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>
@@ -23,25 +25,20 @@
namespace nt {
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructTopic;
/**
* NetworkTables struct-encoded value subscriber.
*/
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructSubscriber : public Subscriber {
using S = wpi::Struct<T>;
static constexpr size_t kBufSize = []() -> size_t {
if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
return S::GetSize();
} else {
return 128;
}
}();
using S = wpi::Struct<T, I...>;
public:
using TopicType = StructTopic<T>;
using TopicType = StructTopic<T, I...>;
using ValueType = T;
using ParamType = const T&;
using TimestampedValueType = Timestamped<T>;
@@ -54,9 +51,12 @@ class StructSubscriber : public Subscriber {
*
* @param handle Native handle
* @param defaultValue Default value
* @param info optional struct type info
*/
StructSubscriber(NT_Subscriber handle, T defaultValue)
: Subscriber{handle}, m_defaultValue{std::move(defaultValue)} {}
StructSubscriber(NT_Subscriber handle, T defaultValue, I... info)
: Subscriber{handle},
m_defaultValue{std::move(defaultValue)},
m_info{std::move(info)...} {}
/**
* Get the last published value.
@@ -88,12 +88,16 @@ class StructSubscriber : public Subscriber {
* @return true if successful
*/
bool GetInto(T* out) {
wpi::SmallVector<uint8_t, kBufSize> buf;
wpi::SmallVector<uint8_t, 128> buf;
TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
if (view.value.size() < S::GetSize()) {
if (view.value.size() < std::apply(S::GetSize, m_info)) {
return false;
} else {
wpi::UnpackStructInto(out, view.value);
std::apply(
[&](const I&... info) {
wpi::UnpackStructInto(out, view.value, info...);
},
m_info);
return true;
}
}
@@ -116,12 +120,16 @@ class StructSubscriber : public Subscriber {
* @return timestamped value
*/
TimestampedValueType GetAtomic(const T& defaultValue) const {
wpi::SmallVector<uint8_t, kBufSize> buf;
wpi::SmallVector<uint8_t, 128> buf;
TimestampedRawView view = ::nt::GetAtomicRaw(m_subHandle, buf, {});
if (view.value.size() < S::GetSize()) {
if (view.value.size() < std::apply(S::GetSize, m_info)) {
return {0, 0, defaultValue};
} else {
return {view.time, view.serverTime, S::Unpack(view.value)};
return {
view.time, view.serverTime,
std::apply(
[&](const I&... info) { return S::Unpack(view.value, info...); },
m_info)};
}
}
@@ -141,11 +149,16 @@ class StructSubscriber : public Subscriber {
std::vector<TimestampedValueType> rv;
rv.reserve(raw.size());
for (auto&& r : raw) {
if (r.value.size() < S::GetSize()) {
if (r.value.size() < std::apply(S::GetSize, m_info)) {
continue;
} else {
rv.emplace_back(r.time, r.serverTime,
S::Unpack(std::span<const uint8_t>(r.value)));
std::apply(
[&](const I&... info) {
rv.emplace_back(
r.time, r.serverTime,
S::Unpack(std::span<const uint8_t>(r.value), info...));
},
m_info);
}
}
return rv;
@@ -157,22 +170,29 @@ class StructSubscriber : public Subscriber {
* @return Topic
*/
TopicType GetTopic() const {
return StructTopic<T>{::nt::GetTopicFromHandle(m_subHandle)};
return std::apply(
[&](const I&... info) {
return StructTopic<T, I...>{::nt::GetTopicFromHandle(m_subHandle),
info...};
},
m_info);
}
private:
ValueType m_defaultValue;
[[no_unique_address]] std::tuple<I...> m_info;
};
/**
* NetworkTables struct-encoded value publisher.
*/
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructPublisher : public Publisher {
using S = wpi::Struct<T>;
using S = wpi::Struct<T, I...>;
public:
using TopicType = StructTopic<T>;
using TopicType = StructTopic<T, I...>;
using ValueType = T;
using ParamType = const T&;
@@ -184,13 +204,17 @@ class StructPublisher : public Publisher {
StructPublisher& operator=(const StructPublisher&) = delete;
StructPublisher(StructPublisher&& rhs)
: Publisher{std::move(rhs)}, m_schemaPublished{rhs.m_schemaPublished} {}
: Publisher{std::move(rhs)},
m_schemaPublished{
rhs.m_schemaPublished.load(std::memory_order_relaxed)},
m_info{std::move(rhs.m_info)} {}
StructPublisher& operator=(StructPublisher&& rhs) {
Publisher::operator=(std::move(rhs));
m_schemaPublished.store(
rhs.m_schemaPublished.load(std::memory_order_relaxed),
std::memory_order_relaxed);
m_info = std::move(rhs.m_info);
return *this;
}
@@ -199,8 +223,10 @@ class StructPublisher : public Publisher {
* StructTopic::Publish() instead.
*
* @param handle Native handle
* @param info optional struct type info
*/
explicit StructPublisher(NT_Publisher handle) : Publisher{handle} {}
explicit StructPublisher(NT_Publisher handle, I... info)
: Publisher{handle}, m_info{std::move(info)...} {}
/**
* Publish a new value.
@@ -210,18 +236,24 @@ class StructPublisher : public Publisher {
*/
void Set(const T& value, int64_t time = 0) {
if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
GetTopic().GetInstance().template AddStructSchema<T>();
std::apply(
[&](const I&... info) {
GetTopic().GetInstance().template AddStructSchema<T>(info...);
},
m_info);
}
if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
uint8_t buf[S::GetSize()];
S::Pack(buf, value);
::nt::SetRaw(m_pubHandle, buf, time);
} else {
wpi::SmallVector<uint8_t, 128> buf;
buf.resize_for_overwrite(S::GetSize());
S::Pack(buf, value);
::nt::SetRaw(m_pubHandle, buf, time);
if constexpr (sizeof...(I) == 0) {
if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
uint8_t buf[S::GetSize()];
S::Pack(buf, value);
::nt::SetRaw(m_pubHandle, buf, time);
return;
}
}
wpi::SmallVector<uint8_t, 128> buf;
buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
std::apply([&](const I&... info) { S::Pack(buf, value, info...); }, m_info);
::nt::SetRaw(m_pubHandle, buf, time);
}
/**
@@ -233,18 +265,24 @@ class StructPublisher : public Publisher {
*/
void SetDefault(const T& value) {
if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
GetTopic().GetInstance().template AddStructSchema<T>();
std::apply(
[&](const I&... info) {
GetTopic().GetInstance().template AddStructSchema<T>(info...);
},
m_info);
}
if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
uint8_t buf[S::GetSize()];
S::Pack(buf, value);
::nt::SetDefaultRaw(m_pubHandle, buf);
} else {
wpi::SmallVector<uint8_t, 128> buf;
buf.resize_for_overwrite(S::GetSize());
S::Pack(buf, value);
::nt::SetDefaultRaw(m_pubHandle, buf);
if constexpr (sizeof...(I) == 0) {
if constexpr (wpi::is_constexpr([] { S::GetSize(); })) {
uint8_t buf[S::GetSize()];
S::Pack(buf, value);
::nt::SetDefaultRaw(m_pubHandle, buf);
return;
}
}
wpi::SmallVector<uint8_t, 128> buf;
buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
std::apply([&](const I&... info) { S::Pack(buf, value, info...); }, m_info);
::nt::SetDefaultRaw(m_pubHandle, buf);
}
/**
@@ -253,11 +291,17 @@ class StructPublisher : public Publisher {
* @return Topic
*/
TopicType GetTopic() const {
return StructTopic<T>{::nt::GetTopicFromHandle(m_pubHandle)};
return std::apply(
[&](const I&... info) {
return StructTopic<T, I...>{::nt::GetTopicFromHandle(m_pubHandle),
info...};
},
m_info);
}
private:
std::atomic_bool m_schemaPublished{false};
[[no_unique_address]] std::tuple<I...> m_info;
};
/**
@@ -265,13 +309,14 @@ class StructPublisher : public Publisher {
*
* @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
*/
template <wpi::StructSerializable T>
class StructEntry final : public StructSubscriber<T>,
public StructPublisher<T> {
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructEntry final : public StructSubscriber<T, I...>,
public StructPublisher<T, I...> {
public:
using SubscriberType = StructSubscriber<T>;
using PublisherType = StructPublisher<T>;
using TopicType = StructTopic<T>;
using SubscriberType = StructSubscriber<T, I...>;
using PublisherType = StructPublisher<T, I...>;
using TopicType = StructTopic<T, I...>;
using ValueType = T;
using ParamType = const T&;
@@ -285,10 +330,11 @@ class StructEntry final : public StructSubscriber<T>,
*
* @param handle Native handle
* @param defaultValue Default value
* @param info optional struct type info
*/
StructEntry(NT_Entry handle, T defaultValue)
: StructSubscriber<T>{handle, std::move(defaultValue)},
StructPublisher<T>{handle} {}
StructEntry(NT_Entry handle, T defaultValue, const I&... info)
: StructSubscriber<T, I...>{handle, std::move(defaultValue), info...},
StructPublisher<T, I...>{handle, info...} {}
/**
* Determines if the native handle is valid.
@@ -309,9 +355,7 @@ class StructEntry final : public StructSubscriber<T>,
*
* @return Topic
*/
TopicType GetTopic() const {
return StructTopic<T>{::nt::GetTopicFromHandle(this->m_subHandle)};
}
TopicType GetTopic() const { return StructSubscriber<T, I...>::GetTopic(); }
/**
* Stops publishing the entry if it's published.
@@ -322,12 +366,13 @@ class StructEntry final : public StructSubscriber<T>,
/**
* NetworkTables struct-encoded value topic.
*/
template <wpi::StructSerializable T>
template <typename T, typename... I>
requires wpi::StructSerializable<T, I...>
class StructTopic final : public Topic {
public:
using SubscriberType = StructSubscriber<T>;
using PublisherType = StructPublisher<T>;
using EntryType = StructEntry<T>;
using SubscriberType = StructSubscriber<T, I...>;
using PublisherType = StructPublisher<T, I...>;
using EntryType = StructEntry<T, I...>;
using ValueType = T;
using ParamType = const T&;
using TimestampedValueType = Timestamped<T>;
@@ -339,15 +384,19 @@ class StructTopic final : public Topic {
* NetworkTableInstance::GetStructTopic() instead.
*
* @param handle Native handle
* @param info optional struct type info
*/
explicit StructTopic(NT_Topic handle) : Topic{handle} {}
explicit StructTopic(NT_Topic handle, I... info)
: Topic{handle}, m_info{std::move(info)...} {}
/**
* Construct from a generic topic.
*
* @param topic Topic
* @param info optional struct type info
*/
explicit StructTopic(Topic topic) : Topic{topic} {}
explicit StructTopic(Topic topic, I... info)
: Topic{topic}, m_info{std::move(info)...} {}
/**
* Create a new subscriber to the topic.
@@ -367,10 +416,15 @@ class StructTopic final : public Topic {
[[nodiscard]]
SubscriberType Subscribe(
T defaultValue, const PubSubOptions& options = kDefaultPubSubOptions) {
return StructSubscriber<T>{
::nt::Subscribe(m_handle, NT_RAW, wpi::GetStructTypeString<T>(),
options),
std::move(defaultValue)};
return std::apply(
[&](const I&... info) {
return StructSubscriber<T, I...>{
::nt::Subscribe(m_handle, NT_RAW,
wpi::GetStructTypeString<T, I...>(info...),
options),
std::move(defaultValue), info...};
},
m_info);
}
/**
@@ -390,8 +444,15 @@ class StructTopic final : public Topic {
*/
[[nodiscard]]
PublisherType Publish(const PubSubOptions& options = kDefaultPubSubOptions) {
return StructPublisher<T>{::nt::Publish(
m_handle, NT_RAW, wpi::GetStructTypeString<T>(), options)};
return std::apply(
[&](const I&... info) {
return StructPublisher<T, I...>{
::nt::Publish(m_handle, NT_RAW,
wpi::GetStructTypeString<T, I...>(info...),
options),
info...};
},
m_info);
}
/**
@@ -415,8 +476,15 @@ class StructTopic final : public Topic {
PublisherType PublishEx(
const wpi::json& properties,
const PubSubOptions& options = kDefaultPubSubOptions) {
return StructPublisher<T>{::nt::PublishEx(
m_handle, NT_RAW, wpi::GetStructTypeString<T>(), properties, options)};
return std::apply(
[&](const I&... info) {
return StructPublisher<T, I...>{
::nt::PublishEx(m_handle, NT_RAW,
wpi::GetStructTypeString<T, I...>(info...),
properties, options),
info...};
},
m_info);
}
/**
@@ -442,11 +510,19 @@ class StructTopic final : public Topic {
[[nodiscard]]
EntryType GetEntry(T defaultValue,
const PubSubOptions& options = kDefaultPubSubOptions) {
return StructEntry<T>{
::nt::GetEntry(m_handle, NT_RAW, wpi::GetStructTypeString<T>(),
options),
std::move(defaultValue)};
return std::apply(
[&](const I&... info) {
return StructEntry<T, I...>{
::nt::GetEntry(m_handle, NT_RAW,
wpi::GetStructTypeString<T, I...>(info...),
options),
std::move(defaultValue), info...};
},
m_info);
}
private:
[[no_unique_address]] std::tuple<I...> m_info;
};
} // namespace nt