Line data Source code
1 : #ifndef THINGER_TYPES
2 : #define THINGER_TYPES
3 :
4 : #include <concepts>
5 : #include <functional>
6 : #include <memory>
7 : #include <type_traits>
8 : #include <boost/asio.hpp>
9 : #include <boost/asio/awaitable.hpp>
10 : #include <boost/asio/use_awaitable.hpp>
11 : #include <boost/asio/as_tuple.hpp>
12 : #include <boost/asio/co_spawn.hpp>
13 : #include <boost/asio/detached.hpp>
14 : #include <boost/asio/experimental/awaitable_operators.hpp>
15 :
16 : namespace thinger {
17 :
18 : // Awaitable type alias
19 : template<typename T = void>
20 : using awaitable = boost::asio::awaitable<T>;
21 :
22 : // Import commonly used awaitable utilities
23 : using boost::asio::use_awaitable;
24 : using boost::asio::co_spawn;
25 : using boost::asio::detached;
26 :
27 : // For error handling without exceptions (returns tuple<error_code, result>)
28 : constexpr auto use_nothrow_awaitable =
29 : boost::asio::as_tuple(boost::asio::use_awaitable);
30 :
31 : // Result type for socket read/write operations (error_code + bytes transferred)
32 : using io_result = std::tuple<boost::system::error_code, size_t>;
33 :
34 : // Awaitable operators for combining coroutines (||, &&)
35 : using namespace boost::asio::experimental::awaitable_operators;
36 :
37 : // Whether T is what a coroutine returns
38 : template<typename T>
39 : inline constexpr bool is_awaitable_v = false;
40 :
41 : template<typename T, typename Executor>
42 : inline constexpr bool is_awaitable_v<boost::asio::awaitable<T, Executor>> = true;
43 :
44 : // A callable that is not a coroutine when called with Args. A std::function<void(...)>
45 : // takes any callable, coroutines too: it would call a coroutine lambda only to drop the
46 : // awaitable it returns, so the coroutine would never run
47 : template<typename F, typename... Args>
48 : concept not_a_coroutine = !is_awaitable_v<std::invoke_result_t<F&, Args...>>;
49 :
50 : // A coroutine taking Args (returning awaitable<void>)
51 : template<typename F, typename... Args>
52 : concept coroutine_callable = std::same_as<std::invoke_result_t<F&, Args...>, awaitable<void>>;
53 :
54 : // Call a callback that may be a coroutine, awaiting it if it is
55 : template<typename F, typename... Args>
56 : awaitable<void> call_and_await(F& callback, Args&&... args) {
57 : if constexpr (is_awaitable_v<std::invoke_result_t<F&, Args...>>) {
58 : co_await std::invoke(callback, std::forward<Args>(args)...);
59 : } else {
60 : std::invoke(callback, std::forward<Args>(args)...);
61 : }
62 : }
63 :
64 : // `operation`, keeping `keeper` alive until it ends
65 : template<typename T>
66 2442 : awaitable<T> kept_alive(std::shared_ptr<void> keeper, awaitable<T> operation) {
67 : co_return co_await std::move(operation);
68 4884 : }
69 :
70 : // Run `operation` on `executor` (e.g. the strand of the object it uses), resuming the
71 : // caller where it was: at once if the caller already runs there. The operation keeps
72 : // `keeper` (the object it uses) alive, also if the caller goes away
73 : template<typename T>
74 17574 : awaitable<T> on_executor(boost::asio::any_io_executor executor, std::shared_ptr<void> keeper,
75 : awaitable<T> operation) {
76 : if (executor == co_await boost::asio::this_coro::executor) co_return co_await std::move(operation);
77 : co_return co_await co_spawn(executor, kept_alive(std::move(keeper), std::move(operation)), use_awaitable);
78 35148 : }
79 :
80 : // A std::function for callbacks that are called, never awaited: it does not take a
81 : // coroutine (see not_a_coroutine), which then fails to compile instead of never running
82 : template<typename Signature>
83 : class sync_callback;
84 :
85 : template<typename R, typename... Args>
86 : class sync_callback<R(Args...)> : public std::function<R(Args...)> {
87 : public:
88 114318 : sync_callback() noexcept = default;
89 118615 : sync_callback(std::nullptr_t) noexcept {}
90 :
91 : template<typename F>
92 : requires (!std::same_as<std::remove_cvref_t<F>, sync_callback>) &&
93 : std::constructible_from<std::function<R(Args...)>, F> &&
94 : not_a_coroutine<std::remove_cvref_t<F>, Args...>
95 2619105 : sync_callback(F&& callback) : std::function<R(Args...)>(std::forward<F>(callback)) {}
96 : };
97 :
98 : }
99 :
100 : #endif
|