LCOV - code coverage report
Current view: top level - capy/io - any_stream.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 89.8 % 49 44 5
Test Date: 2026-02-17 18:14:47 Functions: 100.0 % 9 9

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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_IO_ANY_STREAM_HPP
      11                 : #define BOOST_CAPY_IO_ANY_STREAM_HPP
      12                 : 
      13                 : #include <boost/capy/detail/config.hpp>
      14                 : #include <boost/capy/concept/read_stream.hpp>
      15                 : #include <boost/capy/concept/write_stream.hpp>
      16                 : #include <boost/capy/io/any_read_stream.hpp>
      17                 : #include <boost/capy/io/any_write_stream.hpp>
      18                 : 
      19                 : #include <concepts>
      20                 : 
      21                 : namespace boost {
      22                 : namespace capy {
      23                 : 
      24                 : /** Type-erased wrapper for bidirectional streams.
      25                 : 
      26                 :     This class provides type erasure for any type satisfying both
      27                 :     the @ref ReadStream and @ref WriteStream concepts, enabling
      28                 :     runtime polymorphism for bidirectional I/O operations.
      29                 : 
      30                 :     Inherits from both @ref any_read_stream and @ref any_write_stream,
      31                 :     providing `read_some` and `write_some` operations. Each base
      32                 :     maintains its own cached awaitable storage, allowing concurrent
      33                 :     read and write operations.
      34                 : 
      35                 :     The wrapper supports two construction modes:
      36                 :     - **Owning**: Pass by value to transfer ownership. The wrapper
      37                 :       allocates storage and owns the stream.
      38                 :     - **Reference**: Pass a pointer to wrap without ownership. The
      39                 :       pointed-to stream must outlive this wrapper.
      40                 : 
      41                 :     @par Implicit Conversion
      42                 :     This class implicitly converts to `any_read_stream&` or
      43                 :     `any_write_stream&`, allowing it to be passed to functions
      44                 :     that accept only one capability. However, do not move through
      45                 :     a base reference as this would leave the other base in an
      46                 :     invalid state.
      47                 : 
      48                 :     @par Thread Safety
      49                 :     Not thread-safe. Concurrent operations of the same type
      50                 :     (two reads or two writes) are undefined behavior. One read
      51                 :     and one write may be in flight simultaneously.
      52                 : 
      53                 :     @par Example
      54                 :     @code
      55                 :     // Owning - takes ownership of the stream
      56                 :     any_stream stream(socket{ioc});
      57                 : 
      58                 :     // Reference - wraps without ownership
      59                 :     socket sock(ioc);
      60                 :     any_stream stream(&sock);
      61                 : 
      62                 :     // Use read_some from any_read_stream base
      63                 :     mutable_buffer rbuf(rdata, rsize);
      64                 :     auto [ec1, n1] = co_await stream.read_some(std::span(&rbuf, 1));
      65                 : 
      66                 :     // Use write_some from any_write_stream base
      67                 :     const_buffer wbuf(wdata, wsize);
      68                 :     auto [ec2, n2] = co_await stream.write_some(std::span(&wbuf, 1));
      69                 : 
      70                 :     // Pass to functions expecting one capability
      71                 :     void reader(any_read_stream&);
      72                 :     void writer(any_write_stream&);
      73                 :     reader(stream);  // Implicit upcast
      74                 :     writer(stream);  // Implicit upcast
      75                 :     @endcode
      76                 : 
      77                 :     @see any_read_stream, any_write_stream, ReadStream, WriteStream
      78                 : */
      79                 : class any_stream
      80                 :     : public any_read_stream
      81                 :     , public any_write_stream
      82                 : {
      83                 :     void* storage_ = nullptr;
      84                 :     void* stream_ptr_ = nullptr;
      85                 :     void (*destroy_)(void*) noexcept = nullptr;
      86                 : 
      87                 : public:
      88                 :     /** Destructor.
      89                 : 
      90                 :         Destroys the owned stream (if any). Base class destructors
      91                 :         handle their cached awaitable storage.
      92                 :     */
      93 HIT          26 :     ~any_stream()
      94                 :     {
      95              26 :         if(storage_)
      96                 :         {
      97               1 :             destroy_(stream_ptr_);
      98               1 :             ::operator delete(storage_);
      99                 :         }
     100              26 :     }
     101                 : 
     102                 :     /** Default constructor.
     103                 : 
     104                 :         Constructs an empty wrapper. Operations on a default-constructed
     105                 :         wrapper result in undefined behavior.
     106                 :     */
     107                 :     any_stream() = default;
     108                 : 
     109                 :     /** Non-copyable.
     110                 : 
     111                 :         The awaitable caches are per-instance and cannot be shared.
     112                 :     */
     113                 :     any_stream(any_stream const&) = delete;
     114                 :     any_stream& operator=(any_stream const&) = delete;
     115                 : 
     116                 :     /** Move constructor.
     117                 : 
     118                 :         Transfers ownership from both bases and the owned stream (if any).
     119                 : 
     120                 :         @param other The wrapper to move from.
     121                 :     */
     122               1 :     any_stream(any_stream&& other) noexcept
     123               1 :         : any_read_stream(std::move(static_cast<any_read_stream&>(other)))
     124               1 :         , any_write_stream(std::move(static_cast<any_write_stream&>(other)))
     125               1 :         , storage_(std::exchange(other.storage_, nullptr))
     126               1 :         , stream_ptr_(std::exchange(other.stream_ptr_, nullptr))
     127               2 :         , destroy_(std::exchange(other.destroy_, nullptr))
     128                 :     {
     129               1 :     }
     130                 : 
     131                 :     /** Move assignment operator.
     132                 : 
     133                 :         Destroys any owned stream and releases existing resources,
     134                 :         then transfers ownership from `other`.
     135                 : 
     136                 :         @param other The wrapper to move from.
     137                 :         @return Reference to this wrapper.
     138                 :     */
     139                 :     any_stream&
     140               1 :     operator=(any_stream&& other) noexcept
     141                 :     {
     142               1 :         if(this != &other)
     143                 :         {
     144               1 :             if(storage_)
     145                 :             {
     146 MIS           0 :                 destroy_(stream_ptr_);
     147               0 :                 ::operator delete(storage_);
     148                 :             }
     149                 :             static_cast<any_read_stream&>(*this) =
     150 HIT           1 :                 std::move(static_cast<any_read_stream&>(other));
     151                 :             static_cast<any_write_stream&>(*this) =
     152               1 :                 std::move(static_cast<any_write_stream&>(other));
     153               1 :             storage_ = std::exchange(other.storage_, nullptr);
     154               1 :             stream_ptr_ = std::exchange(other.stream_ptr_, nullptr);
     155               1 :             destroy_ = std::exchange(other.destroy_, nullptr);
     156                 :         }
     157               1 :         return *this;
     158                 :     }
     159                 : 
     160                 :     /** Construct by taking ownership of a bidirectional stream.
     161                 : 
     162                 :         Allocates storage and moves the stream into this wrapper.
     163                 :         The wrapper owns the stream and will destroy it.
     164                 : 
     165                 :         @param s The stream to take ownership of. Must satisfy both
     166                 :             ReadStream and WriteStream concepts.
     167                 :     */
     168                 :     template<class S>
     169                 :         requires ReadStream<S> && WriteStream<S> &&
     170                 :             (!std::same_as<std::decay_t<S>, any_stream>)
     171               1 :     any_stream(S s)
     172               1 :     {
     173                 :         struct guard {
     174                 :             any_stream* self;
     175                 :             void* ptr = nullptr;
     176                 :             bool committed = false;
     177               1 :             ~guard() {
     178               1 :                 if(!committed && ptr) {
     179 MIS           0 :                     static_cast<S*>(ptr)->~S();
     180               0 :                     ::operator delete(self->storage_);
     181               0 :                     self->storage_ = nullptr;
     182                 :                 }
     183 HIT           1 :             }
     184               1 :         } g{this};
     185                 : 
     186               1 :         storage_ = ::operator new(sizeof(S));
     187               1 :         S* ptr = ::new(storage_) S(std::move(s));
     188               1 :         g.ptr = ptr;
     189               1 :         stream_ptr_ = ptr;
     190               2 :         destroy_ = +[](void* p) noexcept { static_cast<S*>(p)->~S(); };
     191                 : 
     192                 :         // Initialize bases with pointer (reference semantics)
     193               1 :         static_cast<any_read_stream&>(*this) = any_read_stream(ptr);
     194               1 :         static_cast<any_write_stream&>(*this) = any_write_stream(ptr);
     195                 : 
     196               1 :         g.committed = true;
     197               1 :     }
     198                 : 
     199                 :     /** Construct by wrapping a bidirectional stream without ownership.
     200                 : 
     201                 :         Wraps the given stream by pointer. The stream must remain
     202                 :         valid for the lifetime of this wrapper.
     203                 : 
     204                 :         @param s Pointer to the stream to wrap. Must satisfy both
     205                 :             ReadStream and WriteStream concepts.
     206                 :     */
     207                 :     template<class S>
     208                 :         requires ReadStream<S> && WriteStream<S>
     209              22 :     any_stream(S* s)
     210                 :         : any_read_stream(s)
     211              22 :         , any_write_stream(s)
     212                 :     {
     213                 :         // storage_ remains nullptr - no ownership
     214              22 :     }
     215                 : 
     216                 :     /** Check if the wrapper contains a valid stream.
     217                 : 
     218                 :         Both bases must be valid for the wrapper to be valid.
     219                 : 
     220                 :         @return `true` if wrapping a stream, `false` if default-constructed
     221                 :             or moved-from.
     222                 :     */
     223                 :     bool
     224               9 :     has_value() const noexcept
     225                 :     {
     226              14 :         return any_read_stream::has_value() &&
     227              14 :                any_write_stream::has_value();
     228                 :     }
     229                 : 
     230                 :     /** Check if the wrapper contains a valid stream.
     231                 : 
     232                 :         Both bases must be valid for the wrapper to be valid.
     233                 : 
     234                 :         @return `true` if wrapping a stream, `false` if default-constructed
     235                 :             or moved-from.
     236                 :     */
     237                 :     explicit
     238               2 :     operator bool() const noexcept
     239                 :     {
     240               2 :         return has_value();
     241                 :     }
     242                 : };
     243                 : 
     244                 : } // namespace capy
     245                 : } // namespace boost
     246                 : 
     247                 : #endif
        

Generated by: LCOV version 2.3