LCOV - code coverage report
Current view: top level - boost/capy/ex - any_dispatcher.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 100.0 % 8 8
Test Date: 2026-01-15 18:27:21 Functions: 100.0 % 8 8

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
       3              : //
       4              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6              : //
       7              : // Official repository: https://github.com/cppalliance/capy
       8              : //
       9              : 
      10              : #ifndef BOOST_CAPY_ANY_DISPATCHER_HPP
      11              : #define BOOST_CAPY_ANY_DISPATCHER_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <boost/capy/ex/any_coro.hpp>
      15              : #include <boost/capy/concept/dispatcher.hpp>
      16              : 
      17              : #include <concepts>
      18              : #include <type_traits>
      19              : 
      20              : namespace boost {
      21              : namespace capy {
      22              : 
      23              : /** A type-erased wrapper for dispatcher objects.
      24              : 
      25              :     This class provides type erasure for any type satisfying the `dispatcher`
      26              :     concept, enabling runtime polymorphism without virtual functions. It stores
      27              :     a pointer to the original dispatcher and a function pointer to invoke it,
      28              :     allowing dispatchers of different types to be stored uniformly.
      29              : 
      30              :     @par Thread Safety
      31              :     The `any_dispatcher` itself is not thread-safe for concurrent modification,
      32              :     but `operator()` is const and safe to call concurrently if the underlying
      33              :     dispatcher supports concurrent dispatch.
      34              : 
      35              :     @par Lifetime
      36              :     The `any_dispatcher` stores a pointer to the original dispatcher object.
      37              :     The caller must ensure the referenced dispatcher outlives the `any_dispatcher`
      38              :     instance. This is typically satisfied when the dispatcher is an executor
      39              :     stored in a coroutine promise or service provider.
      40              : 
      41              :     @see dispatcher
      42              : */
      43              : class any_dispatcher
      44              : {
      45              :     void const* d_ = nullptr;
      46              :     any_coro(*f_)(void const*, any_coro) = nullptr;
      47              : 
      48              : public:
      49              :     /** Default constructor.
      50              : 
      51              :         Constructs an empty `any_dispatcher`. Calling `operator()` on a
      52              :         default-constructed instance results in undefined behavior.
      53              :     */
      54          439 :     any_dispatcher() = default;
      55              : 
      56              :     /** Copy constructor.
      57              : 
      58              :         Copies the internal pointer and function, preserving identity.
      59              :         This enables the same-dispatcher optimization when passing
      60              :         any_dispatcher through coroutine chains.
      61              :     */
      62              :     any_dispatcher(any_dispatcher const&) = default;
      63              : 
      64              :     /** Copy assignment operator. */
      65              :     any_dispatcher& operator=(any_dispatcher const&) = default;
      66              : 
      67              :     /** Constructs from any dispatcher type.
      68              : 
      69              :         Captures a reference to the given dispatcher and stores a type-erased
      70              :         invocation function. The dispatcher must remain valid for the lifetime
      71              :         of this `any_dispatcher` instance.
      72              : 
      73              :         @param d The dispatcher to wrap. Must satisfy the `dispatcher` concept.
      74              :                  A pointer to this object is stored internally; the dispatcher
      75              :                  must outlive this wrapper.
      76              :     */
      77              :     template<dispatcher D>
      78              :         requires (!std::same_as<std::decay_t<D>, any_dispatcher>)
      79           88 :     any_dispatcher(D const& d)
      80           88 :         : d_(&d)
      81          196 :         , f_([](void const* pd, any_coro h) {
      82          108 :                 return static_cast<D const*>(pd)->operator()(h);
      83              :             })
      84              :     {
      85           88 :     }
      86              : 
      87              :     /** Returns true if this instance holds a valid dispatcher.
      88              : 
      89              :         @return `true` if constructed with a dispatcher, `false` if
      90              :                 default-constructed.
      91              :     */
      92              :     explicit operator bool() const noexcept
      93              :     {
      94              :         return d_ != nullptr;
      95              :     }
      96              : 
      97              :     /** Compares two dispatchers for identity.
      98              : 
      99              :         Two `any_dispatcher` instances are equal if they wrap the same
     100              :         underlying dispatcher object (pointer equality). This enables
     101              :         the affinity optimization: when `caller_dispatcher == my_dispatcher`,
     102              :         symmetric transfer can proceed without a `running_in_this_thread()`
     103              :         check.
     104              : 
     105              :         @param other The dispatcher to compare against.
     106              : 
     107              :         @return `true` if both wrap the same dispatcher object.
     108              :     */
     109              :     bool operator==(any_dispatcher const& other) const noexcept
     110              :     {
     111              :         return d_ == other.d_;
     112              :     }
     113              : 
     114              :     /** Dispatches a coroutine handle through the wrapped dispatcher.
     115              : 
     116              :         Invokes the stored dispatcher with the given coroutine handle,
     117              :         returning a handle suitable for symmetric transfer.
     118              : 
     119              :         @param h The coroutine handle to dispatch for resumption.
     120              : 
     121              :         @return A coroutine handle that the caller may use for symmetric
     122              :                 transfer, or `std::noop_coroutine()` if the dispatcher
     123              :                 posted the work for later execution.
     124              : 
     125              :         @pre This instance was constructed with a valid dispatcher
     126              :              (not default-constructed).
     127              :     */
     128          108 :     any_coro operator()(any_coro h) const
     129              :     {
     130          108 :         return f_(d_, h);
     131              :     }
     132              : };
     133              : 
     134              : } // capy
     135              : } // boost
     136              : 
     137              : #endif
        

Generated by: LCOV version 2.3