Skip to content

Commit

Permalink
Support for std::span for winrt::array_view and winrt::com_array (#1343)
Browse files Browse the repository at this point in the history
* Implicit conversion between std::span and winrt::array_view

* Add testing and additional ctad for spans

* PR FB - yes, yes it does!

* PR FB

---------

Co-authored-by: Jaiganésh Kumaran <[email protected]>
Co-authored-by: Jon Wiswall <[email protected]>
Co-authored-by: Kenny Kerr <[email protected]>
  • Loading branch information
4 people committed Aug 28, 2023
1 parent de6ca88 commit 691f6f8
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 0 deletions.
33 changes: 33 additions & 0 deletions strings/base_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ WINRT_EXPORT namespace winrt
array_view(value.begin(), static_cast<size_type>(value.size()))
{}

#ifdef __cpp_lib_span
template <typename C, size_t extent>
array_view(std::span<C, extent> span) noexcept :
array_view(span.data(), static_cast<size_type>(span.size()))
{
WINRT_ASSERT(span.size() <= UINT_MAX);
}

operator std::span<T>() const noexcept
{
return { m_data, m_size };
}
#endif

template <typename C, size_type N>
array_view(C(&value)[N]) noexcept :
array_view(value, N)
Expand Down Expand Up @@ -223,6 +237,11 @@ WINRT_EXPORT namespace winrt
template <typename C, size_t N> array_view(std::array<C, N>& value) -> array_view<C>;
template <typename C, size_t N> array_view(std::array<C, N> const& value) -> array_view<C const>;

#ifdef __cpp_lib_span
template <typename C, size_t extent> array_view(std::span<C, extent>& value) -> array_view<C>;
template <typename C, size_t extent> array_view(std::span<C, extent> const& value) -> array_view<C const>;
#endif

template <typename T>
struct com_array : array_view<T>
{
Expand Down Expand Up @@ -274,6 +293,15 @@ WINRT_EXPORT namespace winrt
com_array(value.begin(), value.end())
{}

#ifdef __cpp_lib_span
template <typename U, size_t extent>
explicit com_array(std::span<U, extent> span) noexcept :
com_array(span.data(), span.data() + span.size())
{
WINRT_ASSERT(span.size() <= UINT_MAX);
}
#endif

template <typename U, size_t N>
explicit com_array(U const(&value)[N]) :
com_array(value, value + N)
Expand Down Expand Up @@ -375,6 +403,11 @@ WINRT_EXPORT namespace winrt
template <size_t N, typename C> com_array(C const(&)[N]) -> com_array<std::decay_t<C>>;
template <typename C> com_array(std::initializer_list<C>) -> com_array<std::decay_t<C>>;

#ifdef __cpp_lib_span
template <typename C, size_t extent> com_array(std::span<C, extent> const& value) -> com_array<std::decay_t<C>>;
#endif


namespace impl
{
template <typename T, typename U>
Expand Down
4 changes: 4 additions & 0 deletions strings/base_includes.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
#include <ostream>
#endif

#ifdef __cpp_lib_span
#include <span>
#endif

#ifdef __cpp_lib_format
#include <format>
#endif
Expand Down
189 changes: 189 additions & 0 deletions test/test_cpp20/array_span.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#include "pch.h"
#include "catch.hpp"
#include <array>

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
using namespace Windows::Data::Json;

//
// This is a helper to create a data reader for use in testing arrays.
//
static IAsyncOperation<IDataReader> CreateDataReader(std::initializer_list<byte> values)
{
InMemoryRandomAccessStream stream;
DataWriter writer(stream);
writer.WriteByte(1);
writer.WriteByte(2);
writer.WriteByte(3);
co_await writer.StoreAsync();

stream.Seek(0);
DataReader reader(stream);
co_await reader.LoadAsync(3);
co_return reader;
}

//
// This test illustrates an array_view<T> (non-const) bound to a std::span<T> on a std::array
//
TEST_CASE("array,DataReader,std::span")
{
auto reader = CreateDataReader({ 1, 2, 3 }).get();

std::array<byte, 3> a{};
std::span<byte> sp(a);
reader.ReadBytes(sp); // FillArray pattern

REQUIRE(a.size() == 3);
REQUIRE(a[0] == 1);
REQUIRE(a[1] == 2);
REQUIRE(a[2] == 3);
}

//
// This test illustrates passing a std::array to a method that takes array_view<T>
//
TEST_CASE("array,DataReader,std::span,direct")
{
auto reader = CreateDataReader({ 1, 2, 3 }).get();

std::array<byte, 3> a{};
reader.ReadBytes(a); // FillArray pattern

REQUIRE(a.size() == 3);
REQUIRE(a[0] == 1);
REQUIRE(a[1] == 2);
REQUIRE(a[2] == 3);
}


TEST_CASE("array_view,span")
{
{
int v[] = { 1, 2, 3 };
std::span<int> s(v);
array_view<int> a = s;
REQUIRE(a.data() == v);
REQUIRE(a.size() == 3);
}

{
int v[] = { 1, 2, 3 };
std::span<int const> s(v);
array_view<int const> a = s;
REQUIRE(a.data() == v);
REQUIRE(a.size() == 3);
}

{
int const v[] = { 1, 2, 3 };
std::span<int const> s(v);
array_view<int const> a = s;
REQUIRE(a.data() == v);
REQUIRE(a.size() == 3);
}
}

//
// Tests com_array support for span construction.
//
TEST_CASE("com_array,span")
{
{
int v[] = { 1, 2, 3 };
std::span<int> s(v);
com_array<int> a(s);
REQUIRE(a.size() == 3);
REQUIRE(a[0] == 1);
REQUIRE(a[1] == 2);
REQUIRE(a[2] == 3);
}
}

//
// Tests array_view support for conversion to span
//
TEST_CASE("array_view,span,as")
{
{
int v[] = { 1, 2, 3 };
array_view<int> a = v;
std::span<int> s(a);
REQUIRE(s.data() == v);
REQUIRE(s.size() == 3);
}

{
int v[] = { 1, 2, 3 };
array_view<int const> a = v;
std::span<int const> s(a);
REQUIRE(s.data() == v);
REQUIRE(s.size() == 3);
}

{
int const v[] = { 1, 2, 3 };
array_view<int const> a = v;
std::span<int const> s(a);
REQUIRE(s.data() == v);
REQUIRE(s.size() == 3);
}
}

//
// Tests com_array support for conversion to span
//
TEST_CASE("com_array,span,as")
{
{
int v[] = { 1, 2, 3 };
com_array<int> a(v);
std::span<int> s(a);
REQUIRE(s.size() == 3);
REQUIRE(s[0] == 1);
REQUIRE(s[1] == 2);
REQUIRE(s[2] == 3);
}
}

// Verify that class template argument deduction works for array_view.
TEST_CASE("array_view,span,ctad")
{
#define REQUIRE_DEDUCED_AS(T, ...) \
static_assert(std::is_same_v<array_view<T>, decltype(array_view(__VA_ARGS__))>)

uint8_t a[] = {1, 2, 3};
std::span<uint8_t, 3> sp{ a };

REQUIRE_DEDUCED_AS(uint8_t, sp);

std::span<uint8_t const, 3> csp{ a };
REQUIRE_DEDUCED_AS(uint8_t const, csp);

std::span<uint8_t, 3> const cs{ a };
REQUIRE_DEDUCED_AS(uint8_t const, cs);

#undef REQUIRE_DEDUCED_AS
}

// Verify that class template argument deduction works for com_array.
TEST_CASE("com_array,span,ctad")
{
#define REQUIRE_DEDUCED_AS(T, ...) \
static_assert(std::is_same_v<com_array<T>, decltype(com_array(__VA_ARGS__))>)

uint8_t a[] = { 1, 2, 3 };

std::span<uint8_t, 3> sp{ a };
REQUIRE_DEDUCED_AS(uint8_t, sp);

std::span<uint8_t const, 3> csp{ a };
REQUIRE_DEDUCED_AS(uint8_t, csp);

std::span<uint8_t, 3> const cs{ a };
REQUIRE_DEDUCED_AS(uint8_t, cs);

#undef REQUIRE_DEDUCED_AS
}
1 change: 1 addition & 0 deletions test/test_cpp20/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Foundation.Collections.h"
#include "winrt/Windows.Foundation.Numerics.h"
#include "winrt/Windows.Storage.Streams.h"
#include <winstring.h>
#include "catch.hpp"

Expand Down
1 change: 1 addition & 0 deletions test/test_cpp20/test_cpp20.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="array_span.cpp" />
<ClCompile Include="await_completed.cpp" />
<ClCompile Include="custom_error.cpp" />
<ClCompile Include="format.cpp" />
Expand Down

0 comments on commit 691f6f8

Please sign in to comment.