35namespace roboctrl::io{
40using data_ptr = std::shared_ptr<const std::vector<std::byte>>;
51 const byte_span bytes{data->data(), data->size()};
52 if constexpr (std::same_as<std::invoke_result_t<Fn&, byte_span>,
awaitable<void>>) {
60using typed_callback_arg_t = utils::function_arg_t<Fn>;
65 (std::same_as<std::invoke_result_t<Fn&, typed_callback_arg_t<Fn>>,
void> ||
66 std::same_as<std::invoke_result_t<Fn&, typed_callback_arg_t<Fn>>, awaitable<void>>);
75template<
typename Arg,
typename Fn>
77 using package_type = std::remove_cvref_t<Arg>;
78 auto package = utils::from_bytes<package_type>(bytes);
80 if constexpr (std::same_as<std::invoke_result_t<Fn&, Arg>,
awaitable<void>>) {
81 co_await std::invoke(fn,
static_cast<Arg
>(package));
83 std::invoke(fn,
static_cast<Arg
>(package));
93template<utils::
byte_container T>
95 auto res = std::make_shared<std::vector<std::byte>>(t.size());
97 std::memcpy(res->data(), t.data(), t.size());
118 callback_.add([fn](
data_ptr data)
mutable ->
auto {
119 return detail::invoke_raw_callback(fn, std::move(data));
123 template<detail::typed_callback_fn Fn>
124 requires (!std::same_as<std::remove_cvref_t<utils::function_arg_t<Fn>>,
byte_span>)
125 inline void on_data(Fn&& fn)
127 using Arg = utils::function_arg_t<Fn>;
130 on_data([fn = std::forward<Fn>(fn)](
byte_span bytes)
mutable {
131 return detail::invoke_typed_callback<Arg>(fn, bytes);
165 const auto [size_it, inserted] = sizes_.try_emplace(key, size);
167 if (size_it->second != 0 && size != 0 && size_it->second != size) {
168 throw std::invalid_argument(
"conflicting payload sizes registered for IO key");
170 if (size_it->second == 0) {
171 size_it->second = size;
174 callbacks_[key].add([fn](
data_ptr data)
mutable ->
auto{
175 return detail::invoke_raw_callback(fn, std::move(data));
182 template<detail::typed_callback_fn Fn>
183 requires (!std::same_as<std::remove_cvref_t<utils::function_arg_t<Fn>>,
byte_span>)
186 using Arg = utils::function_arg_t<Fn>;
189 on_data(key,[fn = std::forward<Fn>(fn)](
byte_span bytes)
mutable {
190 return detail::invoke_typed_callback<Arg>(fn, bytes);
198 const auto callback_it = callbacks_.find(key);
199 if (callback_it == callbacks_.end()) {
202 const auto size_it = sizes_.find(key);
203 if (size_it != sizes_.end() && size_it->second != 0 && size_it->second != data.size()) {
209 inline std::optional<size_t> package_size(
const TK& key)
const {
210 const auto it = sizes_.find(key);
211 if (it == sizes_.end() || it->second == 0) {
218 std::map<TK,callback<data_ptr>> callbacks_;
219 std::map<TK,size_t> sizes_;
226concept bare_io = std::is_base_of_v<bare_io_base, T> &&
requires (T t){
227 {t.task()} -> std::same_as<awaitable<void>>;
228 {t.send(std::declval<byte_span>())} -> std::same_as<awaitable<void>>;
235concept keyed_io = std::is_base_of_v<keyed_io_base<typename T::key_type>, T> &&
requires (T t) {
236 typename T::key_type;
237 {t.task()} -> std::same_as<awaitable<void>>;
238 {t.send(std::declval<typename T::key_type>(),std::declval<byte_span>())} -> std::same_as<awaitable<void>>;
246 typename T::data_type;
247 {t.parse(bytes)} -> std::same_as<std::size_t>;
248 {t.data()} -> std::same_as<typename T::data_type>;
257 using parser_type = std::tuple_element_t<N,std::tuple<parser_types...>>;
260 using data_type =
typename parser_type<N>::data_type;
269 std::apply([&](
auto&... parser) {
274 const size_t consumed = parser.parse(data.subspan(pos));
275 if (consumed == 0 || consumed > data.size() - pos) {
283 return valid ? pos : 0;
291 return std::get<N>(parsers).data();
294 std::tuple<parser_types...> parsers;
303 using data_type = std::array<std::byte,N>;
306 if(bytes.size() < N){
309 std::copy_n(bytes.begin(), N, data_.begin());
323template<roboctrl::utils::package T>
328 if(bytes.size() <
sizeof(T)){
331 std::memcpy(
static_cast<void*
>(&data_),
static_cast<const void*
>(bytes.data()),
sizeof(T));
345template<std::byte... bytes>
347 using data_type = std::array<std::byte,
sizeof...(bytes)>;
349 constexpr static std::array<std::byte,
sizeof...(bytes)> bytes_data{bytes...};
352 if(data.size() <
sizeof...(bytes)){
356 for(
size_t i = 0; i <
sizeof...(bytes); ++i)
357 if(data[i] != bytes_data[i])
360 return sizeof...(bytes);
394template<bare_io io_type>
396 co_await io.send(data);
407template<bare_io io_type>
409 auto& io = roboctrl::get<io_type>(key);
410 co_await io.send(data);
421template<bare_io io_type,utils::package T>
422requires (!std::same_as<T, byte_span>)
424 const auto bytes = utils::to_bytes(pkg);
436template<bare_io io_type,utils::package T>
437requires (!std::same_as<T, byte_span>)
439 const auto bytes = utils::to_bytes(pkg);
440 co_await send<io_type>(key,
byte_span{bytes});
void dispatch(byte_span bytes)
分发收到的字节流。
void on_data(callback_fn< byte_span > auto fn)
注册字节级别的回调。
void dispatch(const TK &key, byte_span data)
将数据派发给对应 key 的回调。
void on_data(const TK &key, callback_fn< byte_span > auto fn, size_t size=0)
注册指定 key 的回调。
void on_data(const TK &key, Fn &&fn)
注册平凡类型包的回调。
约束回调类型可被调用且返回 void 或 awaitable<void>。
裸 IO 概念,要求具备 send/task 协程接口。
带 key 的 IO 概念,要求具备 send/task 协程接口。
std::shared_ptr< const std::vector< std::byte > > data_ptr
只读共享数据缓冲。
awaitable< void > invoke_typed_callback(Fn &fn, byte_span bytes)
解析 typed payload,并让解析结果覆盖异步回调的完整执行期。
std::span< const std::byte > byte_span
只读 byte span。
awaitable< void > send(io_type &io, byte_span data)
发送原始字节流,适配任意裸 IO。
data_ptr make_shared_from(const T &t)
将任意满足 byte_container 的数据拷贝到共享缓冲。
asio::awaitable< T > awaitable
协程任务类型。
auto data() -> data_type< N >
获取指定序号解析器的解析结果。
size_t parse(byte_span data)
依次执行所有解析器。