Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Don't build C++/WinRT yourself - just download the latest version here: https://

## Working on the compiler

If you really want to build it yourself, the simplest way to do so is to run the `build_test_all.cmd` script in the root directory. Developers needing to work on the C++/WinRT compiler itself should go through the following steps to arrive at an efficient inner loop:
If you really want to build it yourself, the simplest way to do so is to run the `build_test_all.cmd` script in the root directory. It takes optional positional arguments: `build_test_all.cmd [platform] [configuration] [version] [clean] [compiler]`, e.g. `build_test_all.cmd x64 Debug 999.999.999.999 "" clang`. The `compiler` argument defaults to `msvc`; pass `clang` (or `clang-cl`) to build the compiler and test projects with the clang-cl toolset, matching the clang-cl leg of CI. Developers needing to work on the C++/WinRT compiler itself should go through the following steps to arrive at an efficient inner loop:

* Open a dev command prompt pointing at the root of the repo.
* Open the `cppwinrt.sln` solution.
Expand Down
32 changes: 19 additions & 13 deletions build_test_all.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,44 @@ set target_platform=%1
set target_configuration=%2
set target_version=%3
set clean_intermediate_files=%4
set target_compiler=%5

if "%target_platform%"=="" set target_platform=x64
if "%target_configuration%"=="" set target_configuration=Release
if "%target_version%"=="" set target_version=999.999.999.999
if "%target_compiler%"=="" set target_compiler=msvc

set compiler_props=
if /i "%target_compiler%"=="clang" set compiler_props=,Clang=1,PlatformToolset=ClangCl
if /i "%target_compiler%"=="clang-cl" set compiler_props=,Clang=1,PlatformToolset=ClangCl

if not exist ".\.nuget" mkdir ".\.nuget"
if not exist ".\.nuget\nuget.exe" powershell -Command "$ProgressPreference = 'SilentlyContinue' ; Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe"

call .nuget\nuget.exe restore cppwinrt.sln"
call .nuget\nuget.exe restore cppwinrt.sln
call .nuget\nuget.exe restore natvis\cppwinrtvisualizer.sln
call .nuget\nuget.exe restore test\nuget\NugetTest.sln

call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:fast_fwd

call msbuild /p:Configuration=%target_configuration%,Platform=%target_platform%,Deployment=Component;CppWinRTBuildVersion=%target_version% natvis\cppwinrtvisualizer.sln
call msbuild /p:Configuration=%target_configuration%,Platform=%target_platform%,Deployment=Standalone;CppWinRTBuildVersion=%target_version% natvis\cppwinrtvisualizer.sln

if "%target_platform%"=="arm64" goto :eof

call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:cppwinrt
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:cppwinrt

call msbuild /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% test\nuget\NugetTest.sln

call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_nocoro
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_cpp20
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_cpp20_no_sourcelocation
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_fast
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_slow
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_module_lock_custom
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_module_lock_none
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_module_lock_none
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\old_tests\test_old
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test_nocoro
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test_cpp20
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test_cpp20_no_sourcelocation
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test_fast
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test_slow
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test_module_lock_custom
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test_module_lock_none
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\test_module_lock_none
Comment thread
jonwis marked this conversation as resolved.
Outdated
call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version%%compiler_props% cppwinrt.sln /t:test\old_tests\test_old

call run_tests.cmd %target_platform% %target_configuration%
91 changes: 91 additions & 0 deletions strings/base_coroutine_foundation.h
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,86 @@ WINRT_IMPL_STD_EXPORT namespace std
};
}

