libcopp  2.2.0
callable_promise.h
Go to the documentation of this file.
1 // Copyright 2023 owent
2 
3 #pragma once
4 
5 #include <libcopp/utils/config/libcopp_build_features.h>
6 
7 // clang-format off
8 #include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
9 // clang-format on
10 #include <cstdlib>
11 #include <type_traits>
12 // clang-format off
13 #include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
14 // clang-format on
15 
16 #if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
17 # include <exception>
18 #endif
19 
22 #include "libcopp/future/future.h"
24 
25 #if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
26 
27 LIBCOPP_COPP_NAMESPACE_BEGIN
28 
29 template <class TFUTURE>
30 class LIBCOPP_COPP_API_HEAD_ONLY some_delegate;
31 
32 template <class TVALUE, class TERROR_TRANSFORM>
33 class LIBCOPP_COPP_API_HEAD_ONLY callable_future;
34 
35 template <class TVALUE, bool RETURN_VOID>
36 class LIBCOPP_COPP_API_HEAD_ONLY callable_promise_base;
37 
38 template <class TPROMISE, class TERROR_TRANSFORM, bool RETURN_VOID>
39 class LIBCOPP_COPP_API_HEAD_ONLY callable_awaitable;
40 
41 template <class TVALUE>
42 class LIBCOPP_COPP_API_HEAD_ONLY callable_promise_base<TVALUE, true> : public promise_base_type {
43  public:
44  using value_type = TVALUE;
45 
46  template <class... TARGS>
47  callable_promise_base(TARGS&&...) {}
48 
49  callable_promise_base() = default;
50 
51  void return_void() noexcept {
52  set_flag(promise_flag::kHasReturned, true);
53  if (get_status() < promise_status::kDone) {
54  set_status(promise_status::kDone);
55  }
56  }
57 };
58 
59 template <class TVALUE, bool IS_DEFAULT_CONSTRUCTIBLE>
60 class LIBCOPP_COPP_API_HEAD_ONLY callable_promise_value_constructor;
61 
62 template <class TVALUE>
63 class LIBCOPP_COPP_API_HEAD_ONLY callable_promise_value_constructor<TVALUE, true> {
64  public:
65  template <class... TARGS>
66  inline static TVALUE construct(TARGS&&...) {
67  return TVALUE{};
68  }
69 };
70 
71 template <class TVALUE>
72 class LIBCOPP_COPP_API_HEAD_ONLY callable_promise_value_constructor<TVALUE, false> {
73  public:
74  template <class... TARGS>
75  inline static TVALUE construct(TARGS&&... args) {
76  return TVALUE{std::forward<TARGS>(args)...};
77  }
78 };
79 
80 template <class TVALUE>
81 class LIBCOPP_COPP_API_HEAD_ONLY callable_promise_base<TVALUE, false> : public promise_base_type {
82  public:
83  using value_type = TVALUE;
84 
85  template <class... TARGS>
86  callable_promise_base(TARGS&&... args)
87  : data_(callable_promise_value_constructor<value_type, !std::is_constructible<value_type, TARGS...>::value>::
88  construct(std::forward<TARGS>(args)...)) {}
89 
90  void return_value(value_type value) {
91  set_flag(promise_flag::kHasReturned, true);
92  if (get_status() < promise_status::kDone) {
93  set_status(promise_status::kDone);
94  }
95  data_ = std::move(value);
96  }
97 
98  UTIL_FORCEINLINE value_type& data() noexcept { return data_; }
99  UTIL_FORCEINLINE const value_type& data() const noexcept { return data_; }
100 
101  protected:
102  value_type data_;
103 };
104 
105 # if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
106 template <DerivedPromiseBaseType TPROMISE>
107 # else
108 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
109 # endif
110 class LIBCOPP_COPP_API_HEAD_ONLY callable_awaitable_base : public awaitable_base_type {
111  public:
112  using promise_type = TPROMISE;
113  using value_type = typename promise_type::value_type;
114  using handle_type = LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<promise_type>;
115 
116  public:
117  callable_awaitable_base(handle_type handle) : callee_{handle} {}
118 
119  UTIL_FORCEINLINE bool await_ready() noexcept {
120  if (!callee_) {
121  return true;
122  }
123 
124  // callee_ should not be destroyed, this only works with callable_future<T>::promise_type
125  if (callee_.done()) {
126  return true;
127  }
128 
129  if (callee_.promise().get_status() >= promise_status::kDone) {
130  return true;
131  }
132 
133  if (callee_.promise().check_flag(promise_flag::kHasReturned)) {
134  return true;
135  }
136 
137  return callee_.done();
138  }
139 
140 # if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
141  template <DerivedPromiseBaseType TCPROMISE>
142 # else
143  template <class TCPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TCPROMISE>::value>>
144 # endif
145  inline bool await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TCPROMISE> caller) noexcept {
146  if (caller.promise().get_status() < promise_status::kDone) {
147  set_caller(caller);
148  caller.promise().set_waiting_handle(callee_);
149  callee_.promise().add_caller(caller);
150 
151  caller.promise().set_flag(promise_flag::kInternalWaitting, true);
152  return true;
153  } else {
154  // Already done and can not suspend again
155  auto& callee_promise = get_callee().promise();
156  // If callee is killed when running, we need inherit status from caller
157  if (callee_promise.get_status() < promise_status::kDone &&
158  callee_promise.check_flag(promise_flag::kInternalWaitting)) {
159  callee_promise.set_status(caller.promise().get_status());
160  callee_.resume();
161  }
162  // caller.resume();
163  return false;
164  }
165  }
166 
167  UTIL_FORCEINLINE handle_type& get_callee() noexcept { return callee_; }
168  UTIL_FORCEINLINE const handle_type& get_callee() const noexcept { return callee_; }
169 
170  protected:
171  void detach() noexcept {
172  // caller maybe null if the callable is already ready when co_await
173  auto caller = get_caller();
174  auto& callee_promise = get_callee().promise();
175 
176  if (caller) {
177  if (nullptr != caller.promise) {
178  caller.promise->set_flag(promise_flag::kInternalWaitting, false);
179  caller.promise->set_waiting_handle(nullptr);
180  }
181  callee_promise.remove_caller(caller, true);
182  set_caller(nullptr);
183  }
184 
185  if (callee_promise.get_status() < promise_status::kDone) {
186  if (!caller) {
187  callee_promise.set_status(promise_status::kKilled);
188  } else {
189  callee_promise.set_status(caller.promise->get_status());
190  }
191  }
192  }
193 
194  private:
195  handle_type callee_;
196 };
197 
198 template <class TPROMISE, class TERROR_TRANSFORM>
199 class LIBCOPP_COPP_API_HEAD_ONLY callable_awaitable<TPROMISE, TERROR_TRANSFORM, true>
200  : public callable_awaitable_base<TPROMISE> {
201  public:
202  using base_type = callable_awaitable_base<TPROMISE>;
203  using promise_type = typename base_type::promise_type;
204  using value_type = typename base_type::value_type;
205  using handle_type = typename base_type::handle_type;
206 
207  public:
208  using base_type::await_ready;
209  using base_type::await_suspend;
210  using base_type::detach;
211  using base_type::get_callee;
212  using base_type::get_caller;
213  using base_type::set_caller;
214  callable_awaitable(handle_type handle) : base_type(handle) {}
215 
216  UTIL_FORCEINLINE void await_resume() {
217  detach();
218  get_callee().promise().resume_waiting(get_callee(), true);
219  }
220 };
221 
222 template <class TPROMISE, class TERROR_TRANSFORM>
223 class LIBCOPP_COPP_API_HEAD_ONLY callable_awaitable<TPROMISE, TERROR_TRANSFORM, false>
224  : public callable_awaitable_base<TPROMISE> {
225  public:
226  using base_type = callable_awaitable_base<TPROMISE>;
227  using promise_type = typename base_type::promise_type;
228  using value_type = typename base_type::value_type;
229  using handle_type = typename base_type::handle_type;
230 
231  public:
232  using base_type::await_ready;
233  using base_type::await_suspend;
234  using base_type::detach;
235  using base_type::get_callee;
236  using base_type::get_caller;
237  using base_type::set_caller;
238  callable_awaitable(handle_type handle) : base_type(handle) {}
239 
240  inline value_type await_resume() {
241  detach();
242  auto& callee_promise = get_callee().promise();
243  callee_promise.resume_waiting(get_callee(), true);
244 
245  if (!callee_promise.check_flag(promise_flag::kHasReturned)) {
246  return TERROR_TRANSFORM()(callee_promise.get_status());
247  }
248 
249  return std::move(callee_promise.data());
250  }
251 };
252 
253 template <class TVALUE, class TERROR_TRANSFORM = promise_error_transform<TVALUE>>
254 class LIBCOPP_COPP_API_HEAD_ONLY callable_future {
255  public:
256  using value_type = TVALUE;
257  using error_transform = TERROR_TRANSFORM;
258  using self_type = callable_future<value_type, error_transform>;
259  class promise_type
260  : public callable_promise_base<value_type, std::is_void<typename std::decay<value_type>::type>::value> {
261  public:
262 # if defined(__GNUC__) && !defined(__clang__)
263  template <class... TARGS>
264  promise_type(TARGS&&... args)
265  : callable_promise_base<value_type, std::is_void<typename std::decay<value_type>::type>::value>(args...) {}
266 # else
267  template <class... TARGS>
268  promise_type(TARGS&&... args)
269  : callable_promise_base<value_type, std::is_void<typename std::decay<value_type>::type>::value>(
270  std::forward<TARGS>(args)...) {}
271 # endif
272 
273  auto get_return_object() noexcept {
274  return self_type{LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<promise_type>::from_promise(*this)};
275  }
276 
277  struct initial_awaitable {
278  inline bool await_ready() const noexcept { return false; }
279 
280  inline void await_resume() const noexcept {
281  if (handle.promise().get_status() == promise_status::kCreated) {
282  promise_status excepted = promise_status::kCreated;
283  handle.promise().set_status(promise_status::kRunning, &excepted);
284  }
285  }
286 
287  inline bool await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<promise_type> caller) noexcept {
288  handle = caller;
289 
290  // Return false to resume the caller
291  return false;
292  }
293 
294  LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<promise_type> handle;
295  };
296  initial_awaitable initial_suspend() noexcept { return {}; }
297 # if defined(LIBCOPP_MACRO_ENABLE_EXCEPTION) && LIBCOPP_MACRO_ENABLE_EXCEPTION
298  void unhandled_exception() { throw; }
299 # elif defined(LIBCOPP_MACRO_HAS_EXCEPTION) && LIBCOPP_MACRO_HAS_EXCEPTION
300  void unhandled_exception() { throw; }
301 # else
302  void unhandled_exception() { std::abort(); }
303 # endif
304  };
305  using handle_type = LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<promise_type>;
306  using awaitable_type =
307  callable_awaitable<promise_type, error_transform, std::is_void<typename std::decay<value_type>::type>::value>;
308 
309  public:
310  callable_future(handle_type handle) noexcept : current_handle_{handle} {}
311 
312  callable_future(const callable_future&) = delete;
313  callable_future(callable_future&& other) noexcept {
314  current_handle_ = other.current_handle_;
315  other.current_handle_ = nullptr;
316  };
317 
318  callable_future& operator=(const callable_future&) = delete;
319  callable_future& operator=(callable_future&& other) noexcept {
320  current_handle_ = other.current_handle_;
321  other.current_handle_ = nullptr;
322  return *this;
323  }
324 
325  ~callable_future() {
326  // Move current_handle_ to stack here to allow recursive call of force_destroy
327  handle_type current_handle = current_handle_;
328  current_handle_ = nullptr;
329 
330  while (current_handle && !current_handle.done() &&
331  !current_handle.promise().check_flag(promise_flag::kHasReturned)) {
332  if (current_handle.promise().get_status() < promise_status::kDone) {
333  current_handle.promise().set_status(promise_status::kKilled);
334  }
335  current_handle.resume();
336  }
337 
338  if (current_handle) {
339  current_handle.promise().set_flag(promise_flag::kDestroying, true);
340  current_handle.destroy();
341  }
342  }
343 
344  awaitable_type operator co_await() { return awaitable_type{current_handle_}; }
345 
346  inline bool is_ready() const noexcept {
347  if (!current_handle_) {
348  return true;
349  }
350 
351  return current_handle_.done() || current_handle_.promise().check_flag(promise_flag::kHasReturned);
352  }
353 
354  UTIL_FORCEINLINE promise_status get_status() const noexcept { return current_handle_.promise().get_status(); }
355 
356  static auto yield_status() noexcept { return promise_base_type::pick_current_status(); }
357 
366  bool kill(promise_status target_status = promise_status::kKilled, bool force_resume = false) noexcept {
367  if (target_status < promise_status::kDone) {
368  return false;
369  }
370 
371  bool ret = true;
372  while (true) {
373  if (!current_handle_) {
374  ret = false;
375  break;
376  }
377 
378  if (current_handle_.done()) {
379  ret = false;
380  break;
381  }
382 
383  promise_status current_status = get_status();
384  if (current_status >= promise_status::kDone) {
385  ret = false;
386  break;
387  }
388 
389  if (!current_handle_.promise().set_status(target_status, &current_status)) {
390  continue;
391  }
392 
393  if ((force_resume || current_handle_.promise().is_waiting()) &&
394  !current_handle_.promise().check_flag(promise_flag::kDestroying) &&
395  !current_handle_.promise().check_flag(promise_flag::kHasReturned)) {
396  // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
397  // We may enable exception in the future
398 # if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
399  std::exception_ptr unhandled_exception;
400  try {
401 # endif
402  current_handle_.resume();
403 
404  // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
405  // We may enable exception in the future
406 # if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
407  } catch (...) {
408  unhandled_exception = std::current_exception();
409  }
410  if (unhandled_exception) {
411  std::rethrow_exception(unhandled_exception);
412  }
413 # endif
414  }
415  break;
416  }
417 
418  return ret;
419  }
420 
427  UTIL_FORCEINLINE const handle_type& get_internal_handle() const noexcept { return current_handle_; }
428 
435  UTIL_FORCEINLINE handle_type& get_internal_handle() noexcept { return current_handle_; }
436 
443  UTIL_FORCEINLINE const promise_type& get_internal_promise() const noexcept { return current_handle_.promise(); }
444 
451  UTIL_FORCEINLINE promise_type& get_internal_promise() noexcept { return current_handle_.promise(); }
452 
453  private:
454  handle_type current_handle_;
455 };
456 
457 // some delegate
458 template <class TFUTURE>
459 struct LIBCOPP_COPP_API_HEAD_ONLY some_delegate_context {
460  using future_type = TFUTURE;
461  using ready_output_type = typename some_ready<future_type>::type;
462 
463  std::list<future_type*> pending;
464  ready_output_type ready;
465  size_t ready_bound = 0;
466  size_t scan_bound = 0;
467  promise_status status = promise_status::kCreated;
468  promise_caller_manager::handle_delegate caller_handle = promise_caller_manager::handle_delegate(nullptr);
469 };
470 
471 template <class TFUTURE, class TDELEGATE_ACTION>
472 class LIBCOPP_COPP_API_HEAD_ONLY some_delegate_base {
473  public:
474  using future_type = TFUTURE;
475  using value_type = typename future_type::value_type;
476  using context_type = some_delegate_context<future_type>;
477  using ready_output_type = typename context_type::ready_output_type;
478  using delegate_action_type = TDELEGATE_ACTION;
479 
480  private:
481  static void force_resume_all(context_type& context) {
482  for (auto& pending_future : context.pending) {
483  delegate_action_type::resume_future(context.caller_handle, *pending_future);
484  }
485 
486  if (context.status < promise_status::kDone && nullptr != context.caller_handle.promise) {
487  context.status = context.caller_handle.promise->get_status();
488  }
489 
490  context.caller_handle = nullptr;
491  if (context.status < promise_status::kDone) {
492  context.status = promise_status::kKilled;
493  }
494  }
495 
496  static void scan_ready(context_type& context) {
497  auto iter = context.pending.begin();
498 
499  while (iter != context.pending.end()) {
500  if (delegate_action_type::is_pending(**iter)) {
501  ++iter;
502  continue;
503  }
504  future_type& future = **iter;
505  context.ready.push_back(gsl::make_not_null(&future));
506  iter = context.pending.erase(iter);
507 
508  delegate_action_type::resume_future(context.caller_handle, future);
509  }
510  }
511 
512  public:
513  class awaitable_type : public awaitable_base_type {
514  public:
515  awaitable_type(context_type* context) : context_(context) {}
516 
517  inline bool await_ready() noexcept {
518  if (nullptr == context_) {
519  return true;
520  }
521 
522  if (context_->status >= promise_status::kDone) {
523  return true;
524  }
525 
526  return context_->pending.empty();
527  }
528 
529 # if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
530  template <DerivedPromiseBaseType TCPROMISE>
531 # else
532  template <class TCPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TCPROMISE>::value>>
533 # endif
534  inline bool await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TCPROMISE> caller) noexcept {
535  if (nullptr == context_ || caller.promise().get_status() >= promise_status::kDone) {
536  // Already done and can not suspend again
537  // caller.resume();
538  return false;
539  }
540 
541  set_caller(caller);
542 
543  // Allow kill resume to forward error information
544  caller.promise().set_flag(promise_flag::kInternalWaitting, true);
545 
546  // set caller for all futures
547  if (!context_->caller_handle) {
548  context_->caller_handle = caller;
549  // Copy pending here, the callback may call resume and will change the pending list
550  std::list<future_type*> copy_pending = context_->pending;
551  for (auto& pending_future : copy_pending) {
552  delegate_action_type::suspend_future(context_->caller_handle, *pending_future);
553  }
554  }
555 
556  return true;
557  }
558 
559  void await_resume() {
560  // caller maybe null if the callable is already ready when co_await
561  auto caller = get_caller();
562  if (caller) {
563  if (nullptr != caller.promise) {
564  caller.promise->set_flag(promise_flag::kInternalWaitting, false);
565  }
566  set_caller(nullptr);
567  }
568 
569  if (nullptr == context_) {
570  return;
571  }
572 
573  ++context_->scan_bound;
574  if (context_->scan_bound >= context_->ready_bound) {
575  scan_ready(*context_);
576  context_->scan_bound = context_->ready.size();
577 
578  if (context_->scan_bound >= context_->ready_bound && context_->status < promise_status::kDone) {
579  context_->status = promise_status::kDone;
580  }
581  }
582  }
583 
584  private:
585  context_type* context_;
586  };
587 
588  public:
589  struct promise_type {
590  context_type* context_;
591 
592  promise_type(context_type* context) : context_(context) {}
593  promise_type(const promise_type&) = delete;
594  promise_type(promise_type&&) = delete;
595  promise_type& operator=(const promise_type&) = delete;
596  promise_type& operator=(promise_type&&) = delete;
597  ~promise_type() {
598  COPP_LIKELY_IF (nullptr != context_ && !!context_->caller_handle) {
599  force_resume_all(*context_);
600  }
601  }
602 
603  inline awaitable_type operator co_await() & { return awaitable_type{context_}; }
604  };
605 
606  template <class TCONTAINER>
607  static callable_future<promise_status> run(ready_output_type& ready_futures, size_t ready_count,
608  TCONTAINER* futures) {
609  using container_type = typename std::decay<typename std::remove_pointer<TCONTAINER>::type>::type;
610  context_type context;
611  context.ready.reserve(gsl::size(*futures));
612 
613  for (auto& future_object : *futures) {
614  auto& future_ref =
615  pick_some_reference<typename std::remove_reference<decltype(future_object)>::type>::unwrap(future_object);
616  if (delegate_action_type::is_pending(future_ref)) {
617  context.pending.push_back(&future_ref);
618  } else {
619  context.ready.push_back(gsl::make_not_null(&future_ref));
620  }
621  }
622 
623  if (context.ready.size() >= ready_count) {
624  context.ready.swap(ready_futures);
625  co_return promise_status::kDone;
626  }
627 
628  if (ready_count >= context.pending.size() + ready_futures.size()) {
629  ready_count = context.pending.size() + ready_futures.size();
630  }
631  context.ready_bound = ready_count;
632  context.scan_bound = context.ready.size();
633  context.status = promise_status::kRunning;
634 
635  {
636  promise_type some_promise{&context};
637  while (context.status < promise_status::kDone) {
638  // Killed by caller
639  auto current_status = co_yield callable_future<promise_status>::yield_status();
640  if (current_status >= promise_status::kDone) {
641  context.status = current_status;
642  break;
643  }
644 
645  co_await some_promise;
646  }
647 
648  // destroy promise object and detach handles
649  }
650 
651  context.ready.swap(ready_futures);
652  co_return context.status;
653  }
654 
655  private:
656  std::shared_ptr<context_type> context_;
657 };
658 
659 // some
660 template <class TVALUE, class TERROR_TRANSFORM>
661 struct LIBCOPP_COPP_API_HEAD_ONLY some_delegate_callable_action {
662  using future_type = callable_future<TVALUE, TERROR_TRANSFORM>;
663  using context_type = some_delegate_context<future_type>;
664 
665  inline static void suspend_future(const promise_caller_manager::handle_delegate& caller, future_type& callee) {
666  callee.get_internal_promise().add_caller(caller);
667  }
668 
669  inline static void resume_future(const promise_caller_manager::handle_delegate& caller, future_type& callee) {
670  callee.get_internal_promise().remove_caller(caller, false);
671  // Do not force resume callee here, we allow to await the unready callable later.
672  }
673 
674  inline static bool is_pending(future_type& future_object) noexcept {
675  auto future_status = future_object.get_status();
676  return future_status >= promise_status::kCreated && future_status < promise_status::kDone;
677  }
678 };
679 
680 template <class TVALUE, class TERROR_TRANSFORM>
681 class LIBCOPP_COPP_API_HEAD_ONLY some_delegate<callable_future<TVALUE, TERROR_TRANSFORM>>
682  : public some_delegate_base<callable_future<TVALUE, TERROR_TRANSFORM>,
683  some_delegate_callable_action<TVALUE, TERROR_TRANSFORM>> {
684  public:
685  using base_type = some_delegate_base<callable_future<TVALUE, TERROR_TRANSFORM>,
686  some_delegate_callable_action<TVALUE, TERROR_TRANSFORM>>;
687  using future_type = typename base_type::future_type;
688  using value_type = typename base_type::value_type;
689  using ready_output_type = typename base_type::ready_output_type;
690  using context_type = typename base_type::context_type;
691 
692  using base_type::run;
693 };
694 
695 LIBCOPP_COPP_NAMESPACE_END
696 
697 #endif
#define UTIL_FORCEINLINE
#define LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE
Definition: coroutine.h:41
#define COPP_LIKELY_IF(...)
Definition: features.h:102
Definition: future.h:18
auto make_not_null(T &&t) noexcept
Definition: not_null.h:111
constexpr auto size(TCONTAINER &&container) -> decltype(container.size())
Definition: span.h:44
constexpr auto data(TCONTAINER &&container) -> decltype(container.data())
Definition: span.h:54
std::shared_ptr< cli::cmd_option_value > value_type
Definition: cmd_option.h:50