如何Mock Boost.Asio中返回asio::awaitable的socket.async_read方法?
解决Boost.Asio协程Mock中awaitable的resume问题
你想要Mock Boost.Asio的auto bytes = co_await socket.async_read(boost::asio::buffer(data), boost::asio::use_awaitable);调用,通过接口抽象分离生产与测试实现的思路是正确的。但asio::awaitable本身确实没有resume()方法,因为它是封装后的协程类型,你需要借助异步完成触发机制手动控制协程唤醒。下面给出两种可行的实现方案:
方案一:使用Boost.Asio Experimental Promise
Boost 1.78及以上版本提供了boost::asio::experimental::promise,能贴合Asio异步范式实现协程手动唤醒,代码更简洁:
#include <boost/asio.hpp> #include <boost/asio/experimental/promise.hpp> #include <string> #include <algorithm> class MockSocket { public: boost::asio::awaitable<size_t> async_read(boost::asio::buffer&& buffer) { buffer_ = std::move(buffer); // 等待promise完成,触发协程挂起 co_await read_promise_.async_wait(boost::asio::use_awaitable); co_return size_; } void complete_read(const std::string& data_to_put_in_buffer) { // 将测试数据写入目标buffer std::copy( data_to_put_in_buffer.begin(), data_to_put_in_buffer.end(), static_cast<char*>(buffer_.data()) ); size_ = data_to_put_in_buffer.length(); // 触发promise完成,唤醒挂起的协程 read_promise_.set_value(); // 重置promise以支持后续重复调用 read_promise_ = boost::asio::experimental::promise<void>(); } private: boost::asio::buffer buffer_; size_t size_ = 0; boost::asio::experimental::promise<void> read_promise_; };
方案二:自定义Awaiter持有协程句柄
如果你的Boost版本较低,不支持Experimental组件,可以手动实现Awaiter,直接持有std::coroutine_handle控制协程恢复:
#include <boost/asio.hpp> #include <coroutine> #include <string> #include <algorithm> class MockSocket { public: // 自定义Awaiter,负责保存协程句柄 struct ReadAwaiter { MockSocket& socket_; // 强制协程挂起 bool await_ready() const noexcept { return false; } // 挂起时保存协程句柄 void await_suspend(std::coroutine_handle<> h) noexcept { socket_.read_coroutine_ = h; } // 恢复时无需返回值 void await_resume() noexcept {} }; boost::asio::awaitable<size_t> async_read(boost::asio::buffer&& buffer) { buffer_ = std::move(buffer); // 等待自定义Awaiter co_await ReadAwaiter{*this}; co_return size_; } void complete_read(const std::string& data_to_put_in_buffer) { std::copy( data_to_put_in_buffer.begin(), data_to_put_in_buffer.end(), static_cast<char*>(buffer_.data()) ); size_ = data_to_put_in_buffer.length(); // 恢复挂起的协程,需先检查句柄非空 if (read_coroutine_) { read_coroutine_.resume(); read_coroutine_ = nullptr; } } private: boost::asio::buffer buffer_; size_t size_ = 0; std::coroutine_handle<> read_coroutine_ = nullptr; };
注意事项
- 两种方案都要确保
complete_read在协程挂起后调用,否则会触发未定义行为。 - 方案一中的Promise需每次使用后重置,避免重复触发。
- 方案二中必须检查协程句柄是否为空,防止空指针调用
resume()。
内容的提问来源于stack exchange,提问作者Lefteris Eleftheriades
相关产品推荐
相关产品推荐