WINRT_EXPORT namespace winrt::impl
{
template <typename Derived, typename AsyncInterface>
struct ready_async_base : implements<Derived, AsyncInterface, Windows::Foundation::IAsyncInfo>
Comment thread
jonwis marked this conversation as resolved.
Outdated
{
using AsyncStatus = Windows::Foundation::AsyncStatus;

void Completed(async_completed_handler_t<AsyncInterface> const& handler)
{
if (m_completed_assigned.exchange(true))
{
throw hresult_illegal_delegate_assignment();
}

if (handler)
{
winrt::impl::invoke(handler, *static_cast<Derived*>(this), AsyncStatus::Completed);
}
}

auto Completed() noexcept
{
return async_completed_handler_t<AsyncInterface>{ nullptr };
}

std::uint32_t Id() const noexcept
{
return 1;
}

AsyncStatus Status() const noexcept
{
return AsyncStatus::Completed;
}

hresult ErrorCode() const noexcept
{
return 0;
}

void Cancel() const noexcept
{
}

void Close() const noexcept
{
}

private:

std::atomic<bool> m_completed_assigned{ false };
};

template <typename TResult>
struct ready_async_operation :
ready_async_base<ready_async_operation<TResult>, Windows::Foundation::IAsyncOperation<TResult>>
{
explicit ready_async_operation(TResult value) : m_result(std::move(value))
{
}

TResult GetResults()
{
return m_result;
}

private:

TResult m_result;
};

struct ready_async_action :
ready_async_base<ready_async_action, Windows::Foundation::IAsyncAction>
{
void GetResults() const noexcept
{
}
};
}

WINRT_EXPORT namespace winrt
{
template <typename... T>
Expand Down Expand Up @@ -884,5 +964,16 @@ WINRT_EXPORT namespace winrt
impl::check_status_canceled(shared->status);
co_return shared->result.GetResults();
}

template <typename TResult>
Windows::Foundation::IAsyncOperation<std::decay_t<TResult>> make_ready(TResult&& value)
{
return make<impl::ready_async_operation<std::decay_t<TResult>>>(std::forward<TResult>(value));
}

inline Windows::Foundation::IAsyncAction make_ready() noexcept
{
return make<impl::ready_async_action>();
}
Comment thread
jonwis marked this conversation as resolved.
Outdated
}
#endif
179 changes: 169 additions & 10 deletions strings/base_iterator.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@

WINRT_EXPORT namespace winrt::impl
{
// Detects whether an indexed (GetAt-capable) collection also exposes the bulk
// GetMany(startIndex, array) accessor. IBindableVectorView and similar have GetAt but no GetMany.
template <typename T>
class has_GetMany_indexed
{
template <typename U, typename E = decltype(std::declval<U>().GetAt(0)),
typename = decltype(std::declval<U const&>().GetMany(0u, std::declval<std::array<E, 1>&>()))>
static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }

public:

static constexpr bool value = get_value<T>(0);
};

// Detects whether an iterator exposes the bulk GetMany(array) accessor. IBindableIterator does not.
template <typename Iterator>
class has_GetMany_iter
{
template <typename U, typename E = decltype(std::declval<U>().Current()),
typename = decltype(std::declval<U&>().GetMany(std::declval<std::array<E, 1>&>()))>
static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }

public:

static constexpr bool value = get_value<Iterator>(0);
};

template <typename T>
struct fast_iterator
{
Expand Down Expand Up @@ -73,12 +102,12 @@ WINRT_EXPORT namespace winrt::impl

reference operator*() const
{
return m_collection->GetAt(m_index);
return fetch(m_index);
}

reference operator[](difference_type n) const
{
return m_collection->GetAt(m_index + static_cast<std::uint32_t>(n));
return fetch(m_index + static_cast<std::uint32_t>(n));
}

bool operator==(fast_iterator const& other) const noexcept
Expand Down Expand Up @@ -126,8 +155,46 @@ WINRT_EXPORT namespace winrt::impl

private:

// Batched forward traversal for random-access (GetAt-capable) collections that also expose
// GetMany. Instead of one GetAt ABI call per element, `fetch` fills a small block with a single
// GetMany call and serves in-window reads from it, so range-for (sequential ++ then *) crosses
// the ABI ~once per `block` elements. Random access still works: an out-of-window index
// re-anchors the block there, and an at/after-end index defers to GetAt so the component's
// bounds behavior (E_BOUNDS) is preserved. Elements are copied out -- no move-out -- so a given
// index may be read repeatedly, as the random-access contract requires. Collections without
// GetMany, or whose element type is not default-constructible, fall back to plain GetAt.
static constexpr bool can_batch = std::is_default_constructible_v<value_type> && has_GetMany_indexed<T>::value;
Comment thread
jonwis marked this conversation as resolved.
Outdated

static constexpr std::uint32_t block = static_cast<std::uint32_t>(
(std::min<std::size_t>)(128, (std::max<std::size_t>)(1, std::size_t{ 2048 } / sizeof(value_type))));

struct no_buffer {};
using buffer_type = std::conditional_t<can_batch, std::array<value_type, block>, no_buffer>;

reference fetch(std::uint32_t const index) const
{
if constexpr (can_batch)
{
if (index < m_buffer_base || index >= m_buffer_base + m_buffer_size)
{
m_buffer_base = index;
m_buffer_size = m_collection->GetMany(index, m_buffer);
Comment thread
jonwis marked this conversation as resolved.
Outdated
}

if (index < m_buffer_base + m_buffer_size)
{
return m_buffer[index - m_buffer_base];
}
}

return m_collection->GetAt(index);
}

T const* m_collection = nullptr;
std::uint32_t m_index = 0;
mutable std::uint32_t m_buffer_base = 0;
mutable std::uint32_t m_buffer_size = 0;
mutable buffer_type m_buffer{};
Comment thread
jonwis marked this conversation as resolved.
Outdated
};

template <typename T>
Expand All @@ -141,23 +208,115 @@ WINRT_EXPORT namespace winrt::impl
static constexpr bool value = get_value<T>(0);
};

template <typename T, std::enable_if_t<!has_GetAt<T>::value, int> = 0>
auto get_begin_iterator(T const& collection) -> decltype(collection.First())
// Forward iterator that batches an IIterator via GetMany into a small buffer and yields from
// it, so range-for over a collection that lacks GetAt crosses the ABI once per block instead of
// once per element (Current/MoveNext). Single-pass, matching IIterator's semantics. Only used
// when the iterator exposes GetMany and its element type is default-constructible.
template <typename Iterator>
struct buffered_iterator
{
auto result = collection.First();
using value_type = decltype(std::declval<Iterator>().Current());
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = value_type const*;
using reference = value_type const&;

static constexpr bool can_batch = std::is_default_constructible_v<value_type> && has_GetMany_iter<Iterator>::value;

static constexpr std::uint32_t buffer_capacity = static_cast<std::uint32_t>(
(std::min<std::size_t>)(128, (std::max<std::size_t>)(1, std::size_t{ 2048 } / sizeof(value_type))));

buffered_iterator() noexcept = default;

explicit buffered_iterator(Iterator iterator) : m_iterator(std::move(iterator))
{
fill();
}

reference operator*() const noexcept
{
return m_buffer[m_index];
}

pointer operator->() const noexcept
{
return std::addressof(m_buffer[m_index]);
}

buffered_iterator& operator++()
{
if (++m_index == m_size)
{
fill();
}

return *this;
}

buffered_iterator operator++(int)
{
auto previous = *this;
++*this;
return previous;
}

if (!result.HasCurrent())
bool operator==(buffered_iterator const& other) const noexcept
{
return {};
return (m_size == 0) && (other.m_size == 0);
Comment thread
jonwis marked this conversation as resolved.
Outdated
}

return result;
bool operator!=(buffered_iterator const& other) const noexcept
{
return !(*this == other);
}

private:

void fill()
{
m_index = 0;
m_size = m_iterator ? m_iterator.GetMany(m_buffer) : 0;
}

Iterator m_iterator{ nullptr };
std::array<value_type, buffer_capacity> m_buffer;
Comment thread
jonwis marked this conversation as resolved.
Outdated
std::uint32_t m_size{ 0 };
std::uint32_t m_index{ 0 };
};

template <typename T, std::enable_if_t<!has_GetAt<T>::value, int> = 0>
auto get_begin_iterator(T const& collection)
{
using iterator_type = decltype(collection.First());
if constexpr (buffered_iterator<iterator_type>::can_batch)
{
return buffered_iterator<iterator_type>{ collection.First() };
}
else
{
auto result = collection.First();

if (!result.HasCurrent())
{
return iterator_type{};
}

return result;
}
}

template <typename T, std::enable_if_t<!has_GetAt<T>::value, int> = 0>
auto get_end_iterator([[maybe_unused]] T const& collection) noexcept -> decltype(collection.First())
auto get_end_iterator([[maybe_unused]] T const& collection)
Comment thread
jonwis marked this conversation as resolved.
Outdated
{
return {};
using iterator_type = decltype(collection.First());
if constexpr (buffered_iterator<iterator_type>::can_batch)
{
return buffered_iterator<iterator_type>{};
}
else
{
return iterator_type{};
}
}

template <typename T, std::enable_if_t<has_GetAt<T>::value, int> = 0>
Expand Down
Loading