Skip to content

[Copilot] Add wil::batched_range and wil::already_complete / already_failed C++/WinRT helpers - #669

Open
Jon Wiswall (jonwis) wants to merge 8 commits into
microsoft:masterfrom
jonwis:jonwis-microsoft-cppwinrt-abi-interop-fixes
Open

[Copilot] Add wil::batched_range and wil::already_complete / already_failed C++/WinRT helpers#669
Jon Wiswall (jonwis) wants to merge 8 commits into
microsoft:masterfrom
jonwis:jonwis-microsoft-cppwinrt-abi-interop-fixes

Conversation

@jonwis

Copy link
Copy Markdown
Member

What this adds

Two C++/WinRT helpers in wil/cppwinrt_helpers.h, spun out of the review discussion on microsoft/cppwinrt#1608 — C++/WinRT stays projection-only, so these interop conveniences live in WIL. Resolves #663 and #664.

wil::already_complete / wil::already_failed (#663)

Return an IAsyncAction / IAsyncOperation<T> that is already settled, with no coroutine frame — co_await, .get(), and a Completed handler all complete synchronously.

IAsyncOperation<int32_t> GetCachedValue()
{
    if (m_haveValue)
        return wil::already_complete(m_value);   // no coroutine frame for the already-known answer
    return ComputeValueAsync();
}

wil::already_complete();          // completed IAsyncAction
wil::already_failed(hr);          // faulted IAsyncAction, GetResults() throws hr
wil::already_failed<T>(hr);       // faulted IAsyncOperation<T>

A single winrt::implements object over the async interface + IAsyncInfo; Completed is fired inline with a single-assignment guard (throws hresult_illegal_delegate_assignment on a second set), no mutex — the compact shape from Raymond Chen's "already-completed asynchronous activity" series. The result storage is null-initialized for projected object types so the faulted path never activates (or requires a default constructor for) a runtimeclass result.

wil::batched_range (#664)

A range-for adapter that prefetches elements in blocks via GetMany instead of one ABI round-trip per element. On a cross-process or heavily-marshaled collection the per-element crossings dominate, so batching cuts them to roughly one per block.

for (winrt::hstring const& item : wil::batched_range(collection))
    use(item);

Works for IVector<T>, IVectorView<T>, IIterable<T>, IIterator<T>, and anything C++/WinRT projects those for (PropertySet, IMap<K,V>, …). Indexed collections prefetch with GetMany(index, …); iterable-only collections buffer through IIterator::GetMany. It's a single-pass input range — a yielded element outlives the step that produced it, matching the observable behavior of wil::to_vector. Block prefetch stops once GetMany returns a short block. Both shapes share one input iterator that differs only in how a block is refilled.

Testing

New coverage in tests/CppWinRTTests.cpp: already_complete/already_failed completed/faulted status, co_await and .get(), inline Completed firing, double-assignment throwing, a runtimeclass (Uri) result on the faulted path, and non-trivial result round-trip; batched_range across the indexed and iterable paths, map/IKeyValuePair, exact block-boundary multiples (1/127/128/129/256/257) for ordering and clean termination, empty collections, duck-typed non-WinRT shapes, and an iterator advanced past its start yielding only the remainder. Built clean across clang and MSVC (debug + relwithdebinfo); full [cppwinrt] suite passes.

References

(via Copilot)

Jon Wiswall (jonwis) and others added 8 commits August 24, 2026 15:11
Implements the two helpers spun out of microsoft/cppwinrt#1608 into WIL:

- make_ready() / make_ready(value) / make_failed(): already-settled
  IAsyncAction / IAsyncOperation<T> with no coroutine frame, firing
  Completed inline with a single-assignment guard (microsoft#663).
- batched(collection): range-for adapter that prefetches elements in
  blocks via GetMany instead of one ABI crossing per element, for
  indexed (IVector/IVectorView) and iterable-only (IIterable/IIterator,
  including map IKeyValuePair) collections (microsoft#664).

Both live in cppwinrt_helpers.h next to to_vector, reusing its
is_winrt_vector_like / is_winrt_iterator_like detection and the
re-includable per-header guard pattern.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Collapse batched_indexed_iterator and batched_buffered_iterator into a
single input iterator parameterized on a small refill 'source' policy.
As a range-for-only adapter it no longer needs Size(), the GetAt fallback,
or random access: it block-prefetches via GetMany and stops when a block
comes back short, exactly matching to_vector's exhaustion rule.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Rename the public helper and its detail range struct (batched_range ->
batched_view) to avoid a name clash.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover exact block-boundary multiples (1/127/128/129/256/257) on both the
indexed and iterable paths to exercise the full-block-then-empty-refill
termination, ordering across seams, single element, and an IIterator
advanced past its start yielding only the remainder.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The failed-operation path never reads m_result (GetResults throws first), but
default-constructing it would activate a projected runtimeclass result -- or
fail to compile for a class without a default constructor (e.g. Uri). Init the
storage with a null handle for object types and a value-init otherwise.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.

Ready-made completed IAsyncOperation<T> / IAsyncAction (make_ready)

1 participant