Skip to content

Conversation

@vinniefalco
Copy link
Member

@vinniefalco vinniefalco commented Feb 1, 2026

Summary by CodeRabbit

  • New Features

    • Added not_found portable error condition.
    • Expanded public API surface to expose additional type‑erased I/O and execution facilities.
    • Tasks become movable (move constructor/assignment).
  • Behavior / API

    • Read/write/read_until operations now document and expose async, cancellation‑aware semantics and dynamic‑buffer overloads with initial sizing.
  • Documentation

    • Major documentation enhancements: richer guides, examples, and new checklist templates for documenting concepts, errors, and async/awaitable I/O.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Feb 1, 2026

📝 Walkthrough

Walkthrough

This PR adds documentation standards files, enriches and expands Doxygen/javadoc-style docs and examples across concept and I/O headers, introduces new buffer tag types and a new cond::not_found case, reorders/includes and exposes additional public headers, updates several coroutine/awaitable concept contracts (notably IoAwaitableTask / IoLaunchableTask), and adds cancellation-aware async read/write APIs and overloads.

Changes

Cohort / File(s) Summary
Documentation Standards
.cursor/rules/doc-awaitable.mdc, .cursor/rules/doc-concept.mdc, .cursor/rules/doc-errors.mdc, .cursor/rules/doc-general.mdc
Added four new Markdown rules files defining javadoc-style templates and checklists for awaitables, concepts, error enums, and general docs.
Master Header & Public Includes
include/boost/capy.hpp
Reorganized and expanded public includes to expose more components (buffers, concepts, execution, io any_* wrappers, ex/*), changing the visible public header surface.
Buffer Types & Utilities
include/boost/capy/buffers.hpp, include/boost/capy/buffers/buffer_copy.hpp
Introduced public tag types (size_tag, slice_tag) and clarified buffer docs; renamed template/parameter identifiers in buffer_copy (e.g., MutableBufferSequenceMB) and adjusted docs.
Concepts (docs & contracts)
include/boost/capy/concept/execution_context.hpp, executor.hpp, io_awaitable.hpp, io_awaitable_task.hpp, io_launchable_task.hpp, match_condition.hpp
Expanded documentation with formal syntactic/semantic requirements, conforming signatures, and examples. Strengthened IoAwaitableTask and IoLaunchableTask contracts to require richer promise interfaces and explicit launch semantics.
Cond / Error enums
include/boost/capy/cond.hpp, include/boost/capy/error.hpp
Added cond::not_found; updated error docs to discourage direct enum-value comparisons and mapped members to cond equivalents; added per-enumerator docs.
I/O type-erasure includes / small header edits
include/boost/capy/io/any_*.(hpp), include/boost/capy/ex/*
Moved <system_error> include locations for several any_* headers; added example blocks to executor-related docs and small doc tweaks.
Async read/write APIs & read_until
include/boost/capy/read.hpp, include/boost/capy/read_until.hpp, include/boost/capy/write.hpp
Converted docs and examples to cancellation-aware async semantics; added new read and read_until overloads accepting DynamicBufferParam with initial_amount (default 2048); updated match_delim/read_until behavior and examples to use dynamic buffers and stop_token handling.
Task & Concurrency docs / semantics
include/boost/capy/task.hpp, include/boost/capy/when_all.hpp
Reworked task docs to emphasize lazy IoLaunchableTask behavior; added move ctor/assignment and lifecycle documentation; clarified when_all concurrency semantics, result ordering, void-filtering, and exception handling.
Small include/order and doc wording changes
various headers (many + small edits)
Primarily documentation, example additions, include-order adjustments, and formatting rewording across multiple headers.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Caller
    participant Awaitable as IoAwaitableTask
    participant Promise as PromiseType
    participant Executor as Executor
    participant Context as ExecutionContext

    Caller->>Awaitable: co_await / await_suspend(ex, token)
    Awaitable->>Promise: promise().set_executor(executor_ref) (noexcept)
    Awaitable->>Promise: promise().set_stop_token(stop_token) (noexcept)
    Awaitable->>Promise: promise().set_continuation(coro, executor_ref)
    Awaitable->>Executor: schedule/post coroutine via executor (may call on_work_started)
    Executor->>Context: uses context-owned resources to run work
    Note over Promise,Caller: Promise stores executor/stop_token and controls completion/continuation
    Executor->>Promise: resume continuation when work completes
    Promise->>Caller: completion via await_resume / complete()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A quick hop through headers bright,

Docs bloom under moonlit byte.
Awaitables set their executor beds,
Buffers sliced and errors said.
I nibble notes in tidy rows — hooray for clearer code that grows!

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Revise javadoc for all public interfaces' accurately and concisely describes the primary change: comprehensive documentation updates across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@cppalliance-bot
Copy link

cppalliance-bot commented Feb 1, 2026

An automated preview of the documentation is available at https://121.capy.prtest3.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-02-01 22:03:16 UTC

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
include/boost/capy/ex/executor_ref.hpp (1)

13-19: ⚠️ Potential issue | 🟡 Minor

Add a file-level implementation overview comment.

This header contains non-trivial type-erasure logic, but there’s no /* */ overview block after the includes to summarize the approach and any tricky aspects.

Suggested overview block
 `#include` <concepts>
 `#include` <coroutine>
 `#include` <type_traits>
 `#include` <utility>
+
+/*
+Overview: Provides a type-erased executor_ref wrapper backed by a static
+vtable to enable Executor-concept polymorphism without allocation.
+*/

As per coding guidelines: Files containing non-trivial implementation logic should include a /* */ block comment after the includes that provides a high-level overview of how the implementation works.

include/boost/capy/read.hpp (2)

13-25: ⚠️ Potential issue | 🟡 Minor

Add a file-level implementation overview comment.

This header contains non-trivial coroutine read loops but lacks the required /* */ overview block after the includes.

Suggested overview block
 `#include` <system_error>

 `#include` <cstddef>
+
+/*
+Overview: Implements coroutine-based read helpers that repeatedly call
+read_some to fill buffer sequences or grow dynamic buffers, aggregating
+bytes read while handling EOF and cancellation.
+*/

As per coding guidelines: Files containing non-trivial implementation logic should include a /* */ block comment after the includes that provides a high-level overview of how the implementation works.


159-199: ⚠️ Potential issue | 🟡 Minor

Use the canonical io_result return tuple format and define T1.

Same return-doc fixes as above apply here.

Suggested doc tweak
-    `@return` An awaitable yielding `(error_code, std::size_t)`.
+    `@return` An awaitable yielding `(error_code,std::size_t)`.
         On success (EOF), `ec` is clear and `n` is total bytes read.
         On error, `n` is bytes read before the error. Compare error
         codes to conditions:
         `@li` `cond::canceled` - Operation was cancelled
+
+    T1 is `std::size_t` bytes transferred.

As per coding guidelines: use the canonical io_result return format (no internal spaces) and explain the meaning of io_result type parameters at the end of the javadoc description.

include/boost/capy/buffers.hpp (2)

91-188: ⚠️ Potential issue | 🟡 Minor

Add parameter and precondition details for buffer constructors.
Pointer/Asio constructors are public and should spell out parameter meaning and lifetime/validity requirements (e.g., storage must remain valid for size bytes; data may be null only when size == 0).

✍️ Suggested doc additions (apply similarly to other constructors)
-    /// Construct from pointer and size.
+    /// Construct from pointer and size.
+    /// `@param` data Pointer to writable storage; must reference at least `size` bytes.
+    /// `@param` size Number of bytes in the region. `data` may be null only when `size == 0`.
     constexpr mutable_buffer(
         void* data, std::size_t size) noexcept
@@
-    /// Construct from Asio mutable_buffer.
+    /// Construct from Asio mutable_buffer.
+    /// `@param` b Source buffer; referenced storage must remain valid for the buffer lifetime.
     template<class MutableBuffer>
         requires std::same_as<MutableBuffer, asio::mutable_buffer>
     constexpr mutable_buffer(
         MutableBuffer const& b) noexcept
@@
-    /// Construct from pointer and size.
+    /// Construct from pointer and size.
+    /// `@param` data Pointer to readable storage; must reference at least `size` bytes.
+    /// `@param` size Number of bytes in the region. `data` may be null only when `size == 0`.
     constexpr const_buffer(
         void const* data, std::size_t size) noexcept
@@
-    /// Construct from Asio buffer types.
+    /// Construct from Asio buffer types.
+    /// `@param` b Source buffer; referenced storage must remain valid for the buffer lifetime.
     template<class ConstBuffer>
         requires (std::same_as<ConstBuffer, asio::const_buffer> ||
                   std::same_as<ConstBuffer, asio::mutable_buffer>)
     constexpr const_buffer(
         ConstBuffer const& b) noexcept

As per coding guidelines: Docstrings are required for all classes and functions in public headers in non-detail namespaces. Docstrings should include a brief description of purpose, document all parameters, return values, and any preconditions, postconditions, or exceptions.

Also applies to: 192-297


340-394: ⚠️ Potential issue | 🟡 Minor

Add @param/@return details for public buffer-sequence callables.
The doc blocks describe behavior but don’t explicitly document parameters and return values for begin, end, buffer_size, buffer_empty, and buffer_length.

✍️ Example doc addition (apply similarly to the other callables)
 /** Return the total byte count across all buffers in a sequence.
 
     Sums the `size()` of each buffer in the sequence. This differs
     from `buffer_length` which counts the number of buffer elements.
+
+    `@param` bs Buffer sequence to measure.
+    `@return` Sum of `size()` for all buffers in the sequence.
 
     `@par` Example

As per coding guidelines: Docstrings are required for all classes and functions in public headers in non-detail namespaces. Docstrings should include a brief description of purpose, document all parameters, return values, and any preconditions, postconditions, or exceptions.

Also applies to: 413-432, 434-455, 484-498

include/boost/capy/task.hpp (1)

239-245: ⚠️ Potential issue | 🟡 Minor

Document await_suspend parameters and return value.
This is a public API method; the doc should spell out the meaning of cont, caller_ex, and token, plus what handle is returned.

✍️ Suggested doc addition
-    /// Start execution with the caller's context.
+    /// Start execution with the caller's context.
+    /// `@param` cont Continuation to resume when the task completes.
+    /// `@param` caller_ex Executor used to schedule continuation resumption.
+    /// `@param` token Stop token propagated into the task.
+    /// `@return` Coroutine handle to resume this task.
     coro await_suspend(coro cont, executor_ref caller_ex, std::stop_token token)

As per coding guidelines: Docstrings are required for all classes and functions in public headers in non-detail namespaces. Docstrings should include a brief description of purpose, document all parameters, return values, and any preconditions, postconditions, or exceptions.

🤖 Fix all issues with AI agents
In `@include/boost/capy/concept/io_launchable_task.hpp`:
- Around line 82-92: The example in template<IoLaunchableTask Task>
blocking_run(Task task) calls task.release() before retrieving the coroutine
handle, which clears ownership and may leave task.handle() null; change the
sequence to capture the handle first (auto h = task.handle()), then release
ownership (task.release() or make release() return the handle), then call
h.resume() and check h.promise().exception(), ensuring the frame is properly
destroyed after exception handling; update references to task.release(),
task.handle(), and h.resume() accordingly.

In `@include/boost/capy/read.hpp`:
- Around line 29-69: The doc for read should use the canonical io_result tuple
syntax and explicitly define the type parameter: update the `@return` block for
function read to state it yields io_result<T1,std::size_t> (no spaces) and add a
short sentence defining T1 as the error_code type (e.g. T1 is the error
code/condition type returned on failure), keeping the rest of the behavior notes
the same; adjust any examples/mentions of the return to match
io_result<T1,std::size_t>.
- Around line 93-133: Replace the plain tuple return description with the
canonical io_result form and add a T1 definition: change the sentence "An
awaitable yielding `(error_code, std::size_t)`." to "An awaitable yielding
io_result<T1,std::size_t>." and append a short note after the return paragraph
defining T1 (e.g., "T1 is the error/condition type (for example
boost::system::error_code)"). Update any similar tuple formatting in the
function template read declaration/comments so there are no internal spaces in
the io_result template (use io_result<T1,std::size_t>).

In `@include/boost/capy/task.hpp`:
- Around line 254-267: The documentation for release() is incorrect: release()
currently nulls h_ so handle() will not return the original coroutine handle
after calling release(); update either the docs or implementation. To fix,
either revise the comment on release() to state that the caller must capture the
handle before calling release() (mention release(), handle(), and member h_), or
change release() to return the original handle (e.g., swap/return h_ instead of
setting it to nullptr) and update callers accordingly so ownership semantics are
consistent.
🧹 Nitpick comments (4)
include/boost/capy/ex/recycling_memory_resource.hpp (1)

38-43: Clarify the undefined ex variable in the example.

The example uses ex without defining or explaining what it represents. Consider adding a comment to clarify that ex should be an executor or execution context, which would make the example more self-contained and helpful for users.

📝 Proposed enhancement to clarify the example
     `@par` Example
     `@code`
+    // where ex is an executor or execution context
     auto* mr = get_recycling_memory_resource();
     run_async(ex, mr)(my_task());
     `@endcode`
include/boost/capy/read_until.hpp (3)

233-238: Minor Doxygen list formatting issue.

Line 233 uses @li for the introductory phrase "The operation completes when:", but this should be plain text followed by the actual list items. The same issue appears at line 311.

📝 Suggested fix
-    `@li` The operation completes when:
-    `@li` The match condition returns a valid position
+    The operation completes when:
+    `@li` The match condition returns a valid position

276-278: Example uses internal detail:: namespace function.

The example calls detail::throw_system_error(ec) which is an implementation detail. Consider using a public error handling pattern or a standard throw mechanism that users can replicate without relying on internal APIs.

📝 Suggested alternative
-        if( ec.failed() )
-            detail::throw_system_error( ec );
+        if( ec.failed() )
+            throw system_error( ec );

Or if system_error isn't directly available, reference the appropriate public API for error handling.


254-258: Consider clarifying error code vs. error condition semantics.

The documentation correctly instructs users to compare against cond::eof, cond::not_found, etc. However, the implementation returns error::eof, error::not_found. This relies on Boost.System's error_code/error_condition equivalence. A brief note like "error codes from this operation compare equal to the following conditions" could help users unfamiliar with this pattern.

Comment on lines +82 to +92
@code
template<IoLaunchableTask Task>
void blocking_run( Task task )
{
task.release(); // take ownership of the frame
auto h = task.handle();
h.resume();

if( auto ep = h.promise().exception() )
std::rethrow_exception( ep );
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Example releases before retrieving the handle.
task::release() clears ownership (and task returns a null handle), so the example can resume a null handle. Capture the handle before release (or have release() return it) and ensure the frame is destroyed.

🐛 Suggested fix for the example
-        task.release();  // take ownership of the frame
-        auto h = task.handle();
+        auto h = task.handle();
+        task.release();  // take ownership of the frame
         h.resume();
 
-        if( auto ep = h.promise().exception() )
-            std::rethrow_exception( ep );
+        if( auto ep = h.promise().exception() )
+        {
+            h.destroy();
+            std::rethrow_exception( ep );
+        }
+        h.destroy();
🤖 Prompt for AI Agents
In `@include/boost/capy/concept/io_launchable_task.hpp` around lines 82 - 92, The
example in template<IoLaunchableTask Task> blocking_run(Task task) calls
task.release() before retrieving the coroutine handle, which clears ownership
and may leave task.handle() null; change the sequence to capture the handle
first (auto h = task.handle()), then release ownership (task.release() or make
release() return the handle), then call h.resume() and check
h.promise().exception(), ensuring the frame is properly destroyed after
exception handling; update references to task.release(), task.handle(), and
h.resume() accordingly.

Comment on lines +29 to +69
/** Asynchronously read until the buffer sequence is full.

This function reads data from the stream into the buffer sequence
until either the entire buffer sequence is filled or an error
occurs (including end-of-file).
Reads data from the stream by calling `read_some` repeatedly
until the entire buffer sequence is filled or an error occurs.

@tparam Stream The stream type, must satisfy @ref ReadStream.
@tparam MB The buffer sequence type, must satisfy
@ref MutableBufferSequence.
@li The operation completes when:
@li The buffer sequence is completely filled
@li An error occurs (including `cond::eof`)
@li The operation is cancelled

@param stream The stream to read from.
@param buffers The buffer sequence to read into.
@par Cancellation
Supports cancellation via `stop_token` propagated through the
IoAwaitable protocol. When cancelled, returns with `cond::canceled`.

@return A task that yields `(std::error_code, std::size_t)`.
On success, `ec` is default-constructed (no error) and `n` is
`buffer_size(buffers)`. On error or EOF, `ec` contains the
error code and `n` is the total number of bytes written before
the error.
@param stream The stream to read from. The caller retains ownership.
@param buffers The buffer sequence to fill. The caller retains
ownership and must ensure validity until the operation completes.

@return An awaitable yielding `(error_code, std::size_t)`.
On success, `n` equals `buffer_size(buffers)`. On error,
`n` is the number of bytes read before the error. Compare
error codes to conditions:
@li `cond::eof` - Stream reached end before buffer was filled
@li `cond::canceled` - Operation was cancelled

@par Example

@code
task<void> example(ReadStream auto& stream)
task<> read_message( ReadStream auto& stream )
{
char buf[1024];
auto [ec, n] = co_await read(stream, mutable_buffer(buf, sizeof(buf)));
if (ec == cond::eof)
{
// Handle end-of-file
}
else if (ec)
{
// Handle other error
}
// n bytes were read into buf
char header[16];
auto [ec, n] = co_await read( stream, mutable_buffer( header ) );
if( ec == cond::eof )
co_return; // Connection closed
if( ec.failed() )
detail::throw_system_error( ec );
// header contains exactly 16 bytes
}
@endcode

@see ReadStream, MutableBufferSequence
@see read_some, ReadStream, MutableBufferSequence
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the canonical io_result return tuple format and define T1.

The return description should use the canonical tuple formatting (no internal spaces) and explicitly define the io_result type parameter at the end of the javadoc description.

Suggested doc tweak
-    `@return` An awaitable yielding `(error_code, std::size_t)`.
+    `@return` An awaitable yielding `(error_code,std::size_t)`.
         On success, `n` equals `buffer_size(buffers)`. On error,
         `n` is the number of bytes read before the error. Compare
         error codes to conditions:
         `@li` `cond::eof` - Stream reached end before buffer was filled
         `@li` `cond::canceled` - Operation was cancelled
+
+    T1 is `std::size_t` bytes transferred.

As per coding guidelines: use the canonical io_result return format (no internal spaces) and explain the meaning of io_result type parameters at the end of the javadoc description.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/** Asynchronously read until the buffer sequence is full.
This function reads data from the stream into the buffer sequence
until either the entire buffer sequence is filled or an error
occurs (including end-of-file).
Reads data from the stream by calling `read_some` repeatedly
until the entire buffer sequence is filled or an error occurs.
@tparam Stream The stream type, must satisfy @ref ReadStream.
@tparam MB The buffer sequence type, must satisfy
@ref MutableBufferSequence.
@li The operation completes when:
@li The buffer sequence is completely filled
@li An error occurs (including `cond::eof`)
@li The operation is cancelled
@param stream The stream to read from.
@param buffers The buffer sequence to read into.
@par Cancellation
Supports cancellation via `stop_token` propagated through the
IoAwaitable protocol. When cancelled, returns with `cond::canceled`.
@return A task that yields `(std::error_code, std::size_t)`.
On success, `ec` is default-constructed (no error) and `n` is
`buffer_size(buffers)`. On error or EOF, `ec` contains the
error code and `n` is the total number of bytes written before
the error.
@param stream The stream to read from. The caller retains ownership.
@param buffers The buffer sequence to fill. The caller retains
ownership and must ensure validity until the operation completes.
@return An awaitable yielding `(error_code, std::size_t)`.
On success, `n` equals `buffer_size(buffers)`. On error,
`n` is the number of bytes read before the error. Compare
error codes to conditions:
@li `cond::eof` - Stream reached end before buffer was filled
@li `cond::canceled` - Operation was cancelled
@par Example
@code
task<void> example(ReadStream auto& stream)
task<> read_message( ReadStream auto& stream )
{
char buf[1024];
auto [ec, n] = co_await read(stream, mutable_buffer(buf, sizeof(buf)));
if (ec == cond::eof)
{
// Handle end-of-file
}
else if (ec)
{
// Handle other error
}
// n bytes were read into buf
char header[16];
auto [ec, n] = co_await read( stream, mutable_buffer( header ) );
if( ec == cond::eof )
co_return; // Connection closed
if( ec.failed() )
detail::throw_system_error( ec );
// header contains exactly 16 bytes
}
@endcode
@see ReadStream, MutableBufferSequence
@see read_some, ReadStream, MutableBufferSequence
/** Asynchronously read until the buffer sequence is full.
Reads data from the stream by calling `read_some` repeatedly
until the entire buffer sequence is filled or an error occurs.
`@li` The operation completes when:
`@li` The buffer sequence is completely filled
`@li` An error occurs (including `cond::eof`)
`@li` The operation is cancelled
`@par` Cancellation
Supports cancellation via `stop_token` propagated through the
IoAwaitable protocol. When cancelled, returns with `cond::canceled`.
`@param` stream The stream to read from. The caller retains ownership.
`@param` buffers The buffer sequence to fill. The caller retains
ownership and must ensure validity until the operation completes.
`@return` An awaitable yielding `(error_code,std::size_t)`.
On success, `n` equals `buffer_size(buffers)`. On error,
`n` is the number of bytes read before the error. Compare
error codes to conditions:
`@li` `cond::eof` - Stream reached end before buffer was filled
`@li` `cond::canceled` - Operation was cancelled
T1 is `std::size_t` bytes transferred.
`@par` Example
`@code`
task<> read_message( ReadStream auto& stream )
{
char header[16];
auto [ec, n] = co_await read( stream, mutable_buffer( header ) );
if( ec == cond::eof )
co_return; // Connection closed
if( ec.failed() )
detail::throw_system_error( ec );
// header contains exactly 16 bytes
}
`@endcode`
`@see` read_some, ReadStream, MutableBufferSequence
🤖 Prompt for AI Agents
In `@include/boost/capy/read.hpp` around lines 29 - 69, The doc for read should
use the canonical io_result tuple syntax and explicitly define the type
parameter: update the `@return` block for function read to state it yields
io_result<T1,std::size_t> (no spaces) and add a short sentence defining T1 as
the error_code type (e.g. T1 is the error code/condition type returned on
failure), keeping the rest of the behavior notes the same; adjust any
examples/mentions of the return to match io_result<T1,std::size_t>.

Comment on lines +93 to 133
/** Asynchronously read all data from a stream into a dynamic buffer.

Reads data by calling `read_some` repeatedly until EOF is reached
or an error occurs. Data is appended using prepare/commit semantics.
The buffer grows with 1.5x factor when filled.

This function reads data from the stream into the dynamic buffer
until end-of-file is reached or an error occurs. Data is appended
to the buffer using prepare/commit semantics.
@li The operation completes when:
@li End-of-stream is reached (`cond::eof`)
@li An error occurs
@li The operation is cancelled

The buffer grows using a strategy that starts with `initial_amount`
bytes and grows by a factor of 1.5 when filled.
@par Cancellation
Supports cancellation via `stop_token` propagated through the
IoAwaitable protocol. When cancelled, returns with `cond::canceled`.

@param stream The stream to read from, must satisfy @ref ReadStream.
@param buffers The dynamic buffer to read into.
@param initial_amount The initial number of bytes to prepare.
@param stream The stream to read from. The caller retains ownership.
@param buffers The dynamic buffer to append data to. Must remain
valid until the operation completes.
@param initial_amount Initial bytes to prepare (default 2048).

@return A task that yields `(std::error_code, std::size_t)`.
On success (EOF reached), `ec` is default-constructed and `n`
is the total number of bytes read. On error, `ec` contains the
error code and `n` is the total number of bytes read before
the error.
@return An awaitable yielding `(error_code, std::size_t)`.
On success (EOF), `ec` is clear and `n` is total bytes read.
On error, `n` is bytes read before the error. Compare error
codes to conditions:
@li `cond::canceled` - Operation was cancelled

@par Example

@code
task<void> example(ReadStream auto& stream)
task<std::string> read_body( ReadStream auto& stream )
{
std::string body;
auto [ec, n] = co_await read(stream, string_dynamic_buffer(&body));
// body contains n bytes of data
auto [ec, n] = co_await read( stream, string_dynamic_buffer( &body ) );
if( ec.failed() )
detail::throw_system_error( ec );
return body;
}
@endcode

@see ReadStream, DynamicBufferParam
@see read_some, ReadStream, DynamicBufferParam
*/
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the canonical io_result return tuple format and define T1.

Same issue here: format the tuple without internal spaces and add the T1 meaning at the end of the javadoc description.

Suggested doc tweak
-    `@return` An awaitable yielding `(error_code, std::size_t)`.
+    `@return` An awaitable yielding `(error_code,std::size_t)`.
         On success (EOF), `ec` is clear and `n` is total bytes read.
         On error, `n` is bytes read before the error. Compare error
         codes to conditions:
         `@li` `cond::canceled` - Operation was cancelled
+
+    T1 is `std::size_t` bytes transferred.

As per coding guidelines: use the canonical io_result return format (no internal spaces) and explain the meaning of io_result type parameters at the end of the javadoc description.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/** Asynchronously read all data from a stream into a dynamic buffer.
Reads data by calling `read_some` repeatedly until EOF is reached
or an error occurs. Data is appended using prepare/commit semantics.
The buffer grows with 1.5x factor when filled.
This function reads data from the stream into the dynamic buffer
until end-of-file is reached or an error occurs. Data is appended
to the buffer using prepare/commit semantics.
@li The operation completes when:
@li End-of-stream is reached (`cond::eof`)
@li An error occurs
@li The operation is cancelled
The buffer grows using a strategy that starts with `initial_amount`
bytes and grows by a factor of 1.5 when filled.
@par Cancellation
Supports cancellation via `stop_token` propagated through the
IoAwaitable protocol. When cancelled, returns with `cond::canceled`.
@param stream The stream to read from, must satisfy @ref ReadStream.
@param buffers The dynamic buffer to read into.
@param initial_amount The initial number of bytes to prepare.
@param stream The stream to read from. The caller retains ownership.
@param buffers The dynamic buffer to append data to. Must remain
valid until the operation completes.
@param initial_amount Initial bytes to prepare (default 2048).
@return A task that yields `(std::error_code, std::size_t)`.
On success (EOF reached), `ec` is default-constructed and `n`
is the total number of bytes read. On error, `ec` contains the
error code and `n` is the total number of bytes read before
the error.
@return An awaitable yielding `(error_code, std::size_t)`.
On success (EOF), `ec` is clear and `n` is total bytes read.
On error, `n` is bytes read before the error. Compare error
codes to conditions:
@li `cond::canceled` - Operation was cancelled
@par Example
@code
task<void> example(ReadStream auto& stream)
task<std::string> read_body( ReadStream auto& stream )
{
std::string body;
auto [ec, n] = co_await read(stream, string_dynamic_buffer(&body));
// body contains n bytes of data
auto [ec, n] = co_await read( stream, string_dynamic_buffer( &body ) );
if( ec.failed() )
detail::throw_system_error( ec );
return body;
}
@endcode
@see ReadStream, DynamicBufferParam
@see read_some, ReadStream, DynamicBufferParam
*/
/** Asynchronously read all data from a stream into a dynamic buffer.
Reads data by calling `read_some` repeatedly until EOF is reached
or an error occurs. Data is appended using prepare/commit semantics.
The buffer grows with 1.5x factor when filled.
`@li` The operation completes when:
`@li` End-of-stream is reached (`cond::eof`)
`@li` An error occurs
`@li` The operation is cancelled
`@par` Cancellation
Supports cancellation via `stop_token` propagated through the
IoAwaitable protocol. When cancelled, returns with `cond::canceled`.
`@param` stream The stream to read from. The caller retains ownership.
`@param` buffers The dynamic buffer to append data to. Must remain
valid until the operation completes.
`@param` initial_amount Initial bytes to prepare (default 2048).
`@return` An awaitable yielding `(error_code,std::size_t)`.
On success (EOF), `ec` is clear and `n` is total bytes read.
On error, `n` is bytes read before the error. Compare error
codes to conditions:
`@li` `cond::canceled` - Operation was cancelled
T1 is `std::size_t` bytes transferred.
`@par` Example
`@code`
task<std::string> read_body( ReadStream auto& stream )
{
std::string body;
auto [ec, n] = co_await read( stream, string_dynamic_buffer( &body ) );
if( ec.failed() )
detail::throw_system_error( ec );
return body;
}
`@endcode`
`@see` read_some, ReadStream, DynamicBufferParam
*/
🤖 Prompt for AI Agents
In `@include/boost/capy/read.hpp` around lines 93 - 133, Replace the plain tuple
return description with the canonical io_result form and add a T1 definition:
change the sentence "An awaitable yielding `(error_code, std::size_t)`." to "An
awaitable yielding io_result<T1,std::size_t>." and append a short note after the
return paragraph defining T1 (e.g., "T1 is the error/condition type (for example
boost::system::error_code)"). Update any similar tuple formatting in the
function template read declaration/comments so there are no internal spaces in
the io_result template (use io_result<T1,std::size_t>).

Comment on lines +254 to 267
/** Release ownership of the coroutine frame.

After calling this, destroying the task does not destroy the
coroutine frame. The caller becomes responsible for the frame's
lifetime.

After calling this, the task no longer owns the handle and will
not destroy it. The caller is responsible for the handle's lifetime.
@par Postconditions
`handle()` returns the original handle, but the task no longer
owns it.
*/
void release() noexcept
{
h_ = nullptr;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

release() postcondition contradicts the implementation.
release() sets h_ to nullptr, so handle() does not return the original handle afterward. The docs should reflect the actual ownership/usage pattern (capture the handle before release), or the implementation should be adjusted to preserve/return the handle.

📌 Doc fix aligned with current behavior
     `@par` Postconditions
-        `handle()` returns the original handle, but the task no longer
-        owns it.
+        `handle()` returns `nullptr` after release. Capture the handle
+        before calling `release()`.
🤖 Prompt for AI Agents
In `@include/boost/capy/task.hpp` around lines 254 - 267, The documentation for
release() is incorrect: release() currently nulls h_ so handle() will not return
the original coroutine handle after calling release(); update either the docs or
implementation. To fix, either revise the comment on release() to state that the
caller must capture the handle before calling release() (mention release(),
handle(), and member h_), or change release() to return the original handle
(e.g., swap/return h_ instead of setting it to nullptr) and update callers
accordingly so ownership semantics are consistent.

@cppalliance-bot
Copy link

GCOVR code coverage report https://121.capy.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://121.capy.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://121.capy.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-02-01 22:05:15 UTC

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@include/boost/capy/read_until.hpp`:
- Around line 329-334: Update the return documentation in the read_until.hpp
docblock to use the canonical io_result tuple format without the internal space
— change `(error_code, std::size_t)` to `(error_code,std::size_t)` — and append
a brief explanation of the type parameters (T1 and T2) for the io_result (e.g.,
T1 = number of bytes transferred, T2 = error_code) at the end of the
description; apply this change in the read_until-related comment block in
include/boost/capy/read_until.hpp so the return line and final sentence reflect
the canonical io_result format and document T1.
- Around line 251-257: Update the return description to use the canonical
io_result tuple format with no internal space — change `(error_code,
std::size_t)` to `(error_code,std::size_t)` — and append a short explanation of
the io_result template parameters (T1: error_code, T2: number of bytes
transferred) at the end of the docblock; locate the docblock in
include/boost/capy/read_until.hpp around the read_until/io_result documentation
and adjust the types and add the T1/T2 note accordingly.

In `@include/boost/capy/read.hpp`:
- Around line 179-183: Update the return description to use the canonical
io_result tuple format with no internal space — change `(error_code,
std::size_t)` to `(error_code,std::size_t)` — and append an explanation of the
template parameter T1 (e.g., "T1 is the number of bytes transferred") at the end
of the docblock; adjust the comment near the cond::canceled line and any
references to io_result to reflect this wording consistently.

In `@include/boost/capy/when_all.hpp`:
- Around line 395-419: Add a brief `@tparam` Ts docline to the when_all template
explaining that each Ts corresponds to the result type of an input task<Ts> (and
that the template accepts a parameter pack of those result types), and mention
any required constraints (e.g., that each task<Ts> is
Awaitable/MoveConstructible as assumed by when_all). Update the documentation
block for the when_all function template (the template taking template parameter
pack Ts) to include that `@tparam` Ts describes the result types of the input
tasks and any minimal concept/constraint required by the implementation.
🧹 Nitpick comments (3)
.cursor/rules/doc-awaitable.mdc (1)

20-27: Add explicit io_result return-format guidance.
The checklist’s @return guidance doesn’t yet encode the required io_result phrasing or the “explain type parameters” rule; adding a short rule here will keep docs consistent.

✍️ Suggested checklist tweak
- - **`@return`** — Describe the returned aggregate and its elements. The first element is always `error_code`. Name and describe each subsequent element (e.g. "bytes_transferred", "endpoint"). Note that the result is customarily destructured by the caller.
+ - **`@return`** — Describe the returned aggregate and its elements. Start with: `An awaitable yielding `(error_code, T1, T2, T3)`'` (omit unused trailing elements; use only type names). The first element is always `error_code`. Name and describe each subsequent element (e.g. "bytes_transferred", "endpoint"). Note that the result is customarily destructured by the caller.
+ - **io_result parameters** — End the `@return` description by explaining what each of `T1`, `T2`, and `T3` represents (if present).

As per coding guidelines, Document io_result<T1,T2,T3> return types with the format: 'An awaitable yielding (error_code, T1, T2, T3)' - include only type names without internal spaces; Explain the meaning of io_result type parameters (T1, T2, T3) at the end of the javadoc description.

include/boost/capy/buffers.hpp (1)

301-337: Add a brief semantics/lifetime note for buffer sequences.
These are core buffer-sequence docs; a short paragraph on non-ownership, lifetime, and why bidirectional (multi-pass) is required would make the contract more first‑principles.

✍️ Suggested addition
 /** Concept for sequences of read-only buffer regions.
@@
     `@par` Syntactic Requirements
     `@li` Convertible to `const_buffer`, OR
     `@li` A bidirectional range with value type convertible to `const_buffer`
 
+    `@par` Semantics
+    The sequence is non-owning; buffer memory must remain valid for the
+    duration of any operation that uses it, and the range is required to
+    be multi-pass (hence bidirectional) so algorithms can iterate more than once.
+
     `@see` const_buffer, MutableBufferSequence
 */
@@
 /** Concept for sequences of writable buffer regions.
@@
     `@par` Syntactic Requirements
     `@li` Convertible to `mutable_buffer`, OR
     `@li` A bidirectional range with value type convertible to `mutable_buffer`
 
+    `@par` Semantics
+    Same non-ownership and lifetime requirements as `ConstBufferSequence`.
+
     `@see` mutable_buffer, ConstBufferSequence
 */

Based on learnings, Core topics (task/coroutine documentation, buffer sequences, streams) require deeper, more methodical treatment with thorough reasoning and first principles explanations.

.cursor/rules/doc-errors.mdc (1)

27-36: Minor inconsistency with existing code example.

The example uses else if(ec) for checking errors, while the cond.hpp example at line 34 uses else if( ec.failed() ). Both are valid, but consider aligning the examples for consistency across the codebase.

Comment on lines +251 to +257
@return An awaitable yielding `(error_code, std::size_t)`.
On success, `n` is the position returned by the match condition
(bytes up to and including the matched delimiter). Compare error
codes to conditions:
@li `cond::eof` - EOF before match; `n` is buffer size
@li `cond::not_found` - `max_size()` reached before match
@li `cond::canceled` - Operation was cancelled
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the canonical io_result return tuple format and define T1.

The return description uses (error_code, std::size_t) with internal space. Per coding guidelines, use (error_code,std::size_t) (no space) and add T1 explanation at the end.

Suggested doc tweak
-    `@return` An awaitable yielding `(error_code, std::size_t)`.
+    `@return` An awaitable yielding `(error_code,std::size_t)`.
         On success, `n` is the position returned by the match condition
         (bytes up to and including the matched delimiter). Compare error
         codes to conditions:
         `@li` `cond::eof` - EOF before match; `n` is buffer size
         `@li` `cond::not_found` - `max_size()` reached before match
         `@li` `cond::canceled` - Operation was cancelled
+
+    T1 is `std::size_t` position after matched delimiter.

As per coding guidelines: use the canonical io_result return format (no internal spaces) and explain the meaning of io_result type parameters at the end of the javadoc description.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@return An awaitable yielding `(error_code, std::size_t)`.
On success, `n` is the position returned by the match condition
(bytes up to and including the matched delimiter). Compare error
codes to conditions:
@li `cond::eof` - EOF before match; `n` is buffer size
@li `cond::not_found` - `max_size()` reached before match
@li `cond::canceled` - Operation was cancelled
`@return` An awaitable yielding `(error_code,std::size_t)`.
On success, `n` is the position returned by the match condition
(bytes up to and including the matched delimiter). Compare error
codes to conditions:
`@li` `cond::eof` - EOF before match; `n` is buffer size
`@li` `cond::not_found` - `max_size()` reached before match
`@li` `cond::canceled` - Operation was cancelled
T1 is `std::size_t` position after matched delimiter.
🤖 Prompt for AI Agents
In `@include/boost/capy/read_until.hpp` around lines 251 - 257, Update the return
description to use the canonical io_result tuple format with no internal space —
change `(error_code, std::size_t)` to `(error_code,std::size_t)` — and append a
short explanation of the io_result template parameters (T1: error_code, T2:
number of bytes transferred) at the end of the docblock; locate the docblock in
include/boost/capy/read_until.hpp around the read_until/io_result documentation
and adjust the types and add the T1/T2 note accordingly.

Comment on lines +329 to +334
@return An awaitable yielding `(error_code, std::size_t)`.
On success, `n` is bytes up to and including the delimiter.
Compare error codes to conditions:
@li `cond::eof` - EOF before delimiter; `n` is buffer size
@li `cond::not_found` - `max_size()` reached before delimiter
@li `cond::canceled` - Operation was cancelled
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the canonical io_result return tuple format and define T1.

The return description uses (error_code, std::size_t) with internal space. Per coding guidelines, use (error_code,std::size_t) (no space) and add T1 explanation at the end.

Suggested doc tweak
-    `@return` An awaitable yielding `(error_code, std::size_t)`.
+    `@return` An awaitable yielding `(error_code,std::size_t)`.
         On success, `n` is bytes up to and including the delimiter.
         Compare error codes to conditions:
         `@li` `cond::eof` - EOF before delimiter; `n` is buffer size
         `@li` `cond::not_found` - `max_size()` reached before delimiter
         `@li` `cond::canceled` - Operation was cancelled
+
+    T1 is `std::size_t` bytes through delimiter.

As per coding guidelines: use the canonical io_result return format (no internal spaces) and explain the meaning of io_result type parameters at the end of the javadoc description.

🤖 Prompt for AI Agents
In `@include/boost/capy/read_until.hpp` around lines 329 - 334, Update the return
documentation in the read_until.hpp docblock to use the canonical io_result
tuple format without the internal space — change `(error_code, std::size_t)` to
`(error_code,std::size_t)` — and append a brief explanation of the type
parameters (T1 and T2) for the io_result (e.g., T1 = number of bytes
transferred, T2 = error_code) at the end of the description; apply this change
in the read_until-related comment block in include/boost/capy/read_until.hpp so
the return line and final sentence reflect the canonical io_result format and
document T1.

Comment on lines +179 to +183
@return An awaitable yielding `(error_code, std::size_t)`.
On success (EOF), `ec` is clear and `n` is total bytes read.
On error, `n` is bytes read before the error. Compare error
codes to conditions:
@li `cond::canceled` - Operation was cancelled
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the canonical io_result return tuple format and define T1.

The return description uses (error_code, std::size_t) with internal space. Per coding guidelines, use (error_code,std::size_t) (no space) and add T1 explanation at the end.

Suggested doc tweak
-    `@return` An awaitable yielding `(error_code, std::size_t)`.
+    `@return` An awaitable yielding `(error_code,std::size_t)`.
         On success (EOF), `ec` is clear and `n` is total bytes read.
         On error, `n` is bytes read before the error. Compare error
         codes to conditions:
         `@li` `cond::canceled` - Operation was cancelled
+
+    T1 is `std::size_t` bytes transferred.

As per coding guidelines: use the canonical io_result return format (no internal spaces) and explain the meaning of io_result type parameters at the end of the javadoc description.

🤖 Prompt for AI Agents
In `@include/boost/capy/read.hpp` around lines 179 - 183, Update the return
description to use the canonical io_result tuple format with no internal space —
change `(error_code, std::size_t)` to `(error_code,std::size_t)` — and append an
explanation of the template parameter T1 (e.g., "T1 is the number of bytes
transferred") at the end of the docblock; adjust the comment near the
cond::canceled line and any references to io_result to reflect this wording
consistently.

Comment on lines +395 to +419
/** Execute multiple tasks concurrently and collect their results.

Launches all tasks simultaneously and waits for all to complete
before returning. Results are collected in input order. If any
task throws, cancellation is requested for siblings and the first
exception is rethrown after all tasks complete.

@li All child tasks run concurrently on the caller's executor
@li Results are returned as a tuple in input order
@li Void-returning tasks do not contribute to the result tuple
@li If all tasks return void, `when_all` returns `task<void>`
@li First exception wins; subsequent exceptions are discarded
@li Stop is requested for siblings on first error
@li Completes only after all children have finished

@par Thread Safety
The returned task must be awaited from a single execution context.
Child tasks execute concurrently but complete through the caller's
executor.

@param tasks The tasks to execute concurrently. Each task is
consumed (moved-from) when `when_all` is awaited.

@return A task yielding a tuple of non-void results. Returns
`task<void>` when all input tasks return void.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add @tparam Ts documentation for the task result types.

The function template docs don’t describe the template parameters or their constraints. Please add a brief @tparam Ts line clarifying that each Ts is the result type of the corresponding input task<Ts>.
As per coding guidelines: “Docstrings are required for all classes and functions in public headers in non-detail namespaces… For template parameters, document constraints or concepts required.”

✍️ Suggested doc addition
     `@li` Completes only after all children have finished
 
+    `@tparam` Ts The result types of the input tasks (deduced from `tasks`).
     `@param` tasks The tasks to execute concurrently. Each task is
         consumed (moved-from) when `when_all` is awaited.
🤖 Prompt for AI Agents
In `@include/boost/capy/when_all.hpp` around lines 395 - 419, Add a brief `@tparam`
Ts docline to the when_all template explaining that each Ts corresponds to the
result type of an input task<Ts> (and that the template accepts a parameter pack
of those result types), and mention any required constraints (e.g., that each
task<Ts> is Awaitable/MoveConstructible as assumed by when_all). Update the
documentation block for the when_all function template (the template taking
template parameter pack Ts) to include that `@tparam` Ts describes the result
types of the input tasks and any minimal concept/constraint required by the
implementation.

@cppalliance-bot
Copy link

GCOVR code coverage report https://121.capy.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://121.capy.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://121.capy.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-02-01 22:11:52 UTC

@vinniefalco vinniefalco merged commit 59af006 into cppalliance:develop Feb 1, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants