libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
callback.hpp
1#pragma once
2
3#include <new>
4
5#include "../topic.hpp"
6#include "libxr_def.hpp"
7
8namespace LibXR
9{
16{
23 template <typename Function>
24 struct FunctionTraits;
25
33 template <typename Return, typename... Args>
34 struct FunctionTraits<Return (*)(Args...)>
35 {
36 using ReturnType = Return;
37 static constexpr size_t ARITY = sizeof...(Args);
38
43 template <size_t Index>
44 using Arg = std::tuple_element_t<Index, std::tuple<Args...>>;
45 };
46
53 template <typename T>
54 struct MessageViewTraits
55 {
56 static constexpr bool VALUE =
57 false;
58 };
59
67 template <typename Data>
68 struct MessageViewTraits<MessageView<Data>>
69 {
70 static constexpr bool VALUE =
71 true;
72 using DataType =
73 Data;
74 };
75
81 template <typename T>
82 using RemoveCVRef = std::remove_cv_t<std::remove_reference_t<T>>;
83
89 template <typename T>
90 static constexpr bool IS_MESSAGE_VIEW =
91 MessageViewTraits<RemoveCVRef<T>>::VALUE;
94
100 template <typename T>
101 static constexpr bool IS_RAW_DATA_VIEW = std::same_as<RemoveCVRef<T>, ConstRawData> ||
102 std::same_as<RemoveCVRef<T>, RawMessageView>;
103
109 template <typename T>
110 static constexpr bool IS_RAW_DATA = std::same_as<RemoveCVRef<T>, RawData>;
111
117 template <typename T>
118 static constexpr bool IS_TYPED_DATA =
119 !IS_MESSAGE_VIEW<T> && !IS_RAW_DATA_VIEW<T> &&
123
129 template <typename T>
130 static constexpr bool IS_CALLBACK_PAYLOAD =
131 IS_MESSAGE_VIEW<T> || IS_RAW_DATA_VIEW<T> ||
132 IS_TYPED_DATA<T>;
134
140 template <typename T>
141 static constexpr bool IS_MUTABLE_MESSAGE_VIEW_REF =
142 IS_MESSAGE_VIEW<T> && std::is_lvalue_reference_v<T> &&
143 !std::is_const_v<std::remove_reference_t<T>>;
144
158 struct BlockHeader
159 {
160 using RunFun = void (*)(const BlockHeader*, bool, MicrosecondTimestamp, void*,
161 size_t);
163
164 RunFun run = nullptr;
166 TypeID::ID payload_type_id = nullptr;
168 bool accepts_raw_payload = false;
170 };
171
178 template <typename PayloadArg>
179 static TypeID::ID PayloadTypeID()
180 {
181 static_assert(!IS_MUTABLE_MESSAGE_VIEW_REF<PayloadArg>,
182 "LibXR::Topic::Callback does not accept MessageView<T>&; "
183 "use MessageView<T> or const MessageView<T>& instead.");
184 static_assert(!IS_RAW_DATA<PayloadArg>,
185 "LibXR::Topic::Callback does not accept RawData as the payload; "
186 "use the topic's payload type T (keeps the type check), or "
187 "ConstRawData for raw bytes.");
188
189 if constexpr (IS_RAW_DATA_VIEW<PayloadArg>)
190 {
191 return nullptr;
192 }
193 else if constexpr (IS_MESSAGE_VIEW<PayloadArg>)
194 {
195 using View = MessageViewTraits<RemoveCVRef<PayloadArg>>;
197 }
198 else
199 {
200 static_assert(IS_TYPED_DATA<PayloadArg>,
201 "LibXR::Topic::Callback payload must be Topic::MessageView<T>, "
202 "Topic::RawMessageView, ConstRawData, T, T&, or const T&.");
204 }
205 }
206
213 template <typename PayloadArg>
214 static constexpr bool AcceptsRawPayload()
215 {
216 return IS_RAW_DATA_VIEW<PayloadArg>;
217 }
218
233 template <typename Function, typename BoundArg, typename PayloadArg>
234 static void InvokePayload(Function fun, BoundArg& arg, bool in_isr,
235 MicrosecondTimestamp timestamp, void* payload_addr,
236 size_t payload_size)
237 {
238 if constexpr (std::same_as<RemoveCVRef<PayloadArg>, ConstRawData>)
239 {
240 ConstRawData raw_payload{payload_addr, payload_size};
241 fun(in_isr, arg, raw_payload);
242 }
243 else if constexpr (std::same_as<RemoveCVRef<PayloadArg>, RawMessageView>)
244 {
245 RawMessageView raw_message{timestamp, ConstRawData{payload_addr, payload_size}};
246 fun(in_isr, arg, raw_message);
247 }
248 else if constexpr (IS_MESSAGE_VIEW<PayloadArg>)
249 {
250 using View = MessageViewTraits<RemoveCVRef<PayloadArg>>;
251 using Data = typename View::DataType;
252 fun(in_isr, arg,
253 MessageView<Data>{timestamp, reinterpret_cast<Data*>(payload_addr)});
254 }
255 else
256 {
257 using Data = RemoveCVRef<PayloadArg>;
258 fun(in_isr, arg, *reinterpret_cast<Data*>(payload_addr));
259 }
260 }
261
269 template <typename Function, typename BoundArg, typename PayloadArg>
270 struct PayloadOnlyBlock : public BlockHeader
271 {
278 PayloadOnlyBlock(Function fun, BoundArg&& arg)
279 : BlockHeader{&Run, PayloadTypeID<PayloadArg>(), AcceptsRawPayload<PayloadArg>()},
280 fun_(fun),
281 arg_(std::move(arg))
282 {
283 }
284
294 static void Run(const BlockHeader* header, bool in_isr,
295 MicrosecondTimestamp timestamp, void* payload_addr,
296 size_t payload_size)
297 {
298 auto* block = static_cast<const PayloadOnlyBlock*>(header);
299 InvokePayload<Function, BoundArg, PayloadArg>(
300 block->fun_, const_cast<BoundArg&>(block->arg_), in_isr, timestamp,
301 payload_addr, payload_size);
302 }
303
304 Function fun_;
305 BoundArg arg_;
306 };
307
316 template <typename Function, typename BoundArg, typename PayloadArg>
317 struct TimestampPayloadBlock : public BlockHeader
318 {
325 TimestampPayloadBlock(Function fun, BoundArg&& arg)
326 : BlockHeader{&Run, PayloadTypeID<PayloadArg>(), AcceptsRawPayload<PayloadArg>()},
327 fun_(fun),
328 arg_(std::move(arg))
329 {
330 }
331
341 static void Run(const BlockHeader* header, bool in_isr,
342 MicrosecondTimestamp timestamp, void* payload_addr,
343 size_t payload_size)
344 {
345 auto* block = static_cast<const TimestampPayloadBlock*>(header);
346 if constexpr (std::same_as<RemoveCVRef<PayloadArg>, ConstRawData>)
347 {
348 ConstRawData raw_payload{payload_addr, payload_size};
349 block->fun_(in_isr, block->arg_, timestamp, raw_payload);
350 }
351 else if constexpr (std::same_as<RemoveCVRef<PayloadArg>, RawMessageView>)
352 {
353 RawMessageView raw_message{timestamp, ConstRawData{payload_addr, payload_size}};
354 block->fun_(in_isr, block->arg_, timestamp, raw_message);
355 }
356 else if constexpr (IS_MESSAGE_VIEW<PayloadArg>)
357 {
358 using View = MessageViewTraits<RemoveCVRef<PayloadArg>>;
359 using Data = typename View::DataType;
360 block->fun_(in_isr, block->arg_, timestamp,
361 MessageView<Data>{timestamp, reinterpret_cast<Data*>(payload_addr)});
362 }
363 else
364 {
365 using Data = RemoveCVRef<PayloadArg>;
366 block->fun_(in_isr, block->arg_, timestamp,
367 *reinterpret_cast<Data*>(payload_addr));
368 }
369 }
370
371 Function fun_;
372 BoundArg arg_;
373 };
374
383 template <typename Function, typename BoundArg, size_t Arity>
384 struct Factory
385 {
386 static_assert(Arity == 3 || Arity == 4,
387 "LibXR::Topic::Callback function must be void(bool, Arg, Payload) "
388 "or void(bool, Arg, MicrosecondTimestamp, Payload).");
389 };
390
397 template <typename Function, typename BoundArg>
398 struct Factory<Function, BoundArg, 3>
399 {
400 using Traits = FunctionTraits<Function>;
401 using PayloadArg = typename Traits::template Arg<2>;
404
412 static BlockHeader* Create(Function fun, BoundArg&& arg)
413 {
414 static_assert(std::same_as<typename Traits::ReturnType, void>);
415 static_assert(std::same_as<typename Traits::template Arg<0>, bool>);
416 static_assert(std::same_as<typename Traits::template Arg<1>, BoundArg>);
417 static_assert(IS_CALLBACK_PAYLOAD<PayloadArg>);
418 return new PayloadOnlyBlock<Function, BoundArg, PayloadArg>(fun, std::move(arg));
419 }
420 };
421
428 template <typename Function, typename BoundArg>
429 struct Factory<Function, BoundArg, 4>
430 {
431 using Traits = FunctionTraits<Function>;
432 using TimestampArg =
433 typename Traits::template Arg<2>;
436 using PayloadArg = typename Traits::template Arg<3>;
439
447 static BlockHeader* Create(Function fun, BoundArg&& arg)
448 {
449 static_assert(std::same_as<typename Traits::ReturnType, void>);
450 static_assert(std::same_as<typename Traits::template Arg<0>, bool>);
451 static_assert(std::same_as<typename Traits::template Arg<1>, BoundArg>);
452 static_assert(std::same_as<RemoveCVRef<TimestampArg>, MicrosecondTimestamp>);
453 static_assert(IS_CALLBACK_PAYLOAD<PayloadArg>);
454 return new TimestampPayloadBlock<Function, BoundArg, PayloadArg>(fun,
455 std::move(arg));
456 }
457 };
458
467 static void EmptyRun(const BlockHeader* header, bool in_isr,
468 MicrosecondTimestamp timestamp, void* payload_addr,
469 size_t payload_size)
470 {
471 UNUSED(header);
472 UNUSED(in_isr);
473 UNUSED(timestamp);
474 UNUSED(payload_addr);
475 UNUSED(payload_size);
476 }
477
478 inline static BlockHeader empty_block_{
479 &EmptyRun, nullptr, false};
481
482 public:
486 Callback() = default;
487
495 Callback(const Callback& other) = default;
496
505 Callback& operator=(const Callback& other) = default;
506
518 template <typename BoundArg, typename Callable>
519 [[nodiscard]] static Callback Create(Callable fun, BoundArg arg)
520 {
521 using Function = decltype(+std::declval<Callable>());
522 using Traits = FunctionTraits<Function>;
523 static_assert(Traits::ARITY == 3 || Traits::ARITY == 4,
524 "LibXR::Topic::Callback::Create expects a capture-free callable "
525 "with bool and bound-argument parameters.");
526 return Callback(
527 Factory<Function, BoundArg, Traits::ARITY>::Create(+fun, std::move(arg)));
528 }
529
537 void Run(bool in_isr, MicrosecondTimestamp timestamp, void* payload_addr,
538 size_t payload_size) const
539 {
540 block_->run(block_, in_isr, timestamp, payload_addr, payload_size);
541 }
542
548 TypeID::ID PayloadTypeID() const { return block_->payload_type_id; }
549
556 bool IsRawPayloadView() const { return block_->accepts_raw_payload; }
557
558 private:
565 explicit Callback(BlockHeader* block) : block_(block ? block : &empty_block_) {}
566
567 BlockHeader* block_ = &empty_block_;
569};
570
577{
584 CallbackBlock(Callback& callback, size_t topic_payload_size)
585 : cb(callback), payload_size(topic_payload_size)
586 {
588 }
589
597 void Run(bool in_isr, MicrosecondTimestamp timestamp, void* payload_addr) const
598 {
599 cb.Run(in_isr, timestamp, payload_addr, payload_size);
600 }
601
603
605 size_t payload_size = 0;
606};
607
621{
622 if (!cb.IsRawPayloadView())
623 {
624 RequireCallbackType(block_, cb.PayloadTypeID());
625 }
626
627 auto node = new (std::align_val_t(LibXR::CONCURRENCY_ALIGNMENT))
628 LockFreeList::Node<CallbackBlock>(cb, block_->data_.payload_size);
629 block_->data_.subers.Add(*node);
630}
631} // namespace LibXR
只读原始数据视图 / Immutable raw data view
数据节点模板,继承自 BaseNode,用于存储具体数据类型。 Template data node that inherits from BaseNode to store specific data...
微秒时间戳 / Microsecond timestamp
Data data_
存储的数据 / Stored data.
Definition rbt.hpp:98
可写原始数据视图 / Mutable raw data view
每次发布时直接执行函数的订阅句柄 / Subscription handle that runs a function on each publish
Definition callback.hpp:16
void Run(bool in_isr, MicrosecondTimestamp timestamp, void *payload_addr, size_t payload_size) const
执行回调 / Run the callback
Definition callback.hpp:537
bool IsRawPayloadView() const
判断该回调是否按 raw payload 视图接收消息 / Tell whether this callback receives messages as a raw payload view
Definition callback.hpp:556
Callback & operator=(const Callback &other)=default
拷贝赋值回调句柄 / Copy-assign one callback handle
Callback(const Callback &other)=default
拷贝构造回调句柄 / Copy-construct one callback handle
static Callback Create(Callable fun, BoundArg arg)
创建一个回调订阅句柄 / Create a callback subscription handle
Definition callback.hpp:519
Callback()=default
构造一个空回调句柄 / Construct one empty callback handle
TypeID::ID PayloadTypeID() const
读取这个回调句柄期望的 payload 类型标识 / Read the payload type ID expected by this callback handle
Definition callback.hpp:548
@ CALLBACK
回调执行型订阅者 / Callback-executing subscriber.
void RegisterCallback(Callback &cb)
注册一个回调订阅者 / Register one callback subscriber
Definition callback.hpp:620
static ID GetID()
获取类型的唯一标识符 / Get a unique identifier for type T
topic 可承载 payload 的类型约束 / Type constraint for payloads carried by one topic
Definition topic.hpp:27
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
constexpr size_t CONCURRENCY_ALIGNMENT
并发结构对齐粒度(用于降低多核伪共享) / Alignment policy used by concurrency-oriented structures
Definition libxr_def.hpp:61
挂在 topic 订阅链表里的回调记录 / Callback record stored in the topic subscriber list
Definition callback.hpp:577
size_t payload_size
注册到的 topic 固定 payload 字节数 / Fixed payload size of the subscribed topic.
Definition callback.hpp:605
Callback cb
订阅的回调句柄 / Subscribed callback handle.
Definition callback.hpp:602
void Run(bool in_isr, MicrosecondTimestamp timestamp, void *payload_addr) const
通过注册时绑定的 topic payload size 执行回调 / Run the callback with the topic payload size bound at registration...
Definition callback.hpp:597
CallbackBlock(Callback &callback, size_t topic_payload_size)
构造一个回调订阅块 / Construct one callback subscriber block
Definition callback.hpp:584
带时间戳和 payload 指针的只读消息视图 / Read-only message view carrying a timestamp and a payload pointer
Definition topic.hpp:135
带时间戳和只读原始 payload 视图的消息 / Message carrying a timestamp and a read-only raw payload view
Definition topic.hpp:154
所有订阅块共用的公共头 / Common header shared by all subscriber blocks
Definition topic.hpp:237
SuberType type
订阅块的具体种类 / Concrete kind of this subscriber block.
Definition topic.hpp:238