From 08eca8bb30678e4e72d4c7693be88cd4031a1274 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Thu, 7 May 2020 12:51:00 -0700 Subject: [PATCH 001/400] utf-8 encoding of runtime names (#610) --- cppwinrt/code_writers.h | 6 +- strings/base_foundation.h | 20 +- strings/base_identity.h | 175 ++++++------------ strings/base_implements.h | 2 +- strings/base_string.h | 24 +++ strings/base_types.h | 3 + strings/base_windows.h | 4 +- strings/base_xaml_typename.h | 24 +-- test/old_tests/UnitTests/constexpr.cpp | 2 +- .../old_tests/UnitTests/string_view_compare.h | 2 +- test/old_tests/UnitTests/xaml_typename.cpp | 22 --- test/test/enum.cpp | 4 +- test/test/generic_types.h | 2 +- test/test/names.cpp | 2 +- test/test_win7/enum.cpp | 4 +- test/test_win7/generic_types.h | 2 +- test/test_win7/names.cpp | 2 +- 17 files changed, 120 insertions(+), 180 deletions(-) diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index 2d1f46590..8467b45d9 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -353,7 +353,7 @@ namespace cppwinrt } else { - w.write(R"(, L", ")"); + w.write(R"(, u8", ")"); } w.write(", name_v<%>", param.Name()); } @@ -366,14 +366,14 @@ namespace cppwinrt if (empty(generics)) { - auto format = R"( template <> inline constexpr auto& name_v<%> = L"%.%"; + auto format = R"( template <> inline constexpr auto& name_v<%> = u8"%.%"; )"; w.write(format, type, type_name.name_space, type_name.name); } else { - auto format = R"( template <%> inline constexpr auto name_v<%> = zcombine(L"%.%<"%, L">"); + auto format = R"( template <%> inline constexpr auto name_v<%> = zcombine(u8"%.%<"%, u8">"); )"; w.write(format, diff --git a/strings/base_foundation.h b/strings/base_foundation.h index ea8881edc..40a83dcf0 100644 --- a/strings/base_foundation.h +++ b/strings/base_foundation.h @@ -102,9 +102,9 @@ WINRT_EXPORT namespace winrt::Windows::Foundation namespace winrt::impl { - template <> inline constexpr auto& name_v = L"Windows.Foundation.Point"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.Size"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.Rect"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Point"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Size"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Rect"; template <> struct category { @@ -123,13 +123,13 @@ namespace winrt::impl #ifdef WINRT_IMPL_NUMERICS - template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Vector2"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Vector3"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Vector4"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Matrix3x2"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Matrix4x4"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Quaternion"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Plane"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Vector2"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Vector3"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Vector4"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Matrix3x2"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Matrix4x4"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Quaternion"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Plane"; template <> struct category { diff --git a/strings/base_identity.h b/strings/base_identity.h index 5bf7bbaa8..8981fc195 100644 --- a/strings/base_identity.h +++ b/strings/base_identity.h @@ -19,6 +19,12 @@ WINRT_EXPORT namespace winrt namespace winrt::impl { +#ifdef __cpp_char8_t + using char_type = char8_t; +#else + using char_type = char; +#endif + template constexpr std::array to_array(T const* value, std::index_sequence const) noexcept { @@ -32,7 +38,7 @@ namespace winrt::impl } template - constexpr auto to_array(char const(&value)[Size]) noexcept + constexpr auto to_array(char_type const(&value)[Size]) noexcept { return to_array(value, std::make_index_sequence()); } @@ -427,7 +433,7 @@ namespace winrt::impl } template - constexpr guid generate_guid(std::array const& value) noexcept + constexpr guid generate_guid(std::array const& value) noexcept { guid namespace_guid = { 0xd57af411, 0x737b, 0xc042,{ 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee } }; @@ -441,7 +447,7 @@ namespace winrt::impl template struct arg_collection { - constexpr static auto data{ combine(to_array(signature::data), ";", arg_collection::data) }; + constexpr static auto data{ combine(to_array(signature::data), u8";", arg_collection::data) }; }; template @@ -467,8 +473,8 @@ namespace winrt::impl { combine ( - to_array(guid_of()), - std::array{ L'\0' } + to_array(guid_of()), + std::array{ '\0' } ) }; @@ -487,98 +493,48 @@ namespace winrt::impl return 3; } - constexpr size_t to_utf8(wchar_t const value, char* buffer) noexcept - { - if (value <= 0x7F) - { - *buffer = static_cast(value); - return 1; - } - - if (value <= 0x7FF) - { - *buffer = static_cast(0xC0 | (value >> 6)); - *(buffer + 1) = 0x80 | (value & 0x3F); - return 2; - } - - *buffer = 0xE0 | (value >> 12); - *(buffer + 1) = 0x80 | ((value >> 6) & 0x3F); - *(buffer + 2) = 0x80 | (value & 0x3F); - return 3; - } - - template - constexpr size_t to_utf8_size() noexcept - { - auto input = to_array(name_v); - size_t length = 0; - - for (wchar_t const element : input) - { - length += to_utf8_size(element); - } - - return length; - } - - template - constexpr auto to_utf8() noexcept - { - auto input = to_array(name_v); - std::array()> output{}; - size_t offset{}; - - for (wchar_t const element : input) - { - offset += to_utf8(element, &output[offset]); - } - - return output; - } - template constexpr guid generic_guid_v{}; template - constexpr auto& basic_signature_v = ""; - - template <> inline constexpr auto& basic_signature_v = "b1"; - template <> inline constexpr auto& basic_signature_v = "i1"; - template <> inline constexpr auto& basic_signature_v = "i2"; - template <> inline constexpr auto& basic_signature_v = "i4"; - template <> inline constexpr auto& basic_signature_v = "i8"; - template <> inline constexpr auto& basic_signature_v = "u1"; - template <> inline constexpr auto& basic_signature_v = "u2"; - template <> inline constexpr auto& basic_signature_v = "u4"; - template <> inline constexpr auto& basic_signature_v = "u8"; - template <> inline constexpr auto& basic_signature_v = "f4"; - template <> inline constexpr auto& basic_signature_v = "f8"; - template <> inline constexpr auto& basic_signature_v = "c2"; - template <> inline constexpr auto& basic_signature_v = "g16"; - template <> inline constexpr auto& basic_signature_v = "string"; - template <> inline constexpr auto& basic_signature_v = "cinterface(IInspectable)"; - - template <> inline constexpr auto& name_v = L"Boolean"; - template <> inline constexpr auto& name_v = L"Int8"; - template <> inline constexpr auto& name_v = L"Int16"; - template <> inline constexpr auto& name_v = L"Int32"; - template <> inline constexpr auto& name_v = L"Int64"; - template <> inline constexpr auto& name_v = L"UInt8"; - template <> inline constexpr auto& name_v = L"UInt16"; - template <> inline constexpr auto& name_v = L"UInt32"; - template <> inline constexpr auto& name_v = L"UInt64"; - template <> inline constexpr auto& name_v = L"Single"; - template <> inline constexpr auto& name_v = L"Double"; - template <> inline constexpr auto& name_v = L"Char16"; - template <> inline constexpr auto& name_v = L"Guid"; - template <> inline constexpr auto& name_v = L"String"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.HResult"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.EventRegistrationToken"; - template <> inline constexpr auto& name_v = L"Object"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.TimeSpan"; - template <> inline constexpr auto& name_v = L"Windows.Foundation.DateTime"; - template <> inline constexpr auto& name_v = L"IAgileObject"; + constexpr auto& basic_signature_v = u8""; + + template <> inline constexpr auto& basic_signature_v = u8"b1"; + template <> inline constexpr auto& basic_signature_v = u8"i1"; + template <> inline constexpr auto& basic_signature_v = u8"i2"; + template <> inline constexpr auto& basic_signature_v = u8"i4"; + template <> inline constexpr auto& basic_signature_v = u8"i8"; + template <> inline constexpr auto& basic_signature_v = u8"u1"; + template <> inline constexpr auto& basic_signature_v = u8"u2"; + template <> inline constexpr auto& basic_signature_v = u8"u4"; + template <> inline constexpr auto& basic_signature_v = u8"u8"; + template <> inline constexpr auto& basic_signature_v = u8"f4"; + template <> inline constexpr auto& basic_signature_v = u8"f8"; + template <> inline constexpr auto& basic_signature_v = u8"c2"; + template <> inline constexpr auto& basic_signature_v = u8"g16"; + template <> inline constexpr auto& basic_signature_v = u8"string"; + template <> inline constexpr auto& basic_signature_v = u8"cinterface(IInspectable)"; + + template <> inline constexpr auto& name_v = u8"Boolean"; + template <> inline constexpr auto& name_v = u8"Int8"; + template <> inline constexpr auto& name_v = u8"Int16"; + template <> inline constexpr auto& name_v = u8"Int32"; + template <> inline constexpr auto& name_v = u8"Int64"; + template <> inline constexpr auto& name_v = u8"UInt8"; + template <> inline constexpr auto& name_v = u8"UInt16"; + template <> inline constexpr auto& name_v = u8"UInt32"; + template <> inline constexpr auto& name_v = u8"UInt64"; + template <> inline constexpr auto& name_v = u8"Single"; + template <> inline constexpr auto& name_v = u8"Double"; + template <> inline constexpr auto& name_v = u8"Char16"; + template <> inline constexpr auto& name_v = u8"Guid"; + template <> inline constexpr auto& name_v = u8"String"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.HResult"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.EventRegistrationToken"; + template <> inline constexpr auto& name_v = u8"Object"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.TimeSpan"; + template <> inline constexpr auto& name_v = u8"Windows.Foundation.DateTime"; + template <> inline constexpr auto& name_v = u8"IAgileObject"; template <> struct category { using type = basic_category; }; template <> struct category { using type = basic_category; }; @@ -609,57 +565,36 @@ namespace winrt::impl struct category_signature { using enum_type = std::underlying_type_t; - constexpr static auto data{ combine("enum(", to_utf8(), ";", signature::data, ")") }; + constexpr static auto data{ combine(u8"enum(", name_v, u8";", signature::data, u8")") }; }; template struct category_signature, T> { - constexpr static auto data{ combine("struct(", to_utf8(), ";", arg_collection::data, ")") }; + constexpr static auto data{ combine(u8"struct(", name_v, u8";", arg_collection::data, u8")") }; }; template struct category_signature { - constexpr static auto data{ combine("rc(", to_utf8(), ";", signature>::data, ")") }; + constexpr static auto data{ combine(u8"rc(", name_v, u8";", signature>::data, u8")") }; }; template struct category_signature, T> { - constexpr static auto data{ combine("pinterface(", to_array(generic_guid_v), ";", arg_collection::data, ")") }; + constexpr static auto data{ combine(u8"pinterface(", to_array(generic_guid_v), u8";", arg_collection::data, u8")") }; }; template struct category_signature { - constexpr static auto data{ to_array(guid_of()) }; + constexpr static auto data{ to_array(guid_of()) }; }; template struct category_signature { - constexpr static auto data{ combine("delegate(", to_array(guid_of()), ")") }; + constexpr static auto data{ combine(u8"delegate(", to_array(guid_of()), u8")") }; }; - - template - constexpr std::wstring_view to_wstring_view(std::array const& value) noexcept - { - return { value.data(), Size - 1 }; - } - - template - constexpr std::wstring_view to_wstring_view(wchar_t const (&value)[Size]) noexcept - { - return { value, Size - 1 }; - } -} - -WINRT_EXPORT namespace winrt -{ - template - constexpr auto name_of() noexcept - { - return impl::to_wstring_view(impl::name_v); - } } diff --git a/strings/base_implements.h b/strings/base_implements.h index 2457b80b4..26eee9e60 100644 --- a/strings/base_implements.h +++ b/strings/base_implements.h @@ -419,7 +419,7 @@ namespace winrt::impl { static hstring get() { - return hstring{ name_of() }; + return name_of(); } }; diff --git a/strings/base_string.h b/strings/base_string.h index 7423a498d..c8f8ecae4 100644 --- a/strings/base_string.h +++ b/strings/base_string.h @@ -638,3 +638,27 @@ WINRT_EXPORT namespace winrt return result; } } + +namespace winrt::impl +{ + template + hstring literal_to_hstring(std::array const& value) noexcept + { + return to_hstring(std::string_view(reinterpret_cast(value.data()), Size - 1)); + } + + template + hstring literal_to_hstring(char_type const (&value)[Size]) noexcept + { + return to_hstring(std::string_view(reinterpret_cast(value), Size - 1)); + } +} + +WINRT_EXPORT namespace winrt +{ + template + hstring name_of() noexcept + { + return impl::literal_to_hstring(impl::name_v); + } +} diff --git a/strings/base_types.h b/strings/base_types.h index 0eadc03cc..531d9b492 100644 --- a/strings/base_types.h +++ b/strings/base_types.h @@ -26,6 +26,9 @@ WINRT_EXPORT namespace winrt struct hstring; struct clock; + template + hstring name_of() noexcept; + struct hresult { int32_t value{}; diff --git a/strings/base_windows.h b/strings/base_windows.h index 76cf29fae..7c2a4d501 100644 --- a/strings/base_windows.h +++ b/strings/base_windows.h @@ -11,8 +11,8 @@ namespace winrt::impl struct diagnostics_info { - std::map queries; - std::map factories; + std::map queries; + std::map factories; }; struct diagnostics_cache diff --git a/strings/base_xaml_typename.h b/strings/base_xaml_typename.h index 4912649a2..05f85c7fb 100644 --- a/strings/base_xaml_typename.h +++ b/strings/base_xaml_typename.h @@ -4,7 +4,7 @@ namespace winrt::impl template struct xaml_typename_name { - static constexpr std::wstring_view value() noexcept + static hstring value() noexcept { return name_of(); } @@ -12,41 +12,41 @@ namespace winrt::impl template <> struct xaml_typename_name { - static constexpr std::wstring_view value() noexcept + static hstring value() noexcept { - return L"Point"sv; + return L"Point"; } }; template <> struct xaml_typename_name { - static constexpr std::wstring_view value() noexcept + static hstring value() noexcept { - return L"Size"sv; + return L"Size"; } }; template <> struct xaml_typename_name { - static constexpr std::wstring_view value() noexcept + static hstring value() noexcept { - return L"Rect"sv; + return L"Rect"; } }; template <> struct xaml_typename_name { - static constexpr std::wstring_view value() noexcept + static hstring value() noexcept { - return L"DateTime"sv; + return L"DateTime"; } }; template <> struct xaml_typename_name { - static constexpr std::wstring_view value() noexcept + static hstring value() noexcept { - return L"TimeSpan"sv; + return L"TimeSpan"; } }; @@ -133,7 +133,7 @@ WINRT_EXPORT namespace winrt inline Windows::UI::Xaml::Interop::TypeName xaml_typename() { static_assert(impl::has_category_v, "T must be WinRT type."); - static const Windows::UI::Xaml::Interop::TypeName name{ hstring{ impl::xaml_typename_name::value() }, impl::xaml_typename_kind::value }; + static const Windows::UI::Xaml::Interop::TypeName name{ impl::xaml_typename_name::value(), impl::xaml_typename_kind::value }; return name; } } diff --git a/test/old_tests/UnitTests/constexpr.cpp b/test/old_tests/UnitTests/constexpr.cpp index 22478af2f..95d38ad43 100644 --- a/test/old_tests/UnitTests/constexpr.cpp +++ b/test/old_tests/UnitTests/constexpr.cpp @@ -38,7 +38,7 @@ TEST_CASE("constexpr") REQUIRE(winrt::name_of() == midl_container::z_get_rc_name_impl()); - constexpr auto name = winrt::name_of(); + auto name = winrt::name_of(); REQUIRE(name == L"Guid"); check_terminated(winrt::name_of()); diff --git a/test/old_tests/UnitTests/string_view_compare.h b/test/old_tests/UnitTests/string_view_compare.h index 83c1f7a16..99e8d7612 100644 --- a/test/old_tests/UnitTests/string_view_compare.h +++ b/test/old_tests/UnitTests/string_view_compare.h @@ -3,7 +3,7 @@ #include template -inline constexpr bool string_view_equal(std::basic_string_view left, std::basic_string_view right) noexcept +bool string_view_equal(std::basic_string_view left, std::basic_string_view right) noexcept { if (left.size() != right.size()) { diff --git a/test/old_tests/UnitTests/xaml_typename.cpp b/test/old_tests/UnitTests/xaml_typename.cpp index 0aaac6023..253ccdad0 100644 --- a/test/old_tests/UnitTests/xaml_typename.cpp +++ b/test/old_tests/UnitTests/xaml_typename.cpp @@ -7,27 +7,6 @@ using namespace winrt; using namespace Windows::UI::Xaml::Interop; using namespace std::string_view_literals; -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Boolean"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Char16"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"UInt8"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Int8"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"UInt16"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Int16"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"UInt32"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Int32"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"UInt64"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Int64"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Single"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Double"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"String"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Guid"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"DateTime"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"TimeSpan"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Point"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Size"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Rect"sv)); -static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Object"sv)); - static_assert(impl::xaml_typename_kind::value == Windows::UI::Xaml::Interop::TypeKind::Primitive); static_assert(impl::xaml_typename_kind::value == Windows::UI::Xaml::Interop::TypeKind::Primitive); static_assert(impl::xaml_typename_kind::value == Windows::UI::Xaml::Interop::TypeKind::Primitive); @@ -59,7 +38,6 @@ namespace TEST_CASE("xaml_typename") { - // We've static asserted all the impl values, but let's verify that the public runtime API behaves as expected REQUIRE(equals(TypeName{ hstring(L"Boolean"sv), TypeKind::Primitive }, xaml_typename())); REQUIRE(equals(TypeName{ hstring(L"Char16"sv), TypeKind::Primitive }, xaml_typename())); REQUIRE(equals(TypeName{ hstring(L"UInt8"sv), TypeKind::Primitive }, xaml_typename())); diff --git a/test/test/enum.cpp b/test/test/enum.cpp index 48f356a75..8dbff04f4 100644 --- a/test/test/enum.cpp +++ b/test/test/enum.cpp @@ -9,8 +9,8 @@ TEST_CASE("enum") STATIC_REQUIRE(std::is_same_v, int32_t>); STATIC_REQUIRE(std::is_same_v, uint32_t>); - STATIC_REQUIRE(name_of() == L"test_component.Signed"sv); - STATIC_REQUIRE(name_of() == L"test_component.Unsigned"sv); + REQUIRE(name_of() == L"test_component.Signed"sv); + REQUIRE(name_of() == L"test_component.Unsigned"sv); REQUIRE(((Unsigned::First | Unsigned::Second | Unsigned::Third) & Unsigned::Second) == Unsigned::Second); diff --git a/test/test/generic_types.h b/test/test/generic_types.h index c305cd734..5ead4b880 100644 --- a/test/test/generic_types.h +++ b/test/test/generic_types.h @@ -7,7 +7,7 @@ using namespace Windows::Foundation::Numerics; using namespace std::literals; #define REQUIRE_EQUAL_GUID(left, ...) STATIC_REQUIRE(equal(make_guid(left), guid_of<__VA_ARGS__>())); -#define REQUIRE_EQUAL_NAME(left, ...) STATIC_REQUIRE(left == name_of<__VA_ARGS__>()); +#define REQUIRE_EQUAL_NAME(left, ...) REQUIRE(left == name_of<__VA_ARGS__>()); namespace { diff --git a/test/test/names.cpp b/test/test/names.cpp index 7730fe82e..13352dcd2 100644 --- a/test/test/names.cpp +++ b/test/test/names.cpp @@ -10,7 +10,7 @@ void check_terminated(winrt::param::hstring const&) TEST_CASE("names") { REQUIRE(name_of() == L"{00000000-0000-0000-c000-000000000046}"sv); - STATIC_REQUIRE(name_of() == L"Object"sv); + REQUIRE(name_of() == L"Object"sv); check_terminated(name_of()); check_terminated(name_of()); diff --git a/test/test_win7/enum.cpp b/test/test_win7/enum.cpp index 48f356a75..8dbff04f4 100644 --- a/test/test_win7/enum.cpp +++ b/test/test_win7/enum.cpp @@ -9,8 +9,8 @@ TEST_CASE("enum") STATIC_REQUIRE(std::is_same_v, int32_t>); STATIC_REQUIRE(std::is_same_v, uint32_t>); - STATIC_REQUIRE(name_of() == L"test_component.Signed"sv); - STATIC_REQUIRE(name_of() == L"test_component.Unsigned"sv); + REQUIRE(name_of() == L"test_component.Signed"sv); + REQUIRE(name_of() == L"test_component.Unsigned"sv); REQUIRE(((Unsigned::First | Unsigned::Second | Unsigned::Third) & Unsigned::Second) == Unsigned::Second); diff --git a/test/test_win7/generic_types.h b/test/test_win7/generic_types.h index c305cd734..5ead4b880 100644 --- a/test/test_win7/generic_types.h +++ b/test/test_win7/generic_types.h @@ -7,7 +7,7 @@ using namespace Windows::Foundation::Numerics; using namespace std::literals; #define REQUIRE_EQUAL_GUID(left, ...) STATIC_REQUIRE(equal(make_guid(left), guid_of<__VA_ARGS__>())); -#define REQUIRE_EQUAL_NAME(left, ...) STATIC_REQUIRE(left == name_of<__VA_ARGS__>()); +#define REQUIRE_EQUAL_NAME(left, ...) REQUIRE(left == name_of<__VA_ARGS__>()); namespace { diff --git a/test/test_win7/names.cpp b/test/test_win7/names.cpp index 7730fe82e..13352dcd2 100644 --- a/test/test_win7/names.cpp +++ b/test/test_win7/names.cpp @@ -10,7 +10,7 @@ void check_terminated(winrt::param::hstring const&) TEST_CASE("names") { REQUIRE(name_of() == L"{00000000-0000-0000-c000-000000000046}"sv); - STATIC_REQUIRE(name_of() == L"Object"sv); + REQUIRE(name_of() == L"Object"sv); check_terminated(name_of()); check_terminated(name_of()); From 9db3f9d7c7698e1a4262600e61b8aba9bd8dcce3 Mon Sep 17 00:00:00 2001 From: Scott Jones Date: Fri, 8 May 2020 16:29:02 -0700 Subject: [PATCH 002/400] A few minor tweaks to make compatible with build systems (#613) --- cppwinrt/cmd_reader.h | 2 +- cppwinrt/main.cpp | 5 +++-- cppwinrt/text_writer.h | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cppwinrt/cmd_reader.h b/cppwinrt/cmd_reader.h index b955d1e34..3dee748a3 100644 --- a/cppwinrt/cmd_reader.h +++ b/cppwinrt/cmd_reader.h @@ -515,7 +515,7 @@ namespace cppwinrt template void extract_option(std::string_view arg, O const& options, L& last) { - if (arg[0] == '-') + if (arg[0] == '-' || arg[0] == '/') { arg.remove_prefix(1); last = find(options, arg); diff --git a/cppwinrt/main.cpp b/cppwinrt/main.cpp index 9c26dfc16..9a68368d0 100644 --- a/cppwinrt/main.cpp +++ b/cppwinrt/main.cpp @@ -31,6 +31,7 @@ namespace cppwinrt { "base", 0, 0, {}, "Generate base.h unconditionally" }, { "optimize", 0, 0, {}, "Generate component projection with unified construction support" }, { "help", 0, option::no_max, {}, "Show detailed help with examples" }, + { "?", 0, option::no_max, {}, {} }, { "library", 0, 1, "", "Specify library prefix (defaults to winrt)" }, { "filter" }, // One or more prefixes to include in input (same as -include) { "license", 0, 0 }, // Generate license comment @@ -252,7 +253,7 @@ Where is one or more of: reader args{ argc, argv, options }; - if (!args || args.exists("help")) + if (!args || args.exists("help") || args.exists("?")) { throw usage_exception{}; } @@ -366,7 +367,7 @@ Where is one or more of: result = 1; } - w.flush_to_console(); + w.flush_to_console(result == 0); return result; } } diff --git a/cppwinrt/text_writer.h b/cppwinrt/text_writer.h index f778bc220..5045b9395 100644 --- a/cppwinrt/text_writer.h +++ b/cppwinrt/text_writer.h @@ -145,10 +145,10 @@ namespace cppwinrt std::swap(m_second, m_first); } - void flush_to_console() noexcept + void flush_to_console(bool to_stdout = true) noexcept { - printf("%.*s", static_cast(m_first.size()), m_first.data()); - printf("%.*s", static_cast(m_second.size()), m_second.data()); + fprintf(to_stdout ? stdout : stderr, "%.*s", static_cast(m_first.size()), m_first.data()); + fprintf(to_stdout ? stdout : stderr, "%.*s", static_cast(m_second.size()), m_second.data()); m_first.clear(); m_second.clear(); } From b95c4f330b14460614a6d0130cc2348aab6790ce Mon Sep 17 00:00:00 2001 From: Scott Jones Date: Mon, 11 May 2020 09:36:43 -0700 Subject: [PATCH 003/400] Use canonical error message format for standard build tool parsing (#615) --- cppwinrt/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cppwinrt/main.cpp b/cppwinrt/main.cpp index 9a68368d0..b46e08f58 100644 --- a/cppwinrt/main.cpp +++ b/cppwinrt/main.cpp @@ -363,7 +363,7 @@ Where is one or more of: } catch (std::exception const& e) { - w.write(" error: %\n", e.what()); + w.write("cppwinrt : error %\n", e.what()); result = 1; } From ede55c09b77f8a8a89fe449de51886a528e32ab2 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 12 May 2020 08:59:03 -0700 Subject: [PATCH 004/400] compact (#617) Co-authored-by: Kenny Kerr --- cppwinrt/component_writers.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/cppwinrt/component_writers.h b/cppwinrt/component_writers.h index ab48276f9..df1fcc3b4 100644 --- a/cppwinrt/component_writers.h +++ b/cppwinrt/component_writers.h @@ -106,7 +106,7 @@ namespace cppwinrt if (settings.component_opt) { auto format = R"( - if (requal(name, L"%.%")) + if (requal(name, "%.%")) { return winrt_make_%(); } @@ -120,7 +120,7 @@ namespace cppwinrt else { auto format = R"( - if (requal(name, L"%.%")) + if (requal(name, "%.%")) { return winrt::detach_abi(winrt::make()); } @@ -149,9 +149,11 @@ bool __stdcall %_can_unload_now() noexcept return true; } -void* __stdcall %_get_activation_factory([[maybe_unused]] std::wstring_view const& name) +void* __stdcall %_get_activation_factory([[maybe_unused]] std::wstring_view const& wname) { - auto requal = [](std::wstring_view const& left, std::wstring_view const& right) noexcept + auto name = winrt::to_string(wname); + + auto requal = [](std::string_view const& left, std::string_view const& right) noexcept { return std::equal(left.rbegin(), left.rend(), right.rbegin(), right.rend()); }; @@ -750,7 +752,7 @@ catch (...) { return winrt::to_hresult(); } % hstring GetRuntimeClassName() const { - return L"%.%"; + return name_of(); } %%%% }; } @@ -820,8 +822,6 @@ catch (...) { return winrt::to_hresult(); } type_name, type_name, composable_base_name, - type_namespace, - type_name, bind(type), bind(type), bind(type), @@ -839,7 +839,7 @@ catch (...) { return winrt::to_hresult(); } hstring GetRuntimeClassName() const { - return L"%.%"; + return name_of(); } % }; } @@ -851,8 +851,6 @@ catch (...) { return winrt::to_hresult(); } bind(factories), type_namespace, type_name, - type_namespace, - type_name, bind(factories)); } From 6f1c52ca12e05d71b0c95e03e5dd4e08dd60b56d Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Wed, 13 May 2020 14:18:42 -0700 Subject: [PATCH 005/400] Revert utf-8 encoding of runtime class names (#621) --- cppwinrt/code_writers.h | 6 +- cppwinrt/component_writers.h | 18 +- strings/base_foundation.h | 20 +- strings/base_identity.h | 175 ++++++++++++------ strings/base_implements.h | 2 +- strings/base_string.h | 24 --- strings/base_types.h | 3 - strings/base_windows.h | 4 +- strings/base_xaml_typename.h | 24 +-- test/old_tests/UnitTests/constexpr.cpp | 2 +- .../old_tests/UnitTests/string_view_compare.h | 2 +- test/old_tests/UnitTests/xaml_typename.cpp | 22 +++ test/test/enum.cpp | 4 +- test/test/generic_types.h | 2 +- test/test/names.cpp | 2 +- test/test_win7/enum.cpp | 4 +- test/test_win7/generic_types.h | 2 +- test/test_win7/names.cpp | 2 +- 18 files changed, 190 insertions(+), 128 deletions(-) diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index 8467b45d9..2d1f46590 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -353,7 +353,7 @@ namespace cppwinrt } else { - w.write(R"(, u8", ")"); + w.write(R"(, L", ")"); } w.write(", name_v<%>", param.Name()); } @@ -366,14 +366,14 @@ namespace cppwinrt if (empty(generics)) { - auto format = R"( template <> inline constexpr auto& name_v<%> = u8"%.%"; + auto format = R"( template <> inline constexpr auto& name_v<%> = L"%.%"; )"; w.write(format, type, type_name.name_space, type_name.name); } else { - auto format = R"( template <%> inline constexpr auto name_v<%> = zcombine(u8"%.%<"%, u8">"); + auto format = R"( template <%> inline constexpr auto name_v<%> = zcombine(L"%.%<"%, L">"); )"; w.write(format, diff --git a/cppwinrt/component_writers.h b/cppwinrt/component_writers.h index df1fcc3b4..ab48276f9 100644 --- a/cppwinrt/component_writers.h +++ b/cppwinrt/component_writers.h @@ -106,7 +106,7 @@ namespace cppwinrt if (settings.component_opt) { auto format = R"( - if (requal(name, "%.%")) + if (requal(name, L"%.%")) { return winrt_make_%(); } @@ -120,7 +120,7 @@ namespace cppwinrt else { auto format = R"( - if (requal(name, "%.%")) + if (requal(name, L"%.%")) { return winrt::detach_abi(winrt::make()); } @@ -149,11 +149,9 @@ bool __stdcall %_can_unload_now() noexcept return true; } -void* __stdcall %_get_activation_factory([[maybe_unused]] std::wstring_view const& wname) +void* __stdcall %_get_activation_factory([[maybe_unused]] std::wstring_view const& name) { - auto name = winrt::to_string(wname); - - auto requal = [](std::string_view const& left, std::string_view const& right) noexcept + auto requal = [](std::wstring_view const& left, std::wstring_view const& right) noexcept { return std::equal(left.rbegin(), left.rend(), right.rbegin(), right.rend()); }; @@ -752,7 +750,7 @@ catch (...) { return winrt::to_hresult(); } % hstring GetRuntimeClassName() const { - return name_of(); + return L"%.%"; } %%%% }; } @@ -822,6 +820,8 @@ catch (...) { return winrt::to_hresult(); } type_name, type_name, composable_base_name, + type_namespace, + type_name, bind(type), bind(type), bind(type), @@ -839,7 +839,7 @@ catch (...) { return winrt::to_hresult(); } hstring GetRuntimeClassName() const { - return name_of(); + return L"%.%"; } % }; } @@ -851,6 +851,8 @@ catch (...) { return winrt::to_hresult(); } bind(factories), type_namespace, type_name, + type_namespace, + type_name, bind(factories)); } diff --git a/strings/base_foundation.h b/strings/base_foundation.h index 40a83dcf0..ea8881edc 100644 --- a/strings/base_foundation.h +++ b/strings/base_foundation.h @@ -102,9 +102,9 @@ WINRT_EXPORT namespace winrt::Windows::Foundation namespace winrt::impl { - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Point"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Size"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Rect"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Point"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Size"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Rect"; template <> struct category { @@ -123,13 +123,13 @@ namespace winrt::impl #ifdef WINRT_IMPL_NUMERICS - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Vector2"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Vector3"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Vector4"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Matrix3x2"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Matrix4x4"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Quaternion"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.Numerics.Plane"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Vector2"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Vector3"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Vector4"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Matrix3x2"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Matrix4x4"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Quaternion"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.Numerics.Plane"; template <> struct category { diff --git a/strings/base_identity.h b/strings/base_identity.h index 8981fc195..5bf7bbaa8 100644 --- a/strings/base_identity.h +++ b/strings/base_identity.h @@ -19,12 +19,6 @@ WINRT_EXPORT namespace winrt namespace winrt::impl { -#ifdef __cpp_char8_t - using char_type = char8_t; -#else - using char_type = char; -#endif - template constexpr std::array to_array(T const* value, std::index_sequence const) noexcept { @@ -38,7 +32,7 @@ namespace winrt::impl } template - constexpr auto to_array(char_type const(&value)[Size]) noexcept + constexpr auto to_array(char const(&value)[Size]) noexcept { return to_array(value, std::make_index_sequence()); } @@ -433,7 +427,7 @@ namespace winrt::impl } template - constexpr guid generate_guid(std::array const& value) noexcept + constexpr guid generate_guid(std::array const& value) noexcept { guid namespace_guid = { 0xd57af411, 0x737b, 0xc042,{ 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee } }; @@ -447,7 +441,7 @@ namespace winrt::impl template struct arg_collection { - constexpr static auto data{ combine(to_array(signature::data), u8";", arg_collection::data) }; + constexpr static auto data{ combine(to_array(signature::data), ";", arg_collection::data) }; }; template @@ -473,8 +467,8 @@ namespace winrt::impl { combine ( - to_array(guid_of()), - std::array{ '\0' } + to_array(guid_of()), + std::array{ L'\0' } ) }; @@ -493,48 +487,98 @@ namespace winrt::impl return 3; } + constexpr size_t to_utf8(wchar_t const value, char* buffer) noexcept + { + if (value <= 0x7F) + { + *buffer = static_cast(value); + return 1; + } + + if (value <= 0x7FF) + { + *buffer = static_cast(0xC0 | (value >> 6)); + *(buffer + 1) = 0x80 | (value & 0x3F); + return 2; + } + + *buffer = 0xE0 | (value >> 12); + *(buffer + 1) = 0x80 | ((value >> 6) & 0x3F); + *(buffer + 2) = 0x80 | (value & 0x3F); + return 3; + } + + template + constexpr size_t to_utf8_size() noexcept + { + auto input = to_array(name_v); + size_t length = 0; + + for (wchar_t const element : input) + { + length += to_utf8_size(element); + } + + return length; + } + + template + constexpr auto to_utf8() noexcept + { + auto input = to_array(name_v); + std::array()> output{}; + size_t offset{}; + + for (wchar_t const element : input) + { + offset += to_utf8(element, &output[offset]); + } + + return output; + } + template constexpr guid generic_guid_v{}; template - constexpr auto& basic_signature_v = u8""; - - template <> inline constexpr auto& basic_signature_v = u8"b1"; - template <> inline constexpr auto& basic_signature_v = u8"i1"; - template <> inline constexpr auto& basic_signature_v = u8"i2"; - template <> inline constexpr auto& basic_signature_v = u8"i4"; - template <> inline constexpr auto& basic_signature_v = u8"i8"; - template <> inline constexpr auto& basic_signature_v = u8"u1"; - template <> inline constexpr auto& basic_signature_v = u8"u2"; - template <> inline constexpr auto& basic_signature_v = u8"u4"; - template <> inline constexpr auto& basic_signature_v = u8"u8"; - template <> inline constexpr auto& basic_signature_v = u8"f4"; - template <> inline constexpr auto& basic_signature_v = u8"f8"; - template <> inline constexpr auto& basic_signature_v = u8"c2"; - template <> inline constexpr auto& basic_signature_v = u8"g16"; - template <> inline constexpr auto& basic_signature_v = u8"string"; - template <> inline constexpr auto& basic_signature_v = u8"cinterface(IInspectable)"; - - template <> inline constexpr auto& name_v = u8"Boolean"; - template <> inline constexpr auto& name_v = u8"Int8"; - template <> inline constexpr auto& name_v = u8"Int16"; - template <> inline constexpr auto& name_v = u8"Int32"; - template <> inline constexpr auto& name_v = u8"Int64"; - template <> inline constexpr auto& name_v = u8"UInt8"; - template <> inline constexpr auto& name_v = u8"UInt16"; - template <> inline constexpr auto& name_v = u8"UInt32"; - template <> inline constexpr auto& name_v = u8"UInt64"; - template <> inline constexpr auto& name_v = u8"Single"; - template <> inline constexpr auto& name_v = u8"Double"; - template <> inline constexpr auto& name_v = u8"Char16"; - template <> inline constexpr auto& name_v = u8"Guid"; - template <> inline constexpr auto& name_v = u8"String"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.HResult"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.EventRegistrationToken"; - template <> inline constexpr auto& name_v = u8"Object"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.TimeSpan"; - template <> inline constexpr auto& name_v = u8"Windows.Foundation.DateTime"; - template <> inline constexpr auto& name_v = u8"IAgileObject"; + constexpr auto& basic_signature_v = ""; + + template <> inline constexpr auto& basic_signature_v = "b1"; + template <> inline constexpr auto& basic_signature_v = "i1"; + template <> inline constexpr auto& basic_signature_v = "i2"; + template <> inline constexpr auto& basic_signature_v = "i4"; + template <> inline constexpr auto& basic_signature_v = "i8"; + template <> inline constexpr auto& basic_signature_v = "u1"; + template <> inline constexpr auto& basic_signature_v = "u2"; + template <> inline constexpr auto& basic_signature_v = "u4"; + template <> inline constexpr auto& basic_signature_v = "u8"; + template <> inline constexpr auto& basic_signature_v = "f4"; + template <> inline constexpr auto& basic_signature_v = "f8"; + template <> inline constexpr auto& basic_signature_v = "c2"; + template <> inline constexpr auto& basic_signature_v = "g16"; + template <> inline constexpr auto& basic_signature_v = "string"; + template <> inline constexpr auto& basic_signature_v = "cinterface(IInspectable)"; + + template <> inline constexpr auto& name_v = L"Boolean"; + template <> inline constexpr auto& name_v = L"Int8"; + template <> inline constexpr auto& name_v = L"Int16"; + template <> inline constexpr auto& name_v = L"Int32"; + template <> inline constexpr auto& name_v = L"Int64"; + template <> inline constexpr auto& name_v = L"UInt8"; + template <> inline constexpr auto& name_v = L"UInt16"; + template <> inline constexpr auto& name_v = L"UInt32"; + template <> inline constexpr auto& name_v = L"UInt64"; + template <> inline constexpr auto& name_v = L"Single"; + template <> inline constexpr auto& name_v = L"Double"; + template <> inline constexpr auto& name_v = L"Char16"; + template <> inline constexpr auto& name_v = L"Guid"; + template <> inline constexpr auto& name_v = L"String"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.HResult"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.EventRegistrationToken"; + template <> inline constexpr auto& name_v = L"Object"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.TimeSpan"; + template <> inline constexpr auto& name_v = L"Windows.Foundation.DateTime"; + template <> inline constexpr auto& name_v = L"IAgileObject"; template <> struct category { using type = basic_category; }; template <> struct category { using type = basic_category; }; @@ -565,36 +609,57 @@ namespace winrt::impl struct category_signature { using enum_type = std::underlying_type_t; - constexpr static auto data{ combine(u8"enum(", name_v, u8";", signature::data, u8")") }; + constexpr static auto data{ combine("enum(", to_utf8(), ";", signature::data, ")") }; }; template struct category_signature, T> { - constexpr static auto data{ combine(u8"struct(", name_v, u8";", arg_collection::data, u8")") }; + constexpr static auto data{ combine("struct(", to_utf8(), ";", arg_collection::data, ")") }; }; template struct category_signature { - constexpr static auto data{ combine(u8"rc(", name_v, u8";", signature>::data, u8")") }; + constexpr static auto data{ combine("rc(", to_utf8(), ";", signature>::data, ")") }; }; template struct category_signature, T> { - constexpr static auto data{ combine(u8"pinterface(", to_array(generic_guid_v), u8";", arg_collection::data, u8")") }; + constexpr static auto data{ combine("pinterface(", to_array(generic_guid_v), ";", arg_collection::data, ")") }; }; template struct category_signature { - constexpr static auto data{ to_array(guid_of()) }; + constexpr static auto data{ to_array(guid_of()) }; }; template struct category_signature { - constexpr static auto data{ combine(u8"delegate(", to_array(guid_of()), u8")") }; + constexpr static auto data{ combine("delegate(", to_array(guid_of()), ")") }; }; + + template + constexpr std::wstring_view to_wstring_view(std::array const& value) noexcept + { + return { value.data(), Size - 1 }; + } + + template + constexpr std::wstring_view to_wstring_view(wchar_t const (&value)[Size]) noexcept + { + return { value, Size - 1 }; + } +} + +WINRT_EXPORT namespace winrt +{ + template + constexpr auto name_of() noexcept + { + return impl::to_wstring_view(impl::name_v); + } } diff --git a/strings/base_implements.h b/strings/base_implements.h index 26eee9e60..2457b80b4 100644 --- a/strings/base_implements.h +++ b/strings/base_implements.h @@ -419,7 +419,7 @@ namespace winrt::impl { static hstring get() { - return name_of(); + return hstring{ name_of() }; } }; diff --git a/strings/base_string.h b/strings/base_string.h index c8f8ecae4..7423a498d 100644 --- a/strings/base_string.h +++ b/strings/base_string.h @@ -638,27 +638,3 @@ WINRT_EXPORT namespace winrt return result; } } - -namespace winrt::impl -{ - template - hstring literal_to_hstring(std::array const& value) noexcept - { - return to_hstring(std::string_view(reinterpret_cast(value.data()), Size - 1)); - } - - template - hstring literal_to_hstring(char_type const (&value)[Size]) noexcept - { - return to_hstring(std::string_view(reinterpret_cast(value), Size - 1)); - } -} - -WINRT_EXPORT namespace winrt -{ - template - hstring name_of() noexcept - { - return impl::literal_to_hstring(impl::name_v); - } -} diff --git a/strings/base_types.h b/strings/base_types.h index 531d9b492..0eadc03cc 100644 --- a/strings/base_types.h +++ b/strings/base_types.h @@ -26,9 +26,6 @@ WINRT_EXPORT namespace winrt struct hstring; struct clock; - template - hstring name_of() noexcept; - struct hresult { int32_t value{}; diff --git a/strings/base_windows.h b/strings/base_windows.h index 7c2a4d501..76cf29fae 100644 --- a/strings/base_windows.h +++ b/strings/base_windows.h @@ -11,8 +11,8 @@ namespace winrt::impl struct diagnostics_info { - std::map queries; - std::map factories; + std::map queries; + std::map factories; }; struct diagnostics_cache diff --git a/strings/base_xaml_typename.h b/strings/base_xaml_typename.h index 05f85c7fb..4912649a2 100644 --- a/strings/base_xaml_typename.h +++ b/strings/base_xaml_typename.h @@ -4,7 +4,7 @@ namespace winrt::impl template struct xaml_typename_name { - static hstring value() noexcept + static constexpr std::wstring_view value() noexcept { return name_of(); } @@ -12,41 +12,41 @@ namespace winrt::impl template <> struct xaml_typename_name { - static hstring value() noexcept + static constexpr std::wstring_view value() noexcept { - return L"Point"; + return L"Point"sv; } }; template <> struct xaml_typename_name { - static hstring value() noexcept + static constexpr std::wstring_view value() noexcept { - return L"Size"; + return L"Size"sv; } }; template <> struct xaml_typename_name { - static hstring value() noexcept + static constexpr std::wstring_view value() noexcept { - return L"Rect"; + return L"Rect"sv; } }; template <> struct xaml_typename_name { - static hstring value() noexcept + static constexpr std::wstring_view value() noexcept { - return L"DateTime"; + return L"DateTime"sv; } }; template <> struct xaml_typename_name { - static hstring value() noexcept + static constexpr std::wstring_view value() noexcept { - return L"TimeSpan"; + return L"TimeSpan"sv; } }; @@ -133,7 +133,7 @@ WINRT_EXPORT namespace winrt inline Windows::UI::Xaml::Interop::TypeName xaml_typename() { static_assert(impl::has_category_v, "T must be WinRT type."); - static const Windows::UI::Xaml::Interop::TypeName name{ impl::xaml_typename_name::value(), impl::xaml_typename_kind::value }; + static const Windows::UI::Xaml::Interop::TypeName name{ hstring{ impl::xaml_typename_name::value() }, impl::xaml_typename_kind::value }; return name; } } diff --git a/test/old_tests/UnitTests/constexpr.cpp b/test/old_tests/UnitTests/constexpr.cpp index 95d38ad43..22478af2f 100644 --- a/test/old_tests/UnitTests/constexpr.cpp +++ b/test/old_tests/UnitTests/constexpr.cpp @@ -38,7 +38,7 @@ TEST_CASE("constexpr") REQUIRE(winrt::name_of() == midl_container::z_get_rc_name_impl()); - auto name = winrt::name_of(); + constexpr auto name = winrt::name_of(); REQUIRE(name == L"Guid"); check_terminated(winrt::name_of()); diff --git a/test/old_tests/UnitTests/string_view_compare.h b/test/old_tests/UnitTests/string_view_compare.h index 99e8d7612..83c1f7a16 100644 --- a/test/old_tests/UnitTests/string_view_compare.h +++ b/test/old_tests/UnitTests/string_view_compare.h @@ -3,7 +3,7 @@ #include template -bool string_view_equal(std::basic_string_view left, std::basic_string_view right) noexcept +inline constexpr bool string_view_equal(std::basic_string_view left, std::basic_string_view right) noexcept { if (left.size() != right.size()) { diff --git a/test/old_tests/UnitTests/xaml_typename.cpp b/test/old_tests/UnitTests/xaml_typename.cpp index 253ccdad0..0aaac6023 100644 --- a/test/old_tests/UnitTests/xaml_typename.cpp +++ b/test/old_tests/UnitTests/xaml_typename.cpp @@ -7,6 +7,27 @@ using namespace winrt; using namespace Windows::UI::Xaml::Interop; using namespace std::string_view_literals; +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Boolean"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Char16"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"UInt8"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Int8"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"UInt16"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Int16"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"UInt32"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Int32"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"UInt64"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Int64"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Single"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Double"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"String"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Guid"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"DateTime"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"TimeSpan"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Point"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Size"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Rect"sv)); +static_assert(string_view_equal(impl::xaml_typename_name::value(), L"Object"sv)); + static_assert(impl::xaml_typename_kind::value == Windows::UI::Xaml::Interop::TypeKind::Primitive); static_assert(impl::xaml_typename_kind::value == Windows::UI::Xaml::Interop::TypeKind::Primitive); static_assert(impl::xaml_typename_kind::value == Windows::UI::Xaml::Interop::TypeKind::Primitive); @@ -38,6 +59,7 @@ namespace TEST_CASE("xaml_typename") { + // We've static asserted all the impl values, but let's verify that the public runtime API behaves as expected REQUIRE(equals(TypeName{ hstring(L"Boolean"sv), TypeKind::Primitive }, xaml_typename())); REQUIRE(equals(TypeName{ hstring(L"Char16"sv), TypeKind::Primitive }, xaml_typename())); REQUIRE(equals(TypeName{ hstring(L"UInt8"sv), TypeKind::Primitive }, xaml_typename())); diff --git a/test/test/enum.cpp b/test/test/enum.cpp index 8dbff04f4..48f356a75 100644 --- a/test/test/enum.cpp +++ b/test/test/enum.cpp @@ -9,8 +9,8 @@ TEST_CASE("enum") STATIC_REQUIRE(std::is_same_v, int32_t>); STATIC_REQUIRE(std::is_same_v, uint32_t>); - REQUIRE(name_of() == L"test_component.Signed"sv); - REQUIRE(name_of() == L"test_component.Unsigned"sv); + STATIC_REQUIRE(name_of() == L"test_component.Signed"sv); + STATIC_REQUIRE(name_of() == L"test_component.Unsigned"sv); REQUIRE(((Unsigned::First | Unsigned::Second | Unsigned::Third) & Unsigned::Second) == Unsigned::Second); diff --git a/test/test/generic_types.h b/test/test/generic_types.h index 5ead4b880..c305cd734 100644 --- a/test/test/generic_types.h +++ b/test/test/generic_types.h @@ -7,7 +7,7 @@ using namespace Windows::Foundation::Numerics; using namespace std::literals; #define REQUIRE_EQUAL_GUID(left, ...) STATIC_REQUIRE(equal(make_guid(left), guid_of<__VA_ARGS__>())); -#define REQUIRE_EQUAL_NAME(left, ...) REQUIRE(left == name_of<__VA_ARGS__>()); +#define REQUIRE_EQUAL_NAME(left, ...) STATIC_REQUIRE(left == name_of<__VA_ARGS__>()); namespace { diff --git a/test/test/names.cpp b/test/test/names.cpp index 13352dcd2..7730fe82e 100644 --- a/test/test/names.cpp +++ b/test/test/names.cpp @@ -10,7 +10,7 @@ void check_terminated(winrt::param::hstring const&) TEST_CASE("names") { REQUIRE(name_of() == L"{00000000-0000-0000-c000-000000000046}"sv); - REQUIRE(name_of() == L"Object"sv); + STATIC_REQUIRE(name_of() == L"Object"sv); check_terminated(name_of()); check_terminated(name_of()); diff --git a/test/test_win7/enum.cpp b/test/test_win7/enum.cpp index 8dbff04f4..48f356a75 100644 --- a/test/test_win7/enum.cpp +++ b/test/test_win7/enum.cpp @@ -9,8 +9,8 @@ TEST_CASE("enum") STATIC_REQUIRE(std::is_same_v, int32_t>); STATIC_REQUIRE(std::is_same_v, uint32_t>); - REQUIRE(name_of() == L"test_component.Signed"sv); - REQUIRE(name_of() == L"test_component.Unsigned"sv); + STATIC_REQUIRE(name_of() == L"test_component.Signed"sv); + STATIC_REQUIRE(name_of() == L"test_component.Unsigned"sv); REQUIRE(((Unsigned::First | Unsigned::Second | Unsigned::Third) & Unsigned::Second) == Unsigned::Second); diff --git a/test/test_win7/generic_types.h b/test/test_win7/generic_types.h index 5ead4b880..c305cd734 100644 --- a/test/test_win7/generic_types.h +++ b/test/test_win7/generic_types.h @@ -7,7 +7,7 @@ using namespace Windows::Foundation::Numerics; using namespace std::literals; #define REQUIRE_EQUAL_GUID(left, ...) STATIC_REQUIRE(equal(make_guid(left), guid_of<__VA_ARGS__>())); -#define REQUIRE_EQUAL_NAME(left, ...) REQUIRE(left == name_of<__VA_ARGS__>()); +#define REQUIRE_EQUAL_NAME(left, ...) STATIC_REQUIRE(left == name_of<__VA_ARGS__>()); namespace { diff --git a/test/test_win7/names.cpp b/test/test_win7/names.cpp index 13352dcd2..7730fe82e 100644 --- a/test/test_win7/names.cpp +++ b/test/test_win7/names.cpp @@ -10,7 +10,7 @@ void check_terminated(winrt::param::hstring const&) TEST_CASE("names") { REQUIRE(name_of() == L"{00000000-0000-0000-c000-000000000046}"sv); - REQUIRE(name_of() == L"Object"sv); + STATIC_REQUIRE(name_of() == L"Object"sv); check_terminated(name_of()); check_terminated(name_of()); From 2f2911f8b03eaf4c8f63bab19a69639a7f0e588b Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 21 May 2020 07:21:08 +0300 Subject: [PATCH 006/400] Added debugging GUID comment generation (#630) --- cppwinrt/code_writers.h | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index 2d1f46590..ed0d4bd96 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -318,6 +318,24 @@ namespace cppwinrt get(get(args[10].value).value)); } + static void write_guid_comment(writer& w, std::vector const& args) + { + using std::get; + + w.write_printf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + get(get(args[0].value).value), + get(get(args[1].value).value), + get(get(args[2].value).value), + get(get(args[3].value).value), + get(get(args[4].value).value), + get(get(args[5].value).value), + get(get(args[6].value).value), + get(get(args[7].value).value), + get(get(args[8].value).value), + get(get(args[9].value).value), + get(get(args[10].value).value)); + } + static void write_category(writer& w, TypeDef const& type, std::string_view const& category) { auto generics = type.GenericParam(); @@ -395,20 +413,22 @@ namespace cppwinrt } auto generics = type.GenericParam(); + auto guid = attribute.Value().FixedArgs(); if (empty(generics)) { - auto format = R"( template <> inline constexpr guid guid_v<%>{ % }; + auto format = R"( template <> inline constexpr guid guid_v<%>{ % }; // % )"; w.write(format, type, - bind(attribute.Value().FixedArgs())); + bind(guid), + bind(guid)); } else { auto format = R"( template <%> inline constexpr guid guid_v<%>{ pinterface_guid<%>::value }; - template <%> inline constexpr guid generic_guid_v<%>{ % }; + template <%> inline constexpr guid generic_guid_v<%>{ % }; // % )"; w.write(format, @@ -417,7 +437,8 @@ namespace cppwinrt type, bind(generics), type, - bind(attribute.Value().FixedArgs())); + bind(guid), + bind(guid)); } } From 094921d04b9f3fead1239ab274f0f4d787e7f346 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Sun, 24 May 2020 21:26:56 -0700 Subject: [PATCH 007/400] Delete null-assignment. (#636) Fixes #627 --- strings/base_string.h | 1 + 1 file changed, 1 insertion(+) diff --git a/strings/base_string.h b/strings/base_string.h index 7423a498d..d35e0f20a 100644 --- a/strings/base_string.h +++ b/strings/base_string.h @@ -188,6 +188,7 @@ WINRT_EXPORT namespace winrt hstring(hstring&&) noexcept = default; hstring& operator=(hstring&&) = default; hstring(std::nullptr_t) = delete; + hstring& operator=(std::nullptr_t) = delete; hstring(std::initializer_list value) : hstring(value.begin(), static_cast(value.size())) From 78d212ab65c196c7e2fba2873f788fd0fe8e4877 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Thu, 28 May 2020 14:26:49 -0700 Subject: [PATCH 008/400] Improve com_array construction (#642) --- strings/base_array.h | 27 ++++++++++++++---- test/old_tests/UnitTests/array.cpp | 46 ++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/strings/base_array.h b/strings/base_array.h index d3cb5c750..eb3fa540b 100644 --- a/strings/base_array.h +++ b/strings/base_array.h @@ -257,23 +257,25 @@ WINRT_EXPORT namespace winrt std::uninitialized_fill_n(this->m_data, count, value); } - template com_array(InIt first, InIt last) + template ::difference_type>> + com_array(InIt first, InIt last) { alloc(static_cast(std::distance(first, last))); std::uninitialized_copy(first, last, this->begin()); } - explicit com_array(std::vector const& value) : + template + explicit com_array(std::vector const& value) : com_array(value.begin(), value.end()) {} - template - explicit com_array(std::array const& value) : + template + explicit com_array(std::array const& value) : com_array(value.begin(), value.end()) {} - template - explicit com_array(value_type const(&value)[N]) : + template + explicit com_array(U const(&value)[N]) : com_array(value, value + N) {} @@ -281,6 +283,11 @@ WINRT_EXPORT namespace winrt com_array(value.begin(), value.end()) {} + template >> + com_array(std::initializer_list value) : + com_array(value.begin(), value.end()) + {} + com_array(com_array&& other) noexcept : array_view(other.m_data, other.m_size) { @@ -340,6 +347,14 @@ WINRT_EXPORT namespace winrt } }; + template com_array(uint32_t, C const&) -> com_array>; + template ::difference_type>> + com_array(InIt, InIt) -> com_array::value_type>>; + template com_array(std::vector const&) -> com_array>; + template com_array(std::array const&) -> com_array>; + template com_array(C const(&)[N]) -> com_array>; + template com_array(std::initializer_list) -> com_array>; + namespace impl { template diff --git a/test/old_tests/UnitTests/array.cpp b/test/old_tests/UnitTests/array.cpp index 74b9f2a9f..aa13b8018 100644 --- a/test/old_tests/UnitTests/array.cpp +++ b/test/old_tests/UnitTests/array.cpp @@ -1282,3 +1282,49 @@ TEST_CASE("array_view,ctad") #undef REQUIRE_DEDUCED_AS } + +// Verify various ways of constructing a com_array. +TEST_CASE("com_array,construct") +{ + com_array two_zeroes{ { 0, 0 } }; + + REQUIRE(com_array(2) == com_array({ 0, 0 })); + + // Verify these are treated as { size, initial_value } constructors + // instead of { first, last } constructors. + REQUIRE(com_array(2, 5) == com_array({ 5, 5 })); + REQUIRE(com_array(2, 5) == com_array({ 5, 5 })); +} + +// Verify that class template argument deduction works for com_array. +TEST_CASE("com_array,ctad") +{ +#define REQUIRE_DEDUCED_AS(T, ...) \ + static_assert(std::is_same_v, decltype(com_array(__VA_ARGS__))>) + + REQUIRE_DEDUCED_AS(uint8_t, 3, uint8_t(5)); + + // Note that this looks like both an array and an initializer_list. + REQUIRE_DEDUCED_AS(uint8_t, { uint8_t(5) }); + + REQUIRE_DEDUCED_AS(uint8_t, com_array()); + + uint8_t a[3]{}; + REQUIRE_DEDUCED_AS(uint8_t, &a[0], &a[0]); + REQUIRE_DEDUCED_AS(uint8_t, a); + + std::array ar{}; + REQUIRE_DEDUCED_AS(uint8_t, ar); + + std::vector v{}; + REQUIRE_DEDUCED_AS(uint8_t, v); + + uint8_t const ca[3]{}; + REQUIRE_DEDUCED_AS(uint8_t, &ca[0], &ca[0]); + REQUIRE_DEDUCED_AS(uint8_t, ca); + + std::array arc{}; + REQUIRE_DEDUCED_AS(uint8_t, arc); + +#undef REQUIRE_DEDUCED_AS +} From 82fa8ec2863540006928aa6c0bad9140b9f6aef9 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Thu, 28 May 2020 14:27:13 -0700 Subject: [PATCH 009/400] Improve async cancellation interop (#643) --- strings/base_coroutine_foundation.h | 83 +++++++++++++++------- test/old_tests/UnitTests/async_cancel.cpp | 86 ++++++++++++++++++++++- 2 files changed, 143 insertions(+), 26 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 5af82458f..61811138c 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -38,19 +38,32 @@ namespace winrt::impl WINRT_ASSERT(!is_sta()); } - template - void wait_for_completed(Async const& async, uint32_t const timeout) + template + std::pair make_delegate_with_shared_state(H&& handler) { - void* event = check_pointer(WINRT_IMPL_CreateEventW(nullptr, true, false, nullptr)); + auto d = make_delegate(std::forward(handler)); + return { std::move(d), reinterpret_cast*>(get_abi(d)) }; + } - // The delegate is a local to ensure that the event outlives the call to WaitForSingleObject. - async_completed_handler_t delegate = [event = handle(event)](auto && ...) + template + auto wait_for_completed(Async const& async, uint32_t const timeout) + { + struct shared_type { - WINRT_VERIFY(WINRT_IMPL_SetEvent(event.get())); + handle event{ check_pointer(WINRT_IMPL_CreateEventW(nullptr, true, false, nullptr)) }; + Windows::Foundation::AsyncStatus status{ Windows::Foundation::AsyncStatus::Started }; + + void operator()(Async const&, Windows::Foundation::AsyncStatus operation_status) noexcept + { + status = operation_status; + WINRT_VERIFY(WINRT_IMPL_SetEvent(event.get())); + } }; + auto [delegate, shared] = make_delegate_with_shared_state>(shared_type{}); async.Completed(delegate); - WINRT_IMPL_WaitForSingleObject(event, timeout); + WINRT_IMPL_WaitForSingleObject(shared->event.get(), timeout); + return shared->status; } template @@ -59,8 +72,15 @@ namespace winrt::impl check_sta_blocking_wait(); auto const milliseconds = std::chrono::duration_cast(timeout).count(); WINRT_ASSERT((milliseconds >= 0) && (static_cast(milliseconds) < 0xFFFFFFFFull)); // Within uint32_t range and not INFINITE - wait_for_completed(async, static_cast(milliseconds)); - return async.Status(); + return wait_for_completed(async, static_cast(milliseconds)); + } + + inline void check_status_canceled(Windows::Foundation::AsyncStatus status) + { + if (status == Windows::Foundation::AsyncStatus::Canceled) + { + throw hresult_canceled(); + } } template @@ -68,10 +88,12 @@ namespace winrt::impl { check_sta_blocking_wait(); - if (async.Status() == Windows::Foundation::AsyncStatus::Started) + auto status = async.Status(); + if (status == Windows::Foundation::AsyncStatus::Started) { - wait_for_completed(async, 0xFFFFFFFF); // INFINITE + status = wait_for_completed(async, 0xFFFFFFFF); // INFINITE } + check_status_canceled(status); return async.GetResults(); } @@ -90,7 +112,7 @@ namespace winrt::impl if (m_handle) Complete(); } - void operator()(Windows::Foundation::IAsyncInfo const&, Windows::Foundation::AsyncStatus) + void operator()() { Complete(); } @@ -109,19 +131,25 @@ namespace winrt::impl struct await_adapter { Async const& async; + Windows::Foundation::AsyncStatus status = Windows::Foundation::AsyncStatus::Started; bool await_ready() const noexcept { return false; } - void await_suspend(std::experimental::coroutine_handle<> handle) const + void await_suspend(std::experimental::coroutine_handle<> handle) { - async.Completed(disconnect_aware_handler{ handle }); + async.Completed([this, handler = disconnect_aware_handler{ handle }](auto&&, auto operation_status) mutable + { + status = operation_status; + handler(); + }); } auto await_resume() const { + check_status_canceled(status); return async.GetResults(); } }; @@ -691,28 +719,33 @@ WINRT_EXPORT namespace winrt struct shared_type { handle event{ check_pointer(WINRT_IMPL_CreateEventW(nullptr, true, false, nullptr)) }; + Windows::Foundation::AsyncStatus status{ Windows::Foundation::AsyncStatus::Started }; T result; + + void operator()(T const& sender, Windows::Foundation::AsyncStatus operation_status) noexcept + { + auto sender_abi = *(impl::unknown_abi**)&sender; + + if (nullptr == _InterlockedCompareExchangePointer(reinterpret_cast(&result), sender_abi, nullptr)) + { + sender_abi->AddRef(); + status = operation_status; + WINRT_VERIFY(WINRT_IMPL_SetEvent(event.get())); + } + } }; - auto shared = std::make_shared(); + auto [delegate, shared] = impl::make_delegate_with_shared_state>(shared_type{}); auto completed = [&](T const& async) { - async.Completed([shared](T const& sender, Windows::Foundation::AsyncStatus) noexcept - { - auto sender_abi = *(impl::unknown_abi**)&sender; - - if (nullptr == _InterlockedCompareExchangePointer(reinterpret_cast(&shared->result), sender_abi, nullptr)) - { - sender_abi->AddRef(); - WINRT_VERIFY(WINRT_IMPL_SetEvent(shared->event.get())); - } - }); + async.Completed(delegate); }; completed(first); (completed(rest), ...); co_await resume_on_signal(shared->event.get()); + impl::check_status_canceled(shared->status); co_return shared->result.GetResults(); } } diff --git a/test/old_tests/UnitTests/async_cancel.cpp b/test/old_tests/UnitTests/async_cancel.cpp index d67dd3692..515b5fcff 100644 --- a/test/old_tests/UnitTests/async_cancel.cpp +++ b/test/old_tests/UnitTests/async_cancel.cpp @@ -58,6 +58,60 @@ namespace REQUIRE(!signaled(callback)); co_await resume_on_signal(end.get()); } + + // Other projections report cancellation via the Completed handler and Status, + // rather than via ErrorCode and GetResults. Verify we interop cancellation properly. + template + struct foreign_canceled_async : implements + { + template + void Completed(Handler&& complete) + { + complete(*this, AsyncStatus::Canceled); + } + + auto Completed() const noexcept + { + return nullptr; + } + + uint32_t Id() const noexcept + { + return 1; + } + + AsyncStatus Status() const noexcept + { + return AsyncStatus::Canceled; + } + + hresult ErrorCode() const noexcept + { + return impl::error_illegal_method_call; + } + + decltype(std::declval().GetResults()) GetResults() const + { + throw_hresult(ErrorCode()); + } + + void Cancel() const noexcept + { + } + + void Close() const noexcept + { + } + }; + + struct foreign_canceled_action : foreign_canceled_async + { + }; + + template + struct foreign_canceled_operation : foreign_canceled_async, IAsyncOperation> + { + }; } TEST_CASE("async_cancel_no_async") @@ -98,4 +152,34 @@ TEST_CASE("async_cancel_after_callback") SetEvent(end.get()); wait(callback); REQUIRE(async.Status() == AsyncStatus::Canceled); -} \ No newline at end of file +} + +TEST_CASE("async_cancel_use_status") +{ + // Validate that co_await preserves cancellation. + handle complete{ CreateEvent(nullptr, true, false, nullptr) }; + [](void* complete) -> fire_and_forget + { + REQUIRE_THROWS_AS(co_await make(), hresult_canceled); + REQUIRE_THROWS_AS(co_await make>(), hresult_canceled); + + REQUIRE_THROWS_AS(co_await when_any(make(), make()), hresult_canceled); + REQUIRE_THROWS_AS(co_await when_any(make>(), make>()), hresult_canceled); + + REQUIRE_THROWS_AS(co_await when_all(make(), make()), hresult_canceled); + REQUIRE_THROWS_AS(co_await when_all(make>(), make>()), hresult_canceled); + + SetEvent(complete); + }(complete.get()); + WaitForSingleObject(complete.get(), INFINITE); + + // Validate that get() preserves cancellation. + REQUIRE_THROWS_AS(make().get(), hresult_canceled); + REQUIRE_THROWS_AS(make>().get(), hresult_canceled); + + REQUIRE_THROWS_AS(when_any(make(), make()).get(), hresult_canceled); + REQUIRE_THROWS_AS(when_any(make>(), make>()).get(), hresult_canceled); + + REQUIRE_THROWS_AS(when_all(make(), make()).get(), hresult_canceled); + REQUIRE_THROWS_AS(when_all(make>(), make>()).get(), hresult_canceled); +} From dd4d45ed58c23b5b4c9459a8f2821c304798b025 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Thu, 28 May 2020 14:52:41 -0700 Subject: [PATCH 010/400] final_suspend (#644) --- strings/base_coroutine_foundation.h | 5 + strings/base_coroutine_threadpool.h | 5 + test/catch.hpp | 441 +++++++++++++++++----------- test/test/notify_awaiter.cpp | 64 ++-- test/test_win7/notify_awaiter.cpp | 217 -------------- test/test_win7/test_win7.vcxproj | 1 - 6 files changed, 294 insertions(+), 439 deletions(-) delete mode 100644 test/test_win7/notify_awaiter.cpp diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 61811138c..2ae165717 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -497,6 +497,11 @@ namespace winrt::impl auto final_suspend() noexcept { + if (winrt_suspend_handler) + { + winrt_suspend_handler(this); + } + return final_suspend_awaiter{ this }; } diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index 1a6b6170e..254840dc2 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -526,6 +526,11 @@ namespace std::experimental suspend_never final_suspend() const noexcept { + if (winrt_suspend_handler) + { + winrt_suspend_handler(this); + } + return{}; } diff --git a/test/catch.hpp b/test/catch.hpp index 6c1756a6c..f64422ae1 100644 --- a/test/catch.hpp +++ b/test/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.11.1 - * Generated: 2019-12-28 21:22:11.930976 + * Catch v2.12.2 + * Generated: 2020-05-25 15:09:23.791719 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,8 +14,8 @@ #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 11 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 12 +#define CATCH_VERSION_PATCH 2 #ifdef __clang__ # pragma clang system_header @@ -132,7 +132,7 @@ namespace Catch { #endif -#if defined(CATCH_CPP17_OR_GREATER) +#if defined(__cpp_lib_uncaught_exceptions) # define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS #endif @@ -141,6 +141,9 @@ namespace Catch { #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + #endif #if defined(__clang__) @@ -148,6 +151,21 @@ namespace Catch { # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") @@ -294,7 +312,7 @@ namespace Catch { #define CATCH_CONFIG_COLOUR_NONE #endif -#if defined(__UCLIBC__) +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) #define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER #endif @@ -420,6 +438,12 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #elif defined(__clang__) && (__clang_major__ < 5) @@ -920,13 +944,13 @@ namespace Catch { #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is - // replaced with std::invoke_result here. Also *_t format is preferred over - // typename *::type format. - template - using FunctionReturnType = std::remove_reference_t>>; + // replaced with std::invoke_result here. + template + using FunctionReturnType = std::remove_reference_t>>; #else - template - using FunctionReturnType = typename std::remove_reference::type>::type>::type; + // Keep ::type here because we still support C++11 + template + using FunctionReturnType = typename std::remove_reference::type>::type>::type; #endif } // namespace Catch @@ -1964,20 +1988,27 @@ namespace Catch { #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER namespace Catch { - struct not_this_one {}; // Tag type for detecting which begin/ end are being selected - - // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace + // Import begin/ end from std here using std::begin; using std::end; - not_this_one begin( ... ); - not_this_one end( ... ); + namespace detail { + template + struct void_type { + using type = void; + }; + + template + struct is_range_impl : std::false_type { + }; + + template + struct is_range_impl()))>::type> : std::true_type { + }; + } // namespace detail template - struct is_range { - static const bool value = - !std::is_same())), not_this_one>::value && - !std::is_same())), not_this_one>::value; + struct is_range : detail::is_range_impl { }; #if defined(_MANAGED) // Managed types are never ranges @@ -2345,6 +2376,18 @@ namespace Catch { auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; } + template + auto operator | (RhsT const& rhs) -> BinaryExpr const { + return { static_cast(m_lhs | rhs), m_lhs, "|", rhs }; + } + template + auto operator & (RhsT const& rhs) -> BinaryExpr const { + return { static_cast(m_lhs & rhs), m_lhs, "&", rhs }; + } + template + auto operator ^ (RhsT const& rhs) -> BinaryExpr const { + return { static_cast(m_lhs ^ rhs), m_lhs, "^", rhs }; + } template auto operator && ( RhsT const& ) -> BinaryExpr const { @@ -2663,6 +2706,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { \ + CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ INTERNAL_CATCH_TRY { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ @@ -2671,8 +2715,7 @@ namespace Catch { CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look - // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ @@ -2989,6 +3032,9 @@ namespace Catch { {} std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return ""; +#else try { if( it == itEnd ) std::rethrow_exception(std::current_exception()); @@ -2998,6 +3044,7 @@ namespace Catch { catch( T& ex ) { return m_translateFunction( ex ); } +#endif } protected: @@ -3266,9 +3313,10 @@ namespace Matchers { return description; } - MatchAllOf& operator && ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; + MatchAllOf operator && ( MatcherBase const& other ) { + auto copy(*this); + copy.m_matchers.push_back( &other ); + return copy; } std::vector const*> m_matchers; @@ -3299,9 +3347,10 @@ namespace Matchers { return description; } - MatchAnyOf& operator || ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; + MatchAnyOf operator || ( MatcherBase const& other ) { + auto copy(*this); + copy.m_matchers.push_back( &other ); + return copy; } std::vector const*> m_matchers; @@ -3558,12 +3607,12 @@ namespace Catch { namespace Matchers { namespace Vector { - template - struct ContainsElementMatcher : MatcherBase> { + template + struct ContainsElementMatcher : MatcherBase> { ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} - bool match(std::vector const &v) const override { + bool match(std::vector const &v) const override { for (auto const& el : v) { if (el == m_comparator) { return true; @@ -3579,12 +3628,12 @@ namespace Matchers { T const& m_comparator; }; - template - struct ContainsMatcher : MatcherBase> { + template + struct ContainsMatcher : MatcherBase> { - ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const override { + bool match(std::vector const &v) const override { // !TBD: see note in EqualsMatcher if (m_comparator.size() > v.size()) return false; @@ -3606,18 +3655,18 @@ namespace Matchers { return "Contains: " + ::Catch::Detail::stringify( m_comparator ); } - std::vector const& m_comparator; + std::vector const& m_comparator; }; - template - struct EqualsMatcher : MatcherBase> { + template + struct EqualsMatcher : MatcherBase> { - EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const override { + bool match(std::vector const &v) const override { // !TBD: This currently works if all elements can be compared using != // - a more general approach would be via a compare template that defaults - // to using !=. but could be specialised for, e.g. std::vector etc + // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly if (m_comparator.size() != v.size()) return false; @@ -3629,15 +3678,15 @@ namespace Matchers { std::string describe() const override { return "Equals: " + ::Catch::Detail::stringify( m_comparator ); } - std::vector const& m_comparator; + std::vector const& m_comparator; }; - template - struct ApproxMatcher : MatcherBase> { + template + struct ApproxMatcher : MatcherBase> { - ApproxMatcher(std::vector const& comparator) : m_comparator( comparator ) {} + ApproxMatcher(std::vector const& comparator) : m_comparator( comparator ) {} - bool match(std::vector const &v) const override { + bool match(std::vector const &v) const override { if (m_comparator.size() != v.size()) return false; for (std::size_t i = 0; i < v.size(); ++i) @@ -3664,16 +3713,14 @@ namespace Matchers { return *this; } - std::vector const& m_comparator; + std::vector const& m_comparator; mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); }; - template - struct UnorderedEqualsMatcher : MatcherBase> { - UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} - bool match(std::vector const& vec) const override { - // Note: This is a reimplementation of std::is_permutation, - // because I don't want to include inside the common path + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} + bool match(std::vector const& vec) const override { if (m_target.size() != vec.size()) { return false; } @@ -3684,7 +3731,7 @@ namespace Matchers { return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); } private: - std::vector const& m_target; + std::vector const& m_target; }; } // namespace Vector @@ -3692,29 +3739,29 @@ namespace Matchers { // The following functions create the actual matcher objects. // This allows the types to be inferred - template - Vector::ContainsMatcher Contains( std::vector const& comparator ) { - return Vector::ContainsMatcher( comparator ); + template, typename AllocMatch = AllocComp> + Vector::ContainsMatcher Contains( std::vector const& comparator ) { + return Vector::ContainsMatcher( comparator ); } - template - Vector::ContainsElementMatcher VectorContains( T const& comparator ) { - return Vector::ContainsElementMatcher( comparator ); + template> + Vector::ContainsElementMatcher VectorContains( T const& comparator ) { + return Vector::ContainsElementMatcher( comparator ); } - template - Vector::EqualsMatcher Equals( std::vector const& comparator ) { - return Vector::EqualsMatcher( comparator ); + template, typename AllocMatch = AllocComp> + Vector::EqualsMatcher Equals( std::vector const& comparator ) { + return Vector::EqualsMatcher( comparator ); } - template - Vector::ApproxMatcher Approx( std::vector const& comparator ) { - return Vector::ApproxMatcher( comparator ); + template, typename AllocMatch = AllocComp> + Vector::ApproxMatcher Approx( std::vector const& comparator ) { + return Vector::ApproxMatcher( comparator ); } - template - Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { - return Vector::UnorderedEqualsMatcher(target); + template, typename AllocMatch = AllocComp> + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { + return Vector::UnorderedEqualsMatcher( target ); } } // namespace Matchers @@ -4411,6 +4458,7 @@ namespace Catch { } // end namespace Catch // end catch_option.hpp +#include #include #include #include @@ -4481,6 +4529,7 @@ namespace Catch { virtual int benchmarkSamples() const = 0; virtual double benchmarkConfidenceInterval() const = 0; virtual unsigned int benchmarkResamples() const = 0; + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; }; using IConfigPtr = std::shared_ptr; @@ -5234,6 +5283,7 @@ namespace Catch { unsigned int benchmarkSamples = 100; double benchmarkConfidenceInterval = 0.95; unsigned int benchmarkResamples = 100000; + std::chrono::milliseconds::rep benchmarkWarmupTime = 100; Verbosity verbosity = Verbosity::Normal; WarnAbout::What warnings = WarnAbout::Nothing; @@ -5299,6 +5349,7 @@ namespace Catch { int benchmarkSamples() const override; double benchmarkConfidenceInterval() const override; unsigned int benchmarkResamples() const override; + std::chrono::milliseconds benchmarkWarmupTime() const override; private: @@ -6506,20 +6557,18 @@ namespace Catch { return {}; } }; - template - using ResultOf_t = typename std::result_of::type; // invoke and not return void :( template - CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { - return CompleteInvoker>::invoke(std::forward(fun), std::forward(args)...); + CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { + return CompleteInvoker>::invoke(std::forward(fun), std::forward(args)...); } const std::string benchmarkErrorMsg = "a benchmark failed to run successfully"; } // namespace Detail template - Detail::CompleteType_t> user_code(Fun&& fun) { + Detail::CompleteType_t> user_code(Fun&& fun) { CATCH_TRY{ return Detail::complete_invoke(std::forward(fun)); } CATCH_CATCH_ALL{ @@ -6764,8 +6813,8 @@ namespace Catch { Result result; int iterations; }; - template - using TimingOf = Timing, Detail::CompleteType_t>>; + template + using TimingOf = Timing, Detail::CompleteType_t>>; } // namespace Benchmark } // namespace Catch @@ -6776,7 +6825,7 @@ namespace Catch { namespace Benchmark { namespace Detail { template - TimingOf measure(Fun&& fun, Args&&... args) { + TimingOf measure(Fun&& fun, Args&&... args) { auto start = Clock::now(); auto&& r = Detail::complete_invoke(fun, std::forward(args)...); auto end = Clock::now(); @@ -6795,11 +6844,11 @@ namespace Catch { namespace Benchmark { namespace Detail { template - TimingOf measure_one(Fun&& fun, int iters, std::false_type) { + TimingOf measure_one(Fun&& fun, int iters, std::false_type) { return Detail::measure(fun, iters); } template - TimingOf measure_one(Fun&& fun, int iters, std::true_type) { + TimingOf measure_one(Fun&& fun, int iters, std::true_type) { Detail::ChronometerModel meter; auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); @@ -6816,7 +6865,7 @@ namespace Catch { }; template - TimingOf)> run_for_at_least(ClockDuration how_long, int seed, Fun&& fun) { + TimingOf> run_for_at_least(ClockDuration how_long, int seed, Fun&& fun) { auto iters = seed; while (iters < (1 << 30)) { auto&& Timing = measure_one(fun, iters, is_callable()); @@ -6884,11 +6933,13 @@ namespace Catch { #include #include #include +#include #include #include #include #include #include +#include namespace Catch { namespace Benchmark { @@ -7238,10 +7289,10 @@ namespace Catch { template ExecutionPlan> prepare(const IConfig &cfg, Environment> env) const { auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; - auto run_time = std::max(min_time, std::chrono::duration_cast(Detail::warmup_time)); + auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); auto&& test = Detail::run_for_at_least(std::chrono::duration_cast>(run_time), 1, fun); int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); - return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast>(Detail::warmup_time), Detail::warmup_iterations }; + return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; } template @@ -7868,8 +7919,10 @@ namespace Catch { #define CATCH_TRAP() __asm__("int $3") #elif defined(__aarch64__) #define CATCH_TRAP() __asm__(".inst 0xd4200000") - #elif defined(__arm__) + #elif defined(__arm__) && !defined(__thumb__) #define CATCH_TRAP() __asm__(".inst 0xe7f001f0") + #elif defined(__arm__) && defined(__thumb__) + #define CATCH_TRAP() __asm__(".inst 0xde01") #endif #elif defined(CATCH_PLATFORM_LINUX) @@ -7890,10 +7943,12 @@ namespace Catch { #define CATCH_TRAP() DebugBreak() #endif -#ifdef CATCH_TRAP - #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }() -#else - #define CATCH_BREAK_INTO_DEBUGGER() []{}() +#ifndef CATCH_BREAK_INTO_DEBUGGER + #ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }() + #else + #define CATCH_BREAK_INTO_DEBUGGER() []{}() + #endif #endif // end catch_debugger.h @@ -9665,8 +9720,7 @@ namespace Catch { if( !startsWith( line, '"' ) ) line = '"' + line + '"'; config.testsOrTags.push_back( line ); - config.testsOrTags.push_back( "," ); - + config.testsOrTags.emplace_back( "," ); } } //Remove comma in the end @@ -9707,14 +9761,16 @@ namespace Catch { }; auto const setWaitForKeypress = [&]( std::string const& keypress ) { auto keypressLc = toLower( keypress ); - if( keypressLc == "start" ) + if (keypressLc == "never") + config.waitForKeypress = WaitForKeypress::Never; + else if( keypressLc == "start" ) config.waitForKeypress = WaitForKeypress::BeforeStart; else if( keypressLc == "exit" ) config.waitForKeypress = WaitForKeypress::BeforeExit; else if( keypressLc == "both" ) config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; else - return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); + return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" ); return ParserResult::ok( ParseResultType::Matched ); }; auto const setVerbosity = [&]( std::string const& verbosity ) { @@ -9814,7 +9870,7 @@ namespace Catch { | Opt( config.libIdentify ) ["--libidentify"] ( "report name and version according to libidentify standard" ) - | Opt( setWaitForKeypress, "start|exit|both" ) + | Opt( setWaitForKeypress, "never|start|exit|both" ) ["--wait-for-keypress"] ( "waits for a keypress before exiting" ) | Opt( config.benchmarkSamples, "samples" ) @@ -9829,7 +9885,10 @@ namespace Catch { | Opt( config.benchmarkNoAnalysis ) ["--benchmark-no-analysis"] ( "perform only measurements; do not perform any analysis" ) - | Arg( config.testsOrTags, "test name|pattern|tags" ) + | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" ) + ["--benchmark-warmup-time"] + ( "amount of time in milliseconds spent on warming up each test (default: 100)" ) + | Arg( config.testsOrTags, "test name|pattern|tags" ) ( "which test or tests to use" ); return cli; @@ -9936,10 +9995,11 @@ namespace Catch { bool Config::showInvisibles() const { return m_data.showInvisibles; } Verbosity Config::verbosity() const { return m_data.verbosity; } - bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } - int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } - double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } - unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } + bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } + int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } + double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } + unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } + std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } IStream const* Config::openStream() { return Catch::makeStream(m_data.outputFilename); @@ -9980,7 +10040,7 @@ namespace Catch { }; struct NoColourImpl : IColourImpl { - void use( Colour::Code ) {} + void use( Colour::Code ) override {} static IColourImpl* instance() { static NoColourImpl s_instance; @@ -10153,13 +10213,13 @@ namespace Catch { namespace Catch { Colour::Colour( Code _colourCode ) { use( _colourCode ); } - Colour::Colour( Colour&& rhs ) noexcept { - m_moved = rhs.m_moved; - rhs.m_moved = true; + Colour::Colour( Colour&& other ) noexcept { + m_moved = other.m_moved; + other.m_moved = true; } - Colour& Colour::operator=( Colour&& rhs ) noexcept { - m_moved = rhs.m_moved; - rhs.m_moved = true; + Colour& Colour::operator=( Colour&& other ) noexcept { + m_moved = other.m_moved; + other.m_moved = true; return *this; } @@ -10171,7 +10231,7 @@ namespace Catch { // However, under some conditions it does happen (see #1626), // and this change is small enough that we can let practicality // triumph over purity in this case. - if (impl != NULL) { + if (impl != nullptr) { impl->use( _colourCode ); } } @@ -10291,8 +10351,7 @@ namespace Catch { #if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) -# include -# include +# include # include # include # include @@ -10524,7 +10583,7 @@ namespace Catch { assert( valueNames.size() == values.size() ); std::size_t i = 0; for( auto value : values ) - enumInfo->m_values.push_back({ value, valueNames[i++] }); + enumInfo->m_values.emplace_back(value, valueNames[i++]); return enumInfo; } @@ -11100,7 +11159,7 @@ namespace Catch { namespace Catch { std::size_t listTests( Config const& config ) { - TestSpec testSpec = config.testSpec(); + TestSpec const& testSpec = config.testSpec(); if( config.hasTestFilters() ) Catch::cout() << "Matching test cases:\n"; else { @@ -11134,7 +11193,7 @@ namespace Catch { } std::size_t listTestsNamesOnly( Config const& config ) { - TestSpec testSpec = config.testSpec(); + TestSpec const& testSpec = config.testSpec(); std::size_t matchedTests = 0; std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); for( auto const& testCaseInfo : matchedTestCases ) { @@ -11172,7 +11231,7 @@ namespace Catch { } std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); + TestSpec const& testSpec = config.testSpec(); if( config.hasTestFilters() ) Catch::cout() << "Tags for matching test cases:\n"; else { @@ -11364,16 +11423,8 @@ namespace { return static_cast(ulpDiff) <= maxUlpDiff; } -} //end anonymous namespace - #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -#if defined(__clang__) -#pragma clang diagnostic push -// The long double overload is currently unused -#pragma clang diagnostic ignored "-Wunused-function" -#endif - float nextafter(float x, float y) { return ::nextafterf(x, y); } @@ -11382,18 +11433,8 @@ namespace { return ::nextafter(x, y); } - long double nextafter(long double x, long double y) { - return ::nextafterl(x, y); - } - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - #endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ -namespace { - template FP step(FP start, FP direction, uint64_t steps) { for (uint64_t i = 0; i < steps; ++i) { @@ -11730,10 +11771,10 @@ namespace Catch { Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { auto trimmed = [&] (size_t start, size_t end) { - while (names[start] == ',' || isspace(names[start])) { + while (names[start] == ',' || isspace(static_cast(names[start]))) { ++start; } - while (names[end] == ',' || isspace(names[end])) { + while (names[end] == ',' || isspace(static_cast(names[end]))) { --end; } return names.substr(start, end - start + 1); @@ -11772,7 +11813,7 @@ namespace Catch { pos = skipq(pos, c); break; case ',': - if (start != pos && openings.size() == 0) { + if (start != pos && openings.empty()) { m_messages.emplace_back(macroName, lineInfo, resultType); m_messages.back().message = static_cast(trimmed(start, pos)); m_messages.back().message += " := "; @@ -11780,7 +11821,7 @@ namespace Catch { } } } - assert(openings.size() == 0 && "Mismatched openings"); + assert(openings.empty() && "Mismatched openings"); m_messages.emplace_back(macroName, lineInfo, resultType); m_messages.back().message = static_cast(trimmed(start, names.size() - 1)); m_messages.back().message += " := "; @@ -13268,11 +13309,11 @@ namespace Catch { char **utf8Argv = new char *[ argc ]; for ( int i = 0; i < argc; ++i ) { - int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); + int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr ); utf8Argv[ i ] = new char[ bufSize ]; - WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); + WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr ); } int returnCode = applyCommandLine( argc, utf8Argv ); @@ -13871,7 +13912,8 @@ namespace Catch { } } if( isHidden ) { - tags.push_back( "." ); + // Add all "hidden" tags to make them behave identically + tags.insert( tags.end(), { ".", "!hide" } ); } TestCaseInfo info( static_cast(nameAndTags.name), _className, desc, tags, _lineInfo ); @@ -13966,27 +14008,77 @@ namespace Catch { // end catch_test_case_info.cpp // start catch_test_case_registry_impl.cpp +#include #include namespace Catch { - std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + namespace { + struct TestHasher { + explicit TestHasher(Catch::SimplePcg32& rng) { + basis = rng(); + basis <<= 32; + basis |= rng(); + } - std::vector sorted = unsortedTestCases; + uint64_t basis; + uint64_t operator()(TestCase const& t) const { + // Modified FNV-1a hash + static constexpr uint64_t prime = 1099511628211; + uint64_t hash = basis; + for (const char c : t.name) { + hash ^= c; + hash *= prime; + } + return hash; + } + }; + } // end unnamed namespace + + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end() ); - break; - case RunTests::InRandomOrder: - seedRng( config ); - std::shuffle( sorted.begin(), sorted.end(), rng() ); - break; case RunTests::InDeclarationOrder: // already in declaration order break; + + case RunTests::InLexicographicalOrder: { + std::vector sorted = unsortedTestCases; + std::sort( sorted.begin(), sorted.end() ); + return sorted; + } + + case RunTests::InRandomOrder: { + seedRng( config ); + TestHasher h( rng() ); + + using hashedTest = std::pair; + std::vector indexed_tests; + indexed_tests.reserve( unsortedTestCases.size() ); + + for (auto const& testCase : unsortedTestCases) { + indexed_tests.emplace_back(h(testCase), &testCase); + } + + std::sort(indexed_tests.begin(), indexed_tests.end(), + [](hashedTest const& lhs, hashedTest const& rhs) { + if (lhs.first == rhs.first) { + return lhs.second->name < rhs.second->name; + } + return lhs.first < rhs.first; + }); + + std::vector sorted; + sorted.reserve( indexed_tests.size() ); + + for (auto const& hashed : indexed_tests) { + sorted.emplace_back(*hashed.second); + } + + return sorted; + } } - return sorted; + return unsortedTestCases; } bool isThrowSafe( TestCase const& testCase, IConfig const& config ) { @@ -14282,8 +14374,8 @@ namespace TestCaseTracking { void SectionTracker::addInitialFilters( std::vector const& filters ) { if( !filters.empty() ) { m_filters.reserve( m_filters.size() + filters.size() + 2 ); - m_filters.push_back(""); // Root - should never be consulted - m_filters.push_back(""); // Test Case - not a section filter + m_filters.emplace_back(""); // Root - should never be consulted + m_filters.emplace_back(""); // Test Case - not a section filter m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); } } @@ -14580,6 +14672,7 @@ namespace Catch { m_pos = m_arg.size(); m_substring.clear(); m_patternName.clear(); + m_realPatternPos = 0; return false; } endMode(); @@ -14598,6 +14691,7 @@ namespace Catch { } m_patternName.clear(); + m_realPatternPos = 0; return token; } @@ -15068,7 +15162,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 2, 11, 1, "", 0 ); + static Version version( 2, 12, 2, "", 0 ); return version; } @@ -15118,8 +15212,6 @@ namespace Catch { #include #include -using uchar = unsigned char; - namespace Catch { namespace { @@ -15192,7 +15284,7 @@ namespace { // (see: http://www.w3.org/TR/xml/#syntax) for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { - uchar c = m_str[idx]; + unsigned char c = m_str[idx]; switch (c) { case '<': os << "<"; break; case '&': os << "&"; break; @@ -15252,7 +15344,7 @@ namespace { bool valid = true; uint32_t value = headerValue(c); for (std::size_t n = 1; n < encBytes; ++n) { - uchar nc = m_str[idx + n]; + unsigned char nc = m_str[idx + n]; valid &= ((nc & 0xC0) == 0x80); value = (value << 6) | (nc & 0x3F); } @@ -15981,15 +16073,11 @@ class Duration { static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; - uint64_t m_inNanoseconds; + double m_inNanoseconds; Unit m_units; public: - explicit Duration(double inNanoseconds, Unit units = Unit::Auto) - : Duration(static_cast(inNanoseconds), units) { - } - - explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) + explicit Duration(double inNanoseconds, Unit units = Unit::Auto) : m_inNanoseconds(inNanoseconds), m_units(units) { if (m_units == Unit::Auto) { @@ -16018,7 +16106,7 @@ class Duration { case Unit::Minutes: return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); default: - return static_cast(m_inNanoseconds); + return m_inNanoseconds; } } auto unitsAsString() const -> std::string { @@ -16137,7 +16225,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig const& config) else { return{ - { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, + { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left }, { "samples mean std dev", 14, ColumnInfo::Right }, { "iterations low mean low std dev", 14, ColumnInfo::Right }, { "estimated high mean high std dev", 14, ColumnInfo::Right } @@ -16454,8 +16542,10 @@ void ConsoleReporter::printSummaryDivider() { } void ConsoleReporter::printTestFilters() { - if (m_config->testSpec().hasFilters()) - stream << Colour(Colour::BrightYellow) << "Filters: " << serializeFilters( m_config->getTestsOrTags() ) << '\n'; + if (m_config->testSpec().hasFilters()) { + Colour guard(Colour::BrightYellow); + stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; + } } CATCH_REGISTER_REPORTER("console", ConsoleReporter) @@ -16651,6 +16741,11 @@ namespace Catch { xml.writeAttribute( "name", name ); } xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute( "status", "run" ); writeAssertions( sectionNode ); @@ -16681,11 +16776,7 @@ namespace Catch { elementName = "error"; break; case ResultWas::ExplicitFailure: - elementName = "failure"; - break; case ResultWas::ExpressionFailed: - elementName = "failure"; - break; case ResultWas::DidntThrowException: elementName = "failure"; break; @@ -17111,16 +17202,16 @@ namespace Catch { m_xml.writeAttribute("samples", info.samples) .writeAttribute("resamples", info.resamples) .writeAttribute("iterations", info.iterations) - .writeAttribute("clockResolution", static_cast(info.clockResolution)) - .writeAttribute("estimatedDuration", static_cast(info.estimatedDuration)) + .writeAttribute("clockResolution", info.clockResolution) + .writeAttribute("estimatedDuration", info.estimatedDuration) .writeComment("All values in nano seconds"); } void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { m_xml.startElement("mean") - .writeAttribute("value", static_cast(benchmarkStats.mean.point.count())) - .writeAttribute("lowerBound", static_cast(benchmarkStats.mean.lower_bound.count())) - .writeAttribute("upperBound", static_cast(benchmarkStats.mean.upper_bound.count())) + .writeAttribute("value", benchmarkStats.mean.point.count()) + .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count()) + .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count()) .writeAttribute("ci", benchmarkStats.mean.confidence_interval); m_xml.endElement(); m_xml.startElement("standardDeviation") @@ -17171,7 +17262,7 @@ namespace Catch { #ifndef __OBJC__ -#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) // Standard C/C++ Win32 Unicode wmain entry point extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { #else diff --git a/test/test/notify_awaiter.cpp b/test/test/notify_awaiter.cpp index 9b074d2e4..1f9e26021 100644 --- a/test/test/notify_awaiter.cpp +++ b/test/test/notify_awaiter.cpp @@ -112,16 +112,14 @@ namespace co_return 0; } - struct notification + enum class notification { - uint32_t suspend{}; - uint32_t resume{}; + suspend, + resume, }; - static std::map watcher; - static slim_mutex lock; + static std::vector> watcher; static handle start_racing{ CreateEventW(nullptr, true, false, nullptr) }; - constexpr size_t test_coroutines = 20; constexpr size_t test_suspension_points = 12; IAsyncAction Async() @@ -155,60 +153,34 @@ TEST_CASE("notify_awaiter") winrt_suspend_handler = [](void const* token) noexcept { - slim_lock_guard guard(lock); - watcher[token].suspend += 1; + watcher.push_back({ token, notification::suspend }); }; winrt_resume_handler = [](void const* token) noexcept { - slim_lock_guard guard(lock); - watcher[token].resume += 1; + auto last = watcher.back(); + REQUIRE(last.first == token); + REQUIRE(last.second == notification::suspend); + watcher.push_back({ token, notification::resume }); }; - // Prepare a few coroutines. - - std::vector concurrency; + // Prepare a coroutine. REQUIRE(watcher.empty()); + auto async = Async(); - for (size_t i = 0; i != test_coroutines; ++i) - { - concurrency.push_back(Async()); - } - - // Give coroutines a moment to get to the starting line. - + // Give coroutine a moment to get to the starting line. Sleep(1000); - // Each coroutine should have suspended once. - - REQUIRE(concurrency.size() == test_coroutines); - REQUIRE(watcher.size() == test_coroutines); - - for (auto&& [_, tally] : watcher) - { - REQUIRE(tally.suspend == 1); - REQUIRE(tally.resume == 0); - } + // Coroutine should have suspended once. + REQUIRE(watcher.size() == 1); + REQUIRE(watcher.back().second == notification::suspend); // And the race is on! - SetEvent(start_racing.get()); + async.get(); - for (auto&& async : concurrency) - { - async.get(); - } - - // Each suspension point should have been recorded. - - REQUIRE(watcher.size() == test_coroutines * test_suspension_points); - - for (auto&& [_, tally] : watcher) - { - // And should be be perfectly balanced. - REQUIRE(tally.suspend == 1); - REQUIRE(tally.resume == 1); - } + // Each suspension point should have been recorded plus one for each final_suspend. + REQUIRE(watcher.size() == 2 * test_suspension_points + 5); // Remove watchers. diff --git a/test/test_win7/notify_awaiter.cpp b/test/test_win7/notify_awaiter.cpp deleted file mode 100644 index 9b074d2e4..000000000 --- a/test/test_win7/notify_awaiter.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "pch.h" - -using namespace winrt; -using namespace Windows::Foundation; - -namespace -{ - struct free_awaitable - { - }; - bool await_ready(free_awaitable) - { - return true; - } - void await_suspend(free_awaitable, std::experimental::coroutine_handle<>) - { - } - void await_resume(free_awaitable) - { - - } - - struct member_awaitable - { - bool await_ready() - { - return true; - } - void await_suspend(std::experimental::coroutine_handle<>) - { - } - void await_resume() - { - - } - }; - - struct free_operator_awaitable - { - }; - auto operator co_await(free_operator_awaitable) - { - struct awaitable - { - bool await_ready() - { - return true; - } - void await_suspend(std::experimental::coroutine_handle<>) - { - } - void await_resume() - { - } - }; - return awaitable{}; - } - - struct member_operator_awaitable - { - auto operator co_await() - { - struct awaitable - { - bool await_ready() - { - return true; - } - void await_suspend(std::experimental::coroutine_handle<>) - { - } - void await_resume() - { - } - }; - return awaitable{}; - } - }; - - struct no_copy_awaitable - { - no_copy_awaitable() = default; - no_copy_awaitable(no_copy_awaitable const&) = delete; - - bool await_ready() - { - return true; - } - void await_suspend(std::experimental::coroutine_handle<>) - { - } - void await_resume() - { - - } - }; - - IAsyncAction AsyncAction() - { - co_return; - } - IAsyncActionWithProgress AsyncActionWithProgress() - { - co_return; - } - IAsyncOperation AsyncOperation() - { - co_return 0; - } - IAsyncOperationWithProgress AsyncOperationWithProgress() - { - co_return 0; - } - - struct notification - { - uint32_t suspend{}; - uint32_t resume{}; - }; - - static std::map watcher; - static slim_mutex lock; - static handle start_racing{ CreateEventW(nullptr, true, false, nullptr) }; - constexpr size_t test_coroutines = 20; - constexpr size_t test_suspension_points = 12; - - IAsyncAction Async() - { - co_await resume_on_signal(start_racing.get()); - co_await resume_background(); - co_await resume_background(); - co_await free_awaitable{}; - co_await member_awaitable{}; - co_await free_operator_awaitable{}; - co_await member_operator_awaitable{}; - co_await no_copy_awaitable{}; - co_await AsyncAction(); - co_await AsyncActionWithProgress(); - co_await AsyncOperation(); - co_await AsyncOperationWithProgress(); - } -} - -TEST_CASE("notify_awaiter") -{ - // Everything works fine when nobody is watching. - - REQUIRE(!winrt_suspend_handler); - REQUIRE(!winrt_resume_handler); - SetEvent(start_racing.get()); - Async().get(); - ResetEvent(start_racing.get()); - - // Hook up some watchers. - - winrt_suspend_handler = [](void const* token) noexcept - { - slim_lock_guard guard(lock); - watcher[token].suspend += 1; - }; - - winrt_resume_handler = [](void const* token) noexcept - { - slim_lock_guard guard(lock); - watcher[token].resume += 1; - }; - - // Prepare a few coroutines. - - std::vector concurrency; - REQUIRE(watcher.empty()); - - for (size_t i = 0; i != test_coroutines; ++i) - { - concurrency.push_back(Async()); - } - - // Give coroutines a moment to get to the starting line. - - Sleep(1000); - - // Each coroutine should have suspended once. - - REQUIRE(concurrency.size() == test_coroutines); - REQUIRE(watcher.size() == test_coroutines); - - for (auto&& [_, tally] : watcher) - { - REQUIRE(tally.suspend == 1); - REQUIRE(tally.resume == 0); - } - - // And the race is on! - - SetEvent(start_racing.get()); - - for (auto&& async : concurrency) - { - async.get(); - } - - // Each suspension point should have been recorded. - - REQUIRE(watcher.size() == test_coroutines * test_suspension_points); - - for (auto&& [_, tally] : watcher) - { - // And should be be perfectly balanced. - REQUIRE(tally.suspend == 1); - REQUIRE(tally.resume == 1); - } - - // Remove watchers. - - winrt_suspend_handler = nullptr; - winrt_resume_handler = nullptr; -} diff --git a/test/test_win7/test_win7.vcxproj b/test/test_win7/test_win7.vcxproj index acc54b931..8abbfe8dc 100644 --- a/test/test_win7/test_win7.vcxproj +++ b/test/test_win7/test_win7.vcxproj @@ -358,7 +358,6 @@ - NotUsing From d610942ee5345fa8ff46e595b0755f2587967bec Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Fri, 29 May 2020 22:07:40 -0700 Subject: [PATCH 011/400] Edge cases in when_all (#645) --- strings/base_coroutine_foundation.h | 3 ++- test/test/when.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 2ae165717..3a2a5d0f9 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -712,7 +712,8 @@ WINRT_EXPORT namespace winrt template Windows::Foundation::IAsyncAction when_all(T... async) { - (co_await async, ...); + ((co_await async, void()), ...); + co_return; } template diff --git a/test/test/when.cpp b/test/test/when.cpp index a3b284169..27ce95551 100644 --- a/test/test/when.cpp +++ b/test/test/when.cpp @@ -5,6 +5,12 @@ using namespace concurrency; using namespace winrt; using namespace Windows::Foundation; +struct CommaStruct +{ + // If the comma operator is invoked, we will get a build failure. + CommaStruct operator,(CommaStruct) = delete; +}; + task ppl(bool& done) { co_await resume_background(); @@ -45,6 +51,12 @@ TEST_CASE("when") IAsyncAction result = when_any(done(), done()); result.get(); } + + // Verify edge case of empty parameter list. + when_all().get(); + + // Verify edge case of overloaded comma operator (shame on you). + when_all(create_task([] { return CommaStruct{}; }), create_task([] { return CommaStruct{}; })).get(); { handle first_event{ check_pointer(CreateEventW(nullptr, true, false, nullptr)) }; handle second_event{ check_pointer(CreateEventW(nullptr, true, false, nullptr)) }; From d6a041224cc7c6f3150bf81a0898dcf3b46fe39d Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Sun, 31 May 2020 07:48:53 -0700 Subject: [PATCH 012/400] Simply when_all implementation and test (#647) --- strings/base_coroutine_foundation.h | 2 +- test/test/when.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 3a2a5d0f9..8b22caa1f 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -712,7 +712,7 @@ WINRT_EXPORT namespace winrt template Windows::Foundation::IAsyncAction when_all(T... async) { - ((co_await async, void()), ...); + (void(co_await async), ...); co_return; } diff --git a/test/test/when.cpp b/test/test/when.cpp index 27ce95551..86edc6c77 100644 --- a/test/test/when.cpp +++ b/test/test/when.cpp @@ -5,10 +5,13 @@ using namespace concurrency; using namespace winrt; using namespace Windows::Foundation; -struct CommaStruct +struct CommaStruct : std::experimental::suspend_never { // If the comma operator is invoked, we will get a build failure. CommaStruct operator,(CommaStruct) = delete; + + // Awaiting the object just returns itself. + auto await_resume() const { return *this; } }; task ppl(bool& done) @@ -56,7 +59,7 @@ TEST_CASE("when") when_all().get(); // Verify edge case of overloaded comma operator (shame on you). - when_all(create_task([] { return CommaStruct{}; }), create_task([] { return CommaStruct{}; })).get(); + when_all(CommaStruct{}, CommaStruct{}).get(); { handle first_event{ check_pointer(CreateEventW(nullptr, true, false, nullptr)) }; handle second_event{ check_pointer(CreateEventW(nullptr, true, false, nullptr)) }; From 4e32538b56e4770a82c4e897de2e8d241eb09fc1 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 2 Jun 2020 07:40:25 -0700 Subject: [PATCH 013/400] winui dispatcher (#648) --- cppwinrt/code_writers.h | 4 +++ cppwinrt/cppwinrt.vcxproj | 1 + cppwinrt/cppwinrt.vcxproj.filters | 3 ++ strings/base_coroutine_system_winui.h | 50 +++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 strings/base_coroutine_system_winui.h diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index ed0d4bd96..ee082f717 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -3168,6 +3168,10 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable { w.write(strings::base_coroutine_system); } + else if (namespace_name == "Microsoft.System") + { + w.write(strings::base_coroutine_system_winui); + } else if (namespace_name == "Windows.UI.Core") { w.write(strings::base_coroutine_ui_core); diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index a87178c47..871316366 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -56,6 +56,7 @@ + diff --git a/cppwinrt/cppwinrt.vcxproj.filters b/cppwinrt/cppwinrt.vcxproj.filters index 20141f18f..069061a3b 100644 --- a/cppwinrt/cppwinrt.vcxproj.filters +++ b/cppwinrt/cppwinrt.vcxproj.filters @@ -157,6 +157,9 @@ strings + + strings + diff --git a/strings/base_coroutine_system_winui.h b/strings/base_coroutine_system_winui.h new file mode 100644 index 000000000..770573664 --- /dev/null +++ b/strings/base_coroutine_system_winui.h @@ -0,0 +1,50 @@ + +WINRT_EXPORT namespace winrt +{ + [[nodiscard]] inline auto resume_foreground( + Microsoft::System::DispatcherQueue const& dispatcher, + Microsoft::System::DispatcherQueuePriority const priority = Microsoft::System::DispatcherQueuePriority::Normal) noexcept + { + struct awaitable + { + awaitable(Microsoft::System::DispatcherQueue const& dispatcher, Microsoft::System::DispatcherQueuePriority const priority) noexcept : + m_dispatcher(dispatcher), + m_priority(priority) + { + } + + bool await_ready() const noexcept + { + return false; + } + + bool await_resume() const noexcept + { + return m_queued; + } + + bool await_suspend(std::experimental::coroutine_handle<> handle) + { + return m_dispatcher.TryEnqueue(m_priority, [handle, this] + { + m_queued = true; + handle(); + }); + } + + private: + Microsoft::System::DispatcherQueue const& m_dispatcher; + Microsoft::System::DispatcherQueuePriority const m_priority; + bool m_queued{}; + }; + + return awaitable{ dispatcher, priority }; + }; + +#ifdef __cpp_coroutines + inline auto operator co_await(Microsoft::System::DispatcherQueue const& dispatcher) + { + return resume_foreground(dispatcher); + } +#endif +} From 297365e712f11a0e4f3891ebf79544952a1e06fd Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 8 Jun 2020 08:18:46 -0700 Subject: [PATCH 014/400] Can clear a specific factory from static lifetime store (#654) --- strings/base_abi.h | 2 +- strings/base_implements.h | 23 +++++++++++++--- test/old_tests/Component/Events.cpp | 42 +++++++++++++++++++++++------ 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/strings/base_abi.h b/strings/base_abi.h index 1ad5e827e..6a88266b9 100644 --- a/strings/base_abi.h +++ b/strings/base_abi.h @@ -70,8 +70,8 @@ namespace winrt::impl virtual int32_t __stdcall unused2() noexcept = 0; virtual int32_t __stdcall unused3() noexcept = 0; virtual int32_t __stdcall Insert(void*, void*, bool*) noexcept = 0; + virtual int32_t __stdcall Remove(void*) noexcept = 0; virtual int32_t __stdcall unused4() noexcept = 0; - virtual int32_t __stdcall unused5() noexcept = 0; }; struct __declspec(novtable) IWeakReference : unknown_abi diff --git a/strings/base_implements.h b/strings/base_implements.h index 2457b80b4..0722aec5f 100644 --- a/strings/base_implements.h +++ b/strings/base_implements.h @@ -1198,6 +1198,14 @@ namespace winrt::impl }; #endif + inline com_ptr get_static_lifetime_map() + { + auto const lifetime_factory = get_activation_factory(L"Windows.ApplicationModel.Core.CoreApplication"); + Windows::Foundation::IUnknown collection; + check_hresult(lifetime_factory->GetCollection(put_abi(collection))); + return collection.as(); + } + template auto make_factory() -> typename impl::implements_default_interface::type { @@ -1209,10 +1217,7 @@ namespace winrt::impl } else { - auto const lifetime_factory = get_activation_factory(L"Windows.ApplicationModel.Core.CoreApplication"); - Windows::Foundation::IUnknown collection; - check_hresult(lifetime_factory->GetCollection(put_abi(collection))); - auto const map = collection.as(); + auto const map = get_static_lifetime_map(); param::hstring const name{ name_of() }; void* result{}; map->Lookup(get_abi(name), &result); @@ -1304,6 +1309,16 @@ WINRT_EXPORT namespace winrt } } + template + inline void clear_factory_static_lifetime() + { + auto unregister = [map = impl::get_static_lifetime_map()](param::hstring name) + { + map->Remove(get_abi(name)); + }; + ((unregister(name_of())), ...); + } + template struct implements : impl::producers, impl::base_implements::type { diff --git a/test/old_tests/Component/Events.cpp b/test/old_tests/Component/Events.cpp index 732569531..842f23556 100644 --- a/test/old_tests/Component/Events.cpp +++ b/test/old_tests/Component/Events.cpp @@ -74,25 +74,51 @@ namespace winrt::Component::factory_implementation bool Events::TestStaticLifetime() { + auto GetReferenceCount = [this]() + { + AddRef(); + return Release(); + }; + // Capture current reference count. - AddRef(); - auto refcount = Release(); + auto refcount = GetReferenceCount(); // Reset constructor count. s_constructorCount = 0; - auto self = make_self(); - if (self.get() != this) + // make_self should return a reference to ourselves + // since we are static_lifetime. + if (make_self().get() != this) { return false; } - self = nullptr; // Refcount should be unchanged. + if (refcount != GetReferenceCount()) + { + return false; + } + // Should not have been constructed spuriously. - AddRef(); - auto new_refcount = Release(); + if (s_constructorCount != 0) + { + return false; + } + + // Clear the static lifetime. That should drop the reference count. + clear_factory_static_lifetime(); + if (refcount == GetReferenceCount()) + { + return false; + } + + // Making a new object should put a different instance into + // the static lifetime. + if (make_self().get() == this) + { + return false; + } - return refcount == new_refcount && s_constructorCount == 0; + return true; } } From f816245db2eea456753979f13965b042e573fe45 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 8 Jun 2020 08:19:10 -0700 Subject: [PATCH 015/400] Fix unbox_value_or with explicit type or implicit hstring (#656) --- strings/base_reference_produce.h | 2 +- test/old_tests/UnitTests/Boxing2.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/strings/base_reference_produce.h b/strings/base_reference_produce.h index 31477b8a0..4ad39c5b9 100644 --- a/strings/base_reference_produce.h +++ b/strings/base_reference_produce.h @@ -318,7 +318,7 @@ WINRT_EXPORT namespace winrt } } - template + template , int> = 0> hstring unbox_value_or(Windows::Foundation::IInspectable const& value, param::hstring const& default_value) { if (value) diff --git a/test/old_tests/UnitTests/Boxing2.cpp b/test/old_tests/UnitTests/Boxing2.cpp index 1c43d0402..3b7c85d6b 100644 --- a/test/old_tests/UnitTests/Boxing2.cpp +++ b/test/old_tests/UnitTests/Boxing2.cpp @@ -214,4 +214,10 @@ TEST_CASE("Boxing") REQUIRE(unbox_value_or(box_value(static_cast(UnsignedEnum::Second)), UnsignedEnum::First) == UnsignedEnum::First); REQUIRE(unbox_value_or(box_value(static_cast(UnsignedEnum::Second)), UnsignedEnum::First) == UnsignedEnum::First); } + + { + // Test some cases where the compiler has to choose between multiple overloads. + REQUIRE(unbox_value_or(nullptr, {}) == IInspectable{}); + REQUIRE(unbox_value_or(nullptr, hstring{}) == hstring{}); + } } From 87436b4b5d683c6fdea292a708c816c153d3206c Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 8 Jun 2020 09:10:17 -0700 Subject: [PATCH 016/400] Fix ABI conformance of IMap::Remove, add TryRemove (#655) * Fix ABI conformance of IMap::Remove, add TryRemove `single_threaded_map()`'s IMap::Remove did not throw `hresult_out_of_range` on attempts to remove a nonexistent key. Now it throws. Failure to remove a nonexistent key does not invalidate iterators, because nothing actually changed. This brings the map implementation in line with the implementations in other projections. Note that this is a breaking change. Code that assumed nonexistent objects could be harmlessly removed will encounter exceptions when run against C++/WinRT implementations. This was, however, a pre-existing bug, because implementations from other projections (C#, C++/CX) always threw under those conditions. Added a TryRemove() method for people who wanted the nonthrowing version. Note that fixing the ABI conformance is required in order for TryRemove to work, because TryRemove relies on the call to Remove failing if the key doesn't exist. (JavaScript doesn't project objects as maps, so there is nothing to validate there.) Tightened the behavior of TryLookup and TryRemove so they propagate RPC failures. Because the inability to remove the item could be due to the server being unavailable, and that's not the same as the item not existing in the collection. Previous code treated loss of server the same as "The item doesn't exist", which is not true: The item could exist, we just were unable to contact the server to find out. * TryLookup and TryRemove should not be noexcept because they can throw on other errors. Added unit test to verify that errors other than "key not found" are propagated. Co-authored-by: Kenny Kerr --- cppwinrt/code_writers.h | 17 ++++++--- strings/base_collections_base.h | 9 ++++- strings/base_error.h | 12 ++++++ test/old_tests/UnitTests/TryLookup.cpp | 38 +++++++++++++++++++ test/old_tests/UnitTests/produce_map.cpp | 5 ++- .../UnitTests/single_threaded_map.cpp | 6 ++- 6 files changed, 75 insertions(+), 12 deletions(-) diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index ee082f717..c7f19d335 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -1305,12 +1305,12 @@ namespace cppwinrt else if (type_name == "Windows.Foundation.Collections.IMapView`2") { w.write(R"( - auto TryLookup(param_type const& key) const noexcept + auto TryLookup(param_type const& key) const { if constexpr (std::is_base_of_v) { V result{ nullptr }; - WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(result)); + impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(result))); return result; } else @@ -1318,7 +1318,7 @@ namespace cppwinrt std::optional result; V value{ empty_value() }; - if (0 == WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(value))) + if (0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(value)))) { result = std::move(value); } @@ -1331,12 +1331,12 @@ namespace cppwinrt else if (type_name == "Windows.Foundation.Collections.IMap`2") { w.write(R"( - auto TryLookup(param_type const& key) const noexcept + auto TryLookup(param_type const& key) const { if constexpr (std::is_base_of_v) { V result{ nullptr }; - WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(result)); + impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(result))); return result; } else @@ -1344,7 +1344,7 @@ namespace cppwinrt std::optional result; V value{ empty_value() }; - if (0 == WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(value))) + if (0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(value)))) { result = std::move(value); } @@ -1352,6 +1352,11 @@ namespace cppwinrt return result; } } + + auto TryRemove(param_type const& key) const + { + return 0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Remove(get_abi(key))); + } )"); } else if (type_name == "Windows.Foundation.IAsyncAction") diff --git a/strings/base_collections_base.h b/strings/base_collections_base.h index 641d4aadb..d798df991 100644 --- a/strings/base_collections_base.h +++ b/strings/base_collections_base.h @@ -1,4 +1,3 @@ - WINRT_EXPORT namespace winrt { template @@ -415,8 +414,14 @@ WINRT_EXPORT namespace winrt void Remove(K const& key) { + auto& container = static_cast(*this).get_container(); + auto found = container.find(static_cast(*this).wrap_value(key)); + if (found == container.end()) + { + throw hresult_out_of_bounds(); + } this->increment_version(); - static_cast(*this).get_container().erase(static_cast(*this).wrap_value(key)); + container.erase(found); } void Clear() noexcept diff --git a/strings/base_error.h b/strings/base_error.h index 20a13a6e1..630e1dd33 100644 --- a/strings/base_error.h +++ b/strings/base_error.h @@ -579,3 +579,15 @@ WINRT_EXPORT namespace winrt abort(); } } + +namespace winrt::impl +{ + inline hresult check_hresult_allow_bounds(hresult const result) + { + if (result != impl::error_out_of_bounds) + { + check_hresult(result); + } + return result; + } +} \ No newline at end of file diff --git a/test/old_tests/UnitTests/TryLookup.cpp b/test/old_tests/UnitTests/TryLookup.cpp index c03cf1104..47d2b5536 100644 --- a/test/old_tests/UnitTests/TryLookup.cpp +++ b/test/old_tests/UnitTests/TryLookup.cpp @@ -90,3 +90,41 @@ TEST_CASE("TryLookup") REQUIRE(map.TryLookup(123).value() == 456); } } + +TEST_CASE("TryRemove") +{ + auto map = single_threaded_map(std::map{ + { 123, nullptr }, + { 124, make(L"remove") }, + { 125, make(L"keep") }, + }); + + REQUIRE(map.TryRemove(122) == false); + REQUIRE(map.TryRemove(123) == true); + REQUIRE(map.TryRemove(124) == true); + + // Should still have one item left. + REQUIRE(map.Size() == 1); + REQUIRE(map.Lookup(125).ToString() == L"keep"); +} + +TEST_CASE("TryLookup TryRemove error") +{ + // Simulate a non-agile map that is being accessed from the wrong thread. + // "Try" operations should throw rather than erroneously report "not found". + // Because they didn't even try. The operation never got off the ground. + struct incorrectly_used_non_agile_map : implements> + { + int Lookup(int) { throw hresult_wrong_thread(); } + int32_t Size() { throw hresult_wrong_thread(); } + bool HasKey(int) { throw hresult_wrong_thread(); } + IMapView GetView() { throw hresult_wrong_thread(); } + bool Insert(int, int) { throw hresult_wrong_thread(); } + void Remove(int) { throw hresult_wrong_thread(); } + void Clear() { throw hresult_wrong_thread(); } + }; + + auto map = make(); + REQUIRE_THROWS_AS(map.TryLookup(123), hresult_wrong_thread); + REQUIRE_THROWS_AS(map.TryRemove(123), hresult_wrong_thread); +} \ No newline at end of file diff --git a/test/old_tests/UnitTests/produce_map.cpp b/test/old_tests/UnitTests/produce_map.cpp index f81267294..b456082a4 100644 --- a/test/old_tests/UnitTests/produce_map.cpp +++ b/test/old_tests/UnitTests/produce_map.cpp @@ -93,7 +93,7 @@ TEST_CASE("produce_IMap_int32_t_hstring") REQUIRE(m.Size() == 2); m.Remove(1); // existing REQUIRE(m.Size() == 1); - m.Remove(3); // not existing + REQUIRE_THROWS_AS(m.Remove(3), hresult_out_of_bounds); // not existing REQUIRE(m.Size() == 1); m.Clear(); @@ -177,7 +177,8 @@ TEST_CASE("produce_IMap_hstring_int32_t") REQUIRE(m.Size() == 2); m.Remove(L"one"); // existing REQUIRE(m.Size() == 1); - m.Remove(L"three"); // not existing + REQUIRE_THROWS_AS(m.Remove(L"three"), hresult_out_of_bounds); // not existing + REQUIRE(!m.TryRemove(L"three")); // not existing REQUIRE(m.Size() == 1); m.Clear(); diff --git a/test/old_tests/UnitTests/single_threaded_map.cpp b/test/old_tests/UnitTests/single_threaded_map.cpp index 44be94c08..ac58c9fa8 100644 --- a/test/old_tests/UnitTests/single_threaded_map.cpp +++ b/test/old_tests/UnitTests/single_threaded_map.cpp @@ -28,6 +28,7 @@ namespace values.Insert(2,20); values.Insert(3,30); IIterator> first = values.First(); + REQUIRE(!values.TryRemove(999)); // failed removal does not invalidate REQUIRE(first.HasCurrent()); [[maybe_unused]] auto pair = first.Current(); REQUIRE(first.MoveNext()); @@ -52,7 +53,8 @@ namespace REQUIRE(!values.Insert(2, 20)); compare(values, { { 1,100 }, {2,20} }); - values.Remove(3); + REQUIRE_THROWS_AS(values.Remove(3), hresult_out_of_bounds); + REQUIRE(!values.TryRemove(3)); compare(values, { { 1,100 },{ 2,20 } }); values.Remove(2); compare(values, { { 1,100 } }); @@ -65,7 +67,7 @@ namespace compare(values, {}); test_invalidation(values, [&] { values.Clear(); }); - test_invalidation(values, [&] { values.Remove(10); }); + test_invalidation(values, [&] { values.Remove(1); }); test_invalidation(values, [&] { values.Insert(1,10); }); } } From 8455a2a8cf6417f0b013a0485eb67ab863a34e08 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 9 Jun 2020 07:27:27 -0700 Subject: [PATCH 017/400] build --- natvis/pch.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/natvis/pch.h b/natvis/pch.h index 1a33d1b2b..d9f1813b1 100644 --- a/natvis/pch.h +++ b/natvis/pch.h @@ -20,11 +20,15 @@ #include "base_com_ptr.h" #include "base_string.h" #include "base_string_input.h" +#include "base_string_operators.h" #include "base_array.h" #include "base_weak_ref.h" #include "base_agile_ref.h" #include "base_error.h" #include "base_marshaler.h" +#include "base_delegate.h" +#include "base_events.h" +#include "base_activation.h" #include "base_implements.h" #include #include From 0a53d3c9873fe7460a3725d43f469dfde87be97c Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Thu, 11 Jun 2020 13:31:15 -0700 Subject: [PATCH 018/400] Set better default for references and project references to keep MdMerge happy. (#612) --- nuget/Microsoft.Windows.CppWinRT.props | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/nuget/Microsoft.Windows.CppWinRT.props b/nuget/Microsoft.Windows.CppWinRT.props index 6e1344f40..94392e36d 100644 --- a/nuget/Microsoft.Windows.CppWinRT.props +++ b/nuget/Microsoft.Windows.CppWinRT.props @@ -43,6 +43,20 @@ Copyright (C) Microsoft Corporation. All rights reserved. nul + + + false + true + + + + false + true + From 26f12959434319d870e20ca8e115c46d3ed4fc36 Mon Sep 17 00:00:00 2001 From: Johan Laanstra Date: Mon, 15 Jun 2020 10:17:53 -0700 Subject: [PATCH 019/400] Make disconnect_aware_handler ctor and move ctor noexcept. (#661) --- strings/base_coroutine_foundation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 8b22caa1f..fe4975b96 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -100,10 +100,10 @@ namespace winrt::impl struct disconnect_aware_handler { - disconnect_aware_handler(std::experimental::coroutine_handle<> handle) + disconnect_aware_handler(std::experimental::coroutine_handle<> handle) noexcept : m_handle(handle) { } - disconnect_aware_handler(disconnect_aware_handler&& other) + disconnect_aware_handler(disconnect_aware_handler&& other) noexcept : m_context(std::move(other.m_context)) , m_handle(std::exchange(other.m_handle, {})) { } From 8c0832fadd643f159a39e790a00a65d1bdef6331 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 15 Jun 2020 13:09:30 -0700 Subject: [PATCH 020/400] try_capture and try_create_instance (#663) --- strings/base_activation.h | 6 +++++ strings/base_com_ptr.h | 27 ++++++++++++++++++++ test/old_tests/UnitTests/capture.cpp | 24 +++++++++++++++++ test/old_tests/UnitTests/create_instance.cpp | 9 +++++++ 4 files changed, 66 insertions(+) diff --git a/strings/base_activation.h b/strings/base_activation.h index 7f35e1a79..685d9f5c0 100644 --- a/strings/base_activation.h +++ b/strings/base_activation.h @@ -487,6 +487,12 @@ WINRT_EXPORT namespace winrt impl::get_factory_cache().clear(); } + template + auto try_create_instance(guid const& clsid, uint32_t context = 0x1 /*CLSCTX_INPROC_SERVER*/, void* outer = nullptr) + { + return try_capture(WINRT_IMPL_CoCreateInstance, clsid, outer, context); + } + template auto create_instance(guid const& clsid, uint32_t context = 0x1 /*CLSCTX_INPROC_SERVER*/, void* outer = nullptr) { diff --git a/strings/base_com_ptr.h b/strings/base_com_ptr.h index 46867a73a..3ae9d88cd 100644 --- a/strings/base_com_ptr.h +++ b/strings/base_com_ptr.h @@ -153,6 +153,18 @@ WINRT_EXPORT namespace winrt *other = m_ptr; } + template + bool try_capture(F function, Args&&...args) + { + return function(args..., guid_of(), put_void()) >= 0; + } + + template + bool try_capture(com_ptr const& object, M method, Args&&...args) + { + return (object.get()->*(method))(args..., guid_of(), put_void()) >= 0; + } + template void capture(F function, Args&&...args) { @@ -204,6 +216,21 @@ WINRT_EXPORT namespace winrt type* m_ptr{}; }; + template + impl::com_ref try_capture(F function, Args&& ...args) + { + void* result{}; + function(args..., guid_of(), &result); + return { result, take_ownership_from_abi }; + } + + template + impl::com_ref try_capture(com_ptr const& object, M method, Args&& ...args) + { + void* result{}; + (object.get()->*(method))(args..., guid_of(), &result); + return { result, take_ownership_from_abi }; + } template impl::com_ref capture(F function, Args&& ...args) { diff --git a/test/old_tests/UnitTests/capture.cpp b/test/old_tests/UnitTests/capture.cpp index e88a29056..d1c4e41de 100644 --- a/test/old_tests/UnitTests/capture.cpp +++ b/test/old_tests/UnitTests/capture.cpp @@ -58,3 +58,27 @@ TEST_CASE("capture") REQUIRE_THROWS_AS(capture(a, &ICapture::CreateMemberCapture, 0), hresult_no_interface); REQUIRE_THROWS_AS(d.capture(a, &ICapture::CreateMemberCapture, 0), hresult_no_interface); } + +TEST_CASE("try_capture") +{ + // Identical to the "capture" test above, just with different + // error handling. + com_ptr a = try_capture(CreateCapture, 10); + REQUIRE(a->GetValue() == 10); + a = nullptr; + REQUIRE(a.try_capture(CreateCapture, 20)); + REQUIRE(a->GetValue() == 20); + + auto b = try_capture(a, &ICapture::CreateMemberCapture, 30); + REQUIRE(b->GetValue() == 30); + b = nullptr; + REQUIRE(b.try_capture(a, &ICapture::CreateMemberCapture, 40)); + REQUIRE(b->GetValue() == 40); + + com_ptr d; + + REQUIRE(!try_capture(CreateCapture, 0)); + REQUIRE(!d.try_capture(CreateCapture, 0)); + REQUIRE(!try_capture(a, &ICapture::CreateMemberCapture, 0)); + REQUIRE(!d.try_capture(a, &ICapture::CreateMemberCapture, 0)); +} diff --git a/test/old_tests/UnitTests/create_instance.cpp b/test/old_tests/UnitTests/create_instance.cpp index 38f1bf031..fa1c2ee53 100644 --- a/test/old_tests/UnitTests/create_instance.cpp +++ b/test/old_tests/UnitTests/create_instance.cpp @@ -9,3 +9,12 @@ TEST_CASE("create_instance") com_ptr dialog = create_instance(guid_of()); REQUIRE(dialog); } + +TEST_CASE("try_create_instance") +{ + com_ptr dialog = try_create_instance(guid_of()); + REQUIRE(dialog); + + dialog = try_create_instance(CLSID_NULL); + REQUIRE(!dialog); +} From a0b18895b0b93ee0449acbf1073bf942d61ceadf Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 15 Jun 2020 23:24:17 -0700 Subject: [PATCH 021/400] Resuming neutral context from STA should force background thread (#662) --- strings/base_coroutine_foundation.h | 4 +- strings/base_coroutine_threadpool.h | 99 ++++++++++++++----- .../old_tests/UnitTests/apartment_context.cpp | 49 +++++++++ test/test/await_adapter.cpp | 22 +++-- 4 files changed, 138 insertions(+), 36 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index fe4975b96..311522a6c 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -35,7 +35,7 @@ namespace winrt::impl { // Note: A blocking wait on the UI thread for an asynchronous operation can cause a deadlock. // See https://docs.microsoft.com/windows/uwp/cpp-and-winrt-apis/concurrency#block-the-calling-thread - WINRT_ASSERT(!is_sta()); + WINRT_ASSERT(!is_sta_thread()); } template @@ -119,7 +119,7 @@ namespace winrt::impl private: std::experimental::coroutine_handle<> m_handle; - com_ptr m_context = apartment_context(); + resume_apartment_context m_context; void Complete() { diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index 254840dc2..ae26677ef 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -1,6 +1,14 @@ namespace winrt::impl { + inline auto submit_threadpool_callback(void(__stdcall* callback)(void*, void* context), void* context) + { + if (!WINRT_IMPL_TrySubmitThreadpoolCallback(callback, context, nullptr)) + { + throw_last_error(); + } + } + inline void __stdcall resume_background_callback(void*, void* context) noexcept { std::experimental::coroutine_handle<>::from_address(context)(); @@ -8,30 +16,43 @@ namespace winrt::impl inline auto resume_background(std::experimental::coroutine_handle<> handle) { - if (!WINRT_IMPL_TrySubmitThreadpoolCallback(resume_background_callback, handle.address(), nullptr)) - { - throw_last_error(); - } + submit_threadpool_callback(resume_background_callback, handle.address()); } - inline bool is_sta() noexcept + inline std::pair get_apartment_type() noexcept { int32_t aptType; int32_t aptTypeQualifier; - return (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) && ((aptType == 0 /*APTTYPE_STA*/) || (aptType == 3 /*APTTYPE_MAINSTA*/)); + if (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) + { + return { aptType, aptTypeQualifier }; + } + else + { + return { 1 /* APTTYPE_MTA */, 1 /* APTTYPEQUALIFIER_IMPLICIT_MTA */ }; + } } - inline bool requires_apartment_context() noexcept + inline bool is_sta_thread() noexcept { - int32_t aptType; - int32_t aptTypeQualifier; - return (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) && ((aptType == 0 /*APTTYPE_STA*/) || (aptType == 2 /*APTTYPE_NA*/) || (aptType == 3 /*APTTYPE_MAINSTA*/)); + auto type = get_apartment_type(); + switch (type.first) + { + case 0: /* APTTYPE_STA */ + case 3: /* APTTYPE_MAINSTA */ + return true; + case 2: /* APTTYPE_NA */ + return type.second == 3 /* APTTYPEQUALIFIER_NA_ON_STA */ || + type.second == 5 /* APTTYPEQUALIFIER_NA_ON_MAINSTA */; + } + return false; } - inline auto apartment_context() + struct resume_apartment_context { - return requires_apartment_context() ? capture(WINRT_IMPL_CoGetObjectContext) : nullptr; - } + com_ptr m_context = try_capture(WINRT_IMPL_CoGetObjectContext); + int32_t m_context_type = get_apartment_type().first; + }; inline int32_t __stdcall resume_apartment_callback(com_callback_args* args) noexcept { @@ -39,25 +60,49 @@ namespace winrt::impl return 0; }; - inline auto resume_apartment(com_ptr const& context, std::experimental::coroutine_handle<> handle) + inline void resume_apartment_sync(com_ptr const& context, std::experimental::coroutine_handle<> handle) + { + com_callback_args args{}; + args.data = handle.address(); + + check_hresult(context->ContextCallback(resume_apartment_callback, &args, guid_of(), 5, nullptr)); + } + + inline void resume_apartment_on_threadpool(com_ptr const& context, std::experimental::coroutine_handle<> handle) { - if (context) + struct threadpool_resume { - com_callback_args args{}; - args.data = handle.address(); + threadpool_resume(com_ptr const& context, std::experimental::coroutine_handle<> handle) : + m_context(context), m_handle(handle) { } + com_ptr m_context; + std::experimental::coroutine_handle<> m_handle; + }; + auto state = std::make_unique(context, handle); + submit_threadpool_callback([](void*, void* p) + { + std::unique_ptr state{ static_cast(p) }; + resume_apartment_sync(state->m_context, state->m_handle); + }, state.get()); + state.release(); + } - check_hresult(context->ContextCallback(resume_apartment_callback, &args, guid_of(), 5, nullptr)); + inline auto resume_apartment(resume_apartment_context const& context, std::experimental::coroutine_handle<> handle) + { + if ((context.m_context == nullptr) || (context.m_context == try_capture(WINRT_IMPL_CoGetObjectContext))) + { + handle(); + } + else if (context.m_context_type == 1 /* APTTYPE_MTA */) + { + resume_background(handle); + } + else if ((context.m_context_type == 2 /* APTTYPE_NTA */) && is_sta_thread()) + { + resume_apartment_on_threadpool(context.m_context, handle); } else { - if (requires_apartment_context()) - { - resume_background(handle); - } - else - { - handle(); - } + resume_apartment_sync(context.m_context, handle); } } @@ -294,7 +339,7 @@ WINRT_EXPORT namespace winrt impl::resume_apartment(context, handle); } - com_ptr context = impl::apartment_context(); + impl::resume_apartment_context context; }; [[nodiscard]] inline auto resume_after(Windows::Foundation::TimeSpan duration) noexcept diff --git a/test/old_tests/UnitTests/apartment_context.cpp b/test/old_tests/UnitTests/apartment_context.cpp index eab146973..ae3511698 100644 --- a/test/old_tests/UnitTests/apartment_context.cpp +++ b/test/old_tests/UnitTests/apartment_context.cpp @@ -1,8 +1,10 @@ #include "pch.h" #include "catch.hpp" +#include using namespace winrt; using namespace Windows::Foundation; +using namespace Windows::System; namespace { @@ -12,9 +14,56 @@ namespace co_await context; } + + template + void InvokeInContext(IContextCallback* context, TLambda&& lambda) + { + ComCallData data; + data.pUserDefined = λ + check_hresult(context->ContextCallback([](ComCallData* data) -> HRESULT + { + auto& lambda = *reinterpret_cast(data->pUserDefined); + lambda(); + return S_OK; + }, &data, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr)); + } + + auto get_winrt_apartment_context_for_com_context(com_ptr<::IContextCallback> const& com_context) + { + std::optional context; + InvokeInContext(com_context.get(), [&] { + context = apartment_context(); + }); + return context.value(); + } + + bool is_nta_on_mta() + { + APTTYPE type; + APTTYPEQUALIFIER qualifier; + check_hresult(CoGetApartmentType(&type, &qualifier)); + return (type == APTTYPE_NA) && (qualifier == APTTYPEQUALIFIER_NA_ON_MTA || qualifier == APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA); + } + + IAsyncAction TestNeutralApartmentContext() + { + auto controller = DispatcherQueueController::CreateOnDedicatedThread(); + co_await resume_foreground(controller.DispatcherQueue()); + + // Entering neutral apartment from STA should resume on explicit background thread. + auto nta = get_winrt_apartment_context_for_com_context(capture<::IContextCallback>(CoGetDefaultContext, APTTYPE_NA)); + co_await nta; + + REQUIRE(is_nta_on_mta()); + } } TEST_CASE("apartment_context coverage") { Async().get(); } + +TEST_CASE("apartment_context nta") +{ + TestNeutralApartmentContext().get(); +} diff --git a/test/test/await_adapter.cpp b/test/test/await_adapter.cpp index 65018d33b..16575699b 100644 --- a/test/test/await_adapter.cpp +++ b/test/test/await_adapter.cpp @@ -8,6 +8,14 @@ using namespace Windows::System; namespace { + bool is_sta() + { + APTTYPE type; + APTTYPEQUALIFIER qualifier; + check_hresult(CoGetApartmentType(&type, &qualifier)); + return (type == APTTYPE_STA) || (type == APTTYPE_MAINSTA); + } + static handle signal{ CreateEventW(nullptr, false, false, nullptr) }; IAsyncAction OtherForegroundAsync() @@ -29,9 +37,9 @@ namespace IAsyncAction ForegroundAsync(DispatcherQueue dispatcher) { - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); co_await resume_foreground(dispatcher); - REQUIRE(impl::is_sta()); + REQUIRE(is_sta()); // This exercises one STA thread waiting on another thus one context callback // completing on another. @@ -48,9 +56,9 @@ namespace fire_and_forget SignalFromForeground(DispatcherQueue dispatcher) { - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); co_await resume_foreground(dispatcher); - REQUIRE(impl::is_sta()); + REQUIRE(is_sta()); // Previously, this signal was never raised because the foreground thread // was always blocked waiting for ContextCallback to return. @@ -61,19 +69,19 @@ namespace { // Switch to a background (MTA) thread. co_await resume_background(); - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); // This exercises one MTA thread waiting on another and just completing // directly without the overhead of a context switch. co_await OtherBackgroundAsync(); - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); // Wait for a coroutine that completes on a foreground (STA) thread. co_await ForegroundAsync(dispatcher); // Resumption should automatically switch to a background (MTA) thread // without blocking the Completed handler (which would in turn block the foreground thread). - REQUIRE(!impl::is_sta()); + REQUIRE(!is_sta()); // Attempt to signal from the foreground thread under the assumption // that the foreground thread is not blocked. From 9836238bfd7b1362242bf55e143b1992538bf2a0 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Tue, 16 Jun 2020 22:59:07 -0700 Subject: [PATCH 022/400] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ca9f70ac..09a03c0c8 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,6 @@ C++/WinRT is an entirely standard C++ language projection for Windows Runtime (W * Visual Studio extension: http://aka.ms/cppwinrt/vsix * Wikipedia: https://en.wikipedia.org/wiki/C++/WinRT -C++/WinRT is part of the [xlang](https://github.com/microsoft/xlang) family of projects that help developers create APIs that can run on multiple platforms and be used with a variety of languages. - # Building C++/WinRT Don't build C++/WinRT yourself - just download the latest version here: https://aka.ms/cppwinrt/nuget @@ -36,3 +34,9 @@ provided by the bot. You will only need to do this once across all repos using o This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +# Project Reunion + +Project Reunion is a set of libraries, frameworks, components, and tools that you can use to access powerful Windows platform functionality from all kinds of apps on many versions of Windows. Project Reunion combines the power of Win32 native applications alongside modern APIs, so your apps light up everywhere your users are. + +Other Project Reunion components include [WinUI](https://github.com/microsoft/microsoft-ui-xaml), WebView2, MSIX, [Rust/WinRT](https://github.com/microsoft/winrt-rs), and [C#/WinRT](https://github.com/microsoft/cswinrt). If you'd like to learn more, contribute to Project Reunion, or have app model questions, visit [Project Reunion on GitHub](https://github.com/microsoft/ProjectReunion). From 276b2f56941213ac3362167d1c4a92ff68bc3469 Mon Sep 17 00:00:00 2001 From: David Fields Date: Fri, 19 Jun 2020 16:21:21 -0700 Subject: [PATCH 023/400] Make array_view(pointer, size) constructor public (#666) --- strings/base_array.h | 10 +++++----- test/old_tests/UnitTests/array.cpp | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/strings/base_array.h b/strings/base_array.h index eb3fa540b..5f0904efe 100644 --- a/strings/base_array.h +++ b/strings/base_array.h @@ -17,6 +17,11 @@ WINRT_EXPORT namespace winrt array_view() noexcept = default; + array_view(pointer data, size_type size) noexcept : + m_data(data), + m_size(size) + {} + array_view(pointer first, pointer last) noexcept : m_data(first), m_size(static_cast(last - first)) @@ -192,11 +197,6 @@ WINRT_EXPORT namespace winrt protected: - array_view(pointer data, size_type size) noexcept : - m_data(data), - m_size(size) - {} - pointer m_data{ nullptr }; size_type m_size{ 0 }; diff --git a/test/old_tests/UnitTests/array.cpp b/test/old_tests/UnitTests/array.cpp index aa13b8018..95a387822 100644 --- a/test/old_tests/UnitTests/array.cpp +++ b/test/old_tests/UnitTests/array.cpp @@ -126,6 +126,24 @@ TEST_CASE("custom,DataReader") REQUIRE(3 == a[2]); } +// +// This test illustrates an array_view (non-const) bound to a raw buffer +// +TEST_CASE("buffer,DataReader") +{ + auto reader = CreateDataReader({ 1, 2, 3 }).get(); + + std::array a; + byte* ptr = a.data(); + auto size = a.size(); + reader.ReadBytes({ ptr, static_cast(size) }); + + REQUIRE(3 == a.size()); + REQUIRE(1 == a[0]); + REQUIRE(2 == a[1]); + REQUIRE(3 == a[2]); +} + // // This test illustrates receiving an IVector and calling GetMany to fill an array. // @@ -1259,6 +1277,7 @@ TEST_CASE("array_view,ctad") uint8_t a[3]{}; REQUIRE_DEDUCED_AS(uint8_t, &a[0], &a[0]); + REQUIRE_DEDUCED_AS(uint8_t, &a[0], 3); REQUIRE_DEDUCED_AS(uint8_t, a); std::array ar{}; From 4fa0e403f55eaf9d4c6182f1496d41cce585cd6e Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Thu, 25 Jun 2020 09:13:43 -0700 Subject: [PATCH 024/400] clang10 (#669) --- strings/base_coroutine_foundation.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 311522a6c..43a8b16bd 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -118,8 +118,8 @@ namespace winrt::impl } private: - std::experimental::coroutine_handle<> m_handle; resume_apartment_context m_context; + std::experimental::coroutine_handle<> m_handle; void Complete() { @@ -743,7 +743,7 @@ WINRT_EXPORT namespace winrt auto [delegate, shared] = impl::make_delegate_with_shared_state>(shared_type{}); - auto completed = [&](T const& async) + auto completed = [delegate = std::move(delegate)](T const& async) { async.Completed(delegate); }; From f67e55ac609450c5e65558c6975b9aa89b0a07c8 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Sun, 28 Jun 2020 08:33:04 -0700 Subject: [PATCH 025/400] Don't destroy the IAsyncInfo from inside Completed handler (#671) --- strings/base_coroutine_foundation.h | 1 + test/test/async_completed.cpp | 66 +++++++++++++++++++++++++++++ test/test/test.vcxproj | 1 + 3 files changed, 68 insertions(+) create mode 100644 test/test/async_completed.cpp diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 43a8b16bd..797070737 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -140,6 +140,7 @@ namespace winrt::impl void await_suspend(std::experimental::coroutine_handle<> handle) { + auto extend_lifetime = async; async.Completed([this, handler = disconnect_aware_handler{ handle }](auto&&, auto operation_status) mutable { status = operation_status; diff --git a/test/test/async_completed.cpp b/test/test/async_completed.cpp new file mode 100644 index 000000000..6b784e451 --- /dev/null +++ b/test/test/async_completed.cpp @@ -0,0 +1,66 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that awaiting an already-completed async operation + // does not destroy the operation from within the Completed handler. + // The Completed handler may run synchronously, and destroying the + // operation from within the Completed handler pulls the rug out + // from under the operation! + // + struct already_completed : implements + { + void Completed(AsyncActionCompletedHandler const& complete) + { + auto self = get_weak(); + complete(*this, AsyncStatus::Completed); + REQUIRE(self.get() != nullptr); + } + + auto Completed() const noexcept + { + return nullptr; + } + + uint32_t Id() const noexcept + { + return 1; + } + + AsyncStatus Status() const noexcept + { + return AsyncStatus::Completed; + } + + hresult ErrorCode() const noexcept + { + return 0; + } + + void GetResults() const noexcept + { + } + + void Cancel() const noexcept + { + } + + void Close() const noexcept + { + } + }; + + IAsyncAction TestCompleted() + { + co_await make(); + } +} + +TEST_CASE("async_completed") +{ + TestCompleted().get(); +} diff --git a/test/test/test.vcxproj b/test/test/test.vcxproj index de9f484ff..1b4cc6cc5 100644 --- a/test/test/test.vcxproj +++ b/test/test/test.vcxproj @@ -293,6 +293,7 @@ + From f48d7a664f48e29bdd9d76a247c69c8f3c8f9d4c Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 29 Jun 2020 14:58:39 -0700 Subject: [PATCH 026/400] make progress_token::operator() const so it can be captured by lambdas (#673) --- strings/base_coroutine_foundation.h | 2 +- test/test/async_progress.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 797070737..84fe8bc81 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -305,7 +305,7 @@ namespace winrt::impl return *this; } - void operator()(Progress const& result) + void operator()(Progress const& result) const { m_promise->set_progress(result); } diff --git a/test/test/async_progress.cpp b/test/test/async_progress.cpp index 0a2790f50..a1ef121c8 100644 --- a/test/test/async_progress.cpp +++ b/test/test/async_progress.cpp @@ -19,8 +19,12 @@ namespace IAsyncOperationWithProgress Operation(HANDLE event) { co_await resume_on_signal(event); - auto progress = co_await get_progress_token(); - progress(123); + + // Invoke from a lambda to ensure that operator() is const. + [progress = co_await get_progress_token()]() + { + progress(123); + }(); co_return 1; } From fba255d96db18096bbefd7e1a8b1133561056617 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 29 Jun 2020 15:35:02 -0700 Subject: [PATCH 027/400] Cancellation token improvements (#674) --- strings/base_coroutine_foundation.h | 7 +++++-- test/test/async_auto_cancel.cpp | 14 ++++++++++++++ test/test/async_cancel_callback.cpp | 17 ++++++++++------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 84fe8bc81..a70b4d0c5 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -273,7 +273,7 @@ namespace winrt::impl return m_promise->Status() == Windows::Foundation::AsyncStatus::Canceled; } - void callback(winrt::delegate<>&& cancel) noexcept + void callback(winrt::delegate<>&& cancel) const noexcept { m_promise->cancellation_callback(std::move(cancel)); } @@ -559,7 +559,10 @@ namespace winrt::impl } } - cancel(); + if (cancel) + { + cancel(); + } } #if defined(_DEBUG) && !defined(WINRT_NO_MAKE_DETECTION) diff --git a/test/test/async_auto_cancel.cpp b/test/test/async_auto_cancel.cpp index 45ff30fcf..2b1fdc728 100644 --- a/test/test/async_auto_cancel.cpp +++ b/test/test/async_auto_cancel.cpp @@ -39,6 +39,19 @@ namespace co_return 1; } + IAsyncAction ActionForceAutoCancel(HANDLE event) + { + co_await resume_on_signal(event); + + // Null out the callback to indicate that we want to cancel + // any existing cancellation callback and rely on auto-cancel. + auto cancel = co_await get_cancellation_token(); + cancel.callback(nullptr); + + co_await std::experimental::suspend_never(); + REQUIRE(false); + } + template void Check(F make) { @@ -70,4 +83,5 @@ TEST_CASE("async_auto_cancel") Check(ActionWithProgress); Check(Operation); Check(OperationWithProgress); + Check(ActionForceAutoCancel); } diff --git a/test/test/async_cancel_callback.cpp b/test/test/async_cancel_callback.cpp index 0ab5b6bb1..a69575a88 100644 --- a/test/test/async_cancel_callback.cpp +++ b/test/test/async_cancel_callback.cpp @@ -11,13 +11,16 @@ namespace IAsyncAction Action(HANDLE event, bool& canceled) { - auto cancel = co_await get_cancellation_token(); - - cancel.callback([&] - { - REQUIRE(!canceled); - canceled = true; - }); + // Put the cancellation token into a lambda just to make + // sure it's possible. + [cancel = co_await get_cancellation_token(), &canceled] + { + cancel.callback([&] + { + REQUIRE(!canceled); + canceled = true; + }); + }(); co_await resume_on_signal(event); co_await std::experimental::suspend_never(); From aeb78bbd6dd3c1c54d0983762e56e61daa87b14f Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Tue, 30 Jun 2020 11:22:18 -0700 Subject: [PATCH 028/400] Fix concat_hstring for 0-length string (#675) --- strings/base_string_operators.h | 7 ++++++- test/old_tests/UnitTests/hstring.cpp | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/strings/base_string_operators.h b/strings/base_string_operators.h index 284cc1118..215186b04 100644 --- a/strings/base_string_operators.h +++ b/strings/base_string_operators.h @@ -98,7 +98,12 @@ namespace winrt::impl { inline hstring concat_hstring(std::wstring_view const& left, std::wstring_view const& right) { - hstring_builder text(static_cast(left.size() + right.size())); + auto size = static_cast(left.size() + right.size()); + if (size == 0) + { + return{}; + } + hstring_builder text(size); memcpy_s(text.data(), left.size() * sizeof(wchar_t), left.data(), left.size() * sizeof(wchar_t)); memcpy_s(text.data() + left.size(), right.size() * sizeof(wchar_t), right.data(), right.size() * sizeof(wchar_t)); return text.to_hstring(); diff --git a/test/old_tests/UnitTests/hstring.cpp b/test/old_tests/UnitTests/hstring.cpp index 38ef1e3ae..1369fd229 100644 --- a/test/old_tests/UnitTests/hstring.cpp +++ b/test/old_tests/UnitTests/hstring.cpp @@ -570,4 +570,7 @@ TEST_CASE("hstring, concat") REQUIRE(hstring() + s == L"abc"); REQUIRE(s + L"" == L"abc"); REQUIRE(L"" + s == L"abc"); + + REQUIRE(hstring() + hstring() == L""); + REQUIRE(get_abi(hstring() + hstring()) == nullptr); } From 61a55bc774cfd50692c6b8bed4305ee53b04fa7a Mon Sep 17 00:00:00 2001 From: Pedro Miguel Justo <40605312+pmsjt@users.noreply.github.com> Date: Fri, 3 Jul 2020 16:23:55 -0700 Subject: [PATCH 029/400] Fix CFG helper calling convention and simplify flow of call target address. (#678) --- fast_fwd/arm64/thunks.asm | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/fast_fwd/arm64/thunks.asm b/fast_fwd/arm64/thunks.asm index be128ecac..0fe5252a0 100644 --- a/fast_fwd/arm64/thunks.asm +++ b/fast_fwd/arm64/thunks.asm @@ -11,10 +11,9 @@ NESTED_ENTRY InvokeForwarder ; Save enregistered args - PROLOG_SAVE_REG_PAIR fp, lr, #-64! - PROLOG_SAVE_REG_PAIR x19, x20, #16 - PROLOG_NOP stp x0, x1, [sp, #32] - PROLOG_NOP stp x2, x3, [sp, #48] + PROLOG_SAVE_REG_PAIR fp, lr, #-48! + PROLOG_NOP stp x0, x1, [sp, #16] + PROLOG_NOP stp x2, x3, [sp, #32] ; Replace forwarder abi with owner abi ldr x1, [x0, #8] @@ -26,23 +25,20 @@ ; Get method address from owner abi vtable ldr x0, [x1] - ldr x19, [x0, x12, lsl #3] - mov x0, x19 + ldr x15, [x0, x12, lsl #3] ; Verify indirect call target adrp x12, __guard_check_icall_fptr ldr x12, [x12, __guard_check_icall_fptr] blr x12 - ; Restore method address, return address, and args - mov x12, x19 - EPILOG_NOP ldp x2, x3, [sp, #48] - EPILOG_NOP ldp x0, x1, [sp, #32] - EPILOG_RESTORE_REG_PAIR x19, x20, #16 - EPILOG_RESTORE_REG_PAIR fp, lr, #64! + ; Restore return address, and args + EPILOG_NOP ldp x2, x3, [sp, #32] + EPILOG_NOP ldp x0, x1, [sp, #16] + EPILOG_RESTORE_REG_PAIR fp, lr, #48! ; Jump to method - EPILOG_NOP br x12 + EPILOG_NOP br x15 NESTED_END InvokeForwarder From 3689d858826ea3b5fad7fe15ff9bdcb962436f6e Mon Sep 17 00:00:00 2001 From: Jon Wiswall Date: Fri, 3 Jul 2020 16:24:24 -0700 Subject: [PATCH 030/400] Add SDKReference-sourced WinMDs when building (#679) --- build_nuget.cmd | 2 +- nuget/Microsoft.Windows.CppWinRT.nuspec | 2 +- nuget/Microsoft.Windows.CppWinRT.targets | 3 ++- nuget/readme.md | 11 +++++++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/build_nuget.cmd b/build_nuget.cmd index 068e1dfd0..5c5c60c6c 100644 --- a/build_nuget.cmd +++ b/build_nuget.cmd @@ -10,4 +10,4 @@ call msbuild /m /p:Configuration=Release,Platform=arm64,CppWinRTBuildVersion=%ta call msbuild /m /p:Configuration=Release,Platform=x86,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:cppwinrt -nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib +nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties target_version=%target_version%;cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib diff --git a/nuget/Microsoft.Windows.CppWinRT.nuspec b/nuget/Microsoft.Windows.CppWinRT.nuspec index a49f1eb8f..882931734 100644 --- a/nuget/Microsoft.Windows.CppWinRT.nuspec +++ b/nuget/Microsoft.Windows.CppWinRT.nuspec @@ -2,7 +2,7 @@ Microsoft.Windows.CppWinRT - 1.0.0.0 + $target_version$ C++/WinRT Build Support Microsoft Microsoft diff --git a/nuget/Microsoft.Windows.CppWinRT.targets b/nuget/Microsoft.Windows.CppWinRT.targets index a04e96a87..f5ee7929c 100644 --- a/nuget/Microsoft.Windows.CppWinRT.targets +++ b/nuget/Microsoft.Windows.CppWinRT.targets @@ -248,11 +248,12 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_CppWinRTDirectWinMDReferences Remove="@(_CppWinRTDirectWinMDReferences)" /> <_CppWinRTDirectWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference'" /> + <_CppWinRTDirectWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ExpandSDKReference'" /> %(FullPath) diff --git a/nuget/readme.md b/nuget/readme.md index 696bf1b76..8dbb9931f 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -94,3 +94,14 @@ Example: For more complex analysis of build errors, the [MSBuild Binary and Structured Log Viewer](http://msbuildlog.com/) is highly recommended. +## Building, Testing + +Be sure to get the latest nuget.exe from [nuget.org](https://www.nuget.org/downloads) and place it in your path. + +Build the package by running [build_nuget.cmd](../build_nuget.cmd) from a developer environment command line. For testing pass a version number that is much higher than your currently installed, like: + +``` +c:\repos\cppwinrt> .\build_nuget.cmd 5.0.0.0 +``` + +Add the cppwinrt repo directory as a nuget source location and update your projects' references to point at it, update project references, then rebuild a test/sample project. From 8f7fab26e0bb6a5f7189865c657ec5e0c385131a Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Fri, 3 Jul 2020 18:34:57 -0700 Subject: [PATCH 031/400] Fix build --- build_nuget.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_nuget.cmd b/build_nuget.cmd index 5c5c60c6c..068e1dfd0 100644 --- a/build_nuget.cmd +++ b/build_nuget.cmd @@ -10,4 +10,4 @@ call msbuild /m /p:Configuration=Release,Platform=arm64,CppWinRTBuildVersion=%ta call msbuild /m /p:Configuration=Release,Platform=x86,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:cppwinrt -nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties target_version=%target_version%;cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib +nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib From 11abc6e7f4ebdde63acab937ab8382b7bef94815 Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Fri, 3 Jul 2020 18:35:31 -0700 Subject: [PATCH 032/400] Fix build --- nuget/Microsoft.Windows.CppWinRT.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nuget/Microsoft.Windows.CppWinRT.nuspec b/nuget/Microsoft.Windows.CppWinRT.nuspec index 882931734..a49f1eb8f 100644 --- a/nuget/Microsoft.Windows.CppWinRT.nuspec +++ b/nuget/Microsoft.Windows.CppWinRT.nuspec @@ -2,7 +2,7 @@ Microsoft.Windows.CppWinRT - $target_version$ + 1.0.0.0 C++/WinRT Build Support Microsoft Microsoft From 070820f0e05266618ab64102417748ea774b5c77 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 6 Jul 2020 09:32:04 -0700 Subject: [PATCH 033/400] Support coroutines returning references (#682) --- strings/base_coroutine_threadpool.h | 6 +-- test/test/async_ref_result.cpp | 65 +++++++++++++++++++++++++++++ test/test/test.vcxproj | 1 + 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 test/test/async_ref_result.cpp diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index ae26677ef..acb03624f 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -166,7 +166,7 @@ namespace winrt::impl return free_await_adapter_impl{ static_cast(awaitable) }.suspend(handle); } - auto await_resume() + decltype(auto) await_resume() { return free_await_adapter_impl{ static_cast(awaitable) }.resume(); } @@ -188,7 +188,7 @@ namespace winrt::impl return awaitable.await_suspend(handle); } - auto await_resume() + decltype(auto) await_resume() { return awaitable.await_resume(); } @@ -243,7 +243,7 @@ namespace winrt::impl return awaitable.await_suspend(handle); } - auto await_resume() + decltype(auto) await_resume() { if (winrt_resume_handler) { diff --git a/test/test/async_ref_result.cpp b/test/test/async_ref_result.cpp new file mode 100644 index 000000000..015bb7b31 --- /dev/null +++ b/test/test/async_ref_result.cpp @@ -0,0 +1,65 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace +{ + // + // Checks that references returned by awaitables + // are not accidentally decayed. + // + // This test "runs" at compile time via static_assert. + + template + struct awaitable : std::experimental::suspend_never + { + std::decay_t value; + T await_resume() { return static_cast(value); } + }; + + template + struct awaitable_member_awaiter : std::experimental::suspend_never + { + decltype(auto) get_awaiter() { return *this; } + std::decay_t value; + T await_resume() { return static_cast(value); } + }; + + template + struct awaitable_free_awaiter : std::experimental::suspend_never + { + std::decay_t value; + T await_resume() { return static_cast(value); } + }; + template + decltype(auto) get_awaiter(awaitable_free_awaiter&& value) { return std::move(value); } + + template typename A, typename T> + IAsyncAction Check() + { + decltype(auto) value = co_await A(); + static_assert(std::is_same_v); + } + + template typename A> + IAsyncAction Check() + { + co_await Check(); + co_await Check(); + co_await Check(); + co_await Check(); + } + + IAsyncAction Test() + { + co_await Check(); + co_await Check(); + co_await Check(); + } +} + +TEST_CASE("async_ref_result") +{ + Test().get(); +} diff --git a/test/test/test.vcxproj b/test/test/test.vcxproj index 1b4cc6cc5..f29835de6 100644 --- a/test/test/test.vcxproj +++ b/test/test/test.vcxproj @@ -294,6 +294,7 @@ + From 2e05e58d1a5bbed8f1e71e25f427b5f7242f011f Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Thu, 9 Jul 2020 07:37:03 -0700 Subject: [PATCH 034/400] Improve detection of mismatched header versions (#683) --- cppwinrt/code_writers.h | 3 ++- cppwinrt/cppwinrt.vcxproj | 1 + cppwinrt/cppwinrt.vcxproj.filters | 3 +++ cppwinrt/file_writers.h | 3 ++- strings/base_version.h | 6 ++++-- strings/base_version_odr.h | 2 ++ 6 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 strings/base_version_odr.h diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index c7f19d335..b44516d60 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -25,8 +25,9 @@ namespace cppwinrt { w.write_root_include("base"); auto format = R"(static_assert(winrt::check_version(CPPWINRT_VERSION, "%"), "Mismatched C++/WinRT headers."); +#define CPPWINRT_VERSION "%" )"; - w.write(format, CPPWINRT_VERSION_STRING); + w.write(format, CPPWINRT_VERSION_STRING, CPPWINRT_VERSION_STRING); } static void write_include_guard(writer& w) diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index 871316366..6055d3101 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -81,6 +81,7 @@ + diff --git a/cppwinrt/cppwinrt.vcxproj.filters b/cppwinrt/cppwinrt.vcxproj.filters index 069061a3b..21fabf0f2 100644 --- a/cppwinrt/cppwinrt.vcxproj.filters +++ b/cppwinrt/cppwinrt.vcxproj.filters @@ -136,6 +136,9 @@ strings + + strings + strings diff --git a/cppwinrt/file_writers.h b/cppwinrt/file_writers.h index 42fb86556..db81fddf9 100644 --- a/cppwinrt/file_writers.h +++ b/cppwinrt/file_writers.h @@ -6,6 +6,7 @@ namespace cppwinrt { writer w; write_preamble(w); + w.write(strings::base_version_odr, CPPWINRT_VERSION_STRING); write_open_file_guard(w, "BASE"); w.write(strings::base_includes); @@ -38,7 +39,7 @@ namespace cppwinrt w.write(strings::base_std_hash); w.write(strings::base_coroutine_threadpool); w.write(strings::base_natvis); - w.write(strings::base_version, CPPWINRT_VERSION_STRING); + w.write(strings::base_version); write_endif(w); w.flush_to_file(settings.output_folder + "winrt/base.h"); diff --git a/strings/base_version.h b/strings/base_version.h index 865792b82..227f10402 100644 --- a/strings/base_version.h +++ b/strings/base_version.h @@ -1,6 +1,4 @@ -#define CPPWINRT_VERSION "%" - // WINRT_version is used by Microsoft to analyze C++/WinRT library adoption and inform future product decisions. extern "C" __declspec(selectany) @@ -12,6 +10,10 @@ char const * const WINRT_version = "C++/WinRT version:" CPPWINRT_VERSION; #pragma comment(linker, "/include:WINRT_version") #endif +#if defined(_MSC_VER) +#pragma detect_mismatch("C++/WinRT version", CPPWINRT_VERSION) +#endif + WINRT_EXPORT namespace winrt { template diff --git a/strings/base_version_odr.h b/strings/base_version_odr.h new file mode 100644 index 000000000..03963dded --- /dev/null +++ b/strings/base_version_odr.h @@ -0,0 +1,2 @@ +#define CPPWINRT_VERSION "%" + From 5a5e33ce6b613ad209a10931684c753a3267324a Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Fri, 10 Jul 2020 08:24:02 -0700 Subject: [PATCH 035/400] Disable warning 4458 in delegates (#686) --- strings/base_delegate.h | 9 +++++++++ test/test/pch.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/strings/base_delegate.h b/strings/base_delegate.h index e3995dd11..fa1484dd2 100644 --- a/strings/base_delegate.h +++ b/strings/base_delegate.h @@ -1,6 +1,11 @@ namespace winrt::impl { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4458) // declaration hides class member (okay because we do not use named members of base class) +#endif + template struct implements_delegate : abi_t, H, update_module_lock { @@ -187,6 +192,10 @@ namespace winrt::impl return { static_cast(new variadic_delegate(std::forward(handler))), take_ownership_from_abi }; } }; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } WINRT_EXPORT namespace winrt diff --git a/test/test/pch.h b/test/test/pch.h index f027fb732..ea74230cc 100644 --- a/test/test/pch.h +++ b/test/test/pch.h @@ -1,5 +1,7 @@ #pragma once +#pragma warning(4: 4458) // ensure we compile clean with this warning enabled + #define WINRT_LEAN_AND_MEAN #include #include "winrt/Windows.Foundation.Collections.h" From 048c5c6a0f95328fcc882d215386b4632f57f89f Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Fri, 10 Jul 2020 08:24:31 -0700 Subject: [PATCH 036/400] Accommodate other "key not found" errors (#687) --- strings/base_error.h | 2 +- strings/base_types.h | 1 + test/old_tests/UnitTests/TryLookup.cpp | 40 ++++++++++++++++++-------- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/strings/base_error.h b/strings/base_error.h index 630e1dd33..f22214718 100644 --- a/strings/base_error.h +++ b/strings/base_error.h @@ -584,7 +584,7 @@ namespace winrt::impl { inline hresult check_hresult_allow_bounds(hresult const result) { - if (result != impl::error_out_of_bounds) + if (result != impl::error_out_of_bounds && result != impl::error_fail && result != impl::error_file_not_found) { check_hresult(result); } diff --git a/strings/base_types.h b/strings/base_types.h index 0eadc03cc..e6ca11fb0 100644 --- a/strings/base_types.h +++ b/strings/base_types.h @@ -147,4 +147,5 @@ namespace winrt::impl constexpr hresult error_canceled{ static_cast(0x800704C7) }; // HRESULT_FROM_WIN32(ERROR_CANCELLED) constexpr hresult error_bad_alloc{ static_cast(0x8007000E) }; // E_OUTOFMEMORY constexpr hresult error_not_initialized{ static_cast(0x800401F0) }; // CO_E_NOTINITIALIZED + constexpr hresult error_file_not_found{ static_cast(0x80070002) }; // HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) } diff --git a/test/old_tests/UnitTests/TryLookup.cpp b/test/old_tests/UnitTests/TryLookup.cpp index 47d2b5536..3c6c54580 100644 --- a/test/old_tests/UnitTests/TryLookup.cpp +++ b/test/old_tests/UnitTests/TryLookup.cpp @@ -110,21 +110,37 @@ TEST_CASE("TryRemove") TEST_CASE("TryLookup TryRemove error") { - // Simulate a non-agile map that is being accessed from the wrong thread. - // "Try" operations should throw rather than erroneously report "not found". - // Because they didn't even try. The operation never got off the ground. - struct incorrectly_used_non_agile_map : implements> + // A map that throws a specific error, used to verify various edge cases. + struct error_map : implements> { - int Lookup(int) { throw hresult_wrong_thread(); } - int32_t Size() { throw hresult_wrong_thread(); } - bool HasKey(int) { throw hresult_wrong_thread(); } - IMapView GetView() { throw hresult_wrong_thread(); } - bool Insert(int, int) { throw hresult_wrong_thread(); } - void Remove(int) { throw hresult_wrong_thread(); } - void Clear() { throw hresult_wrong_thread(); } + hresult code; + int Lookup(int) { throw_hresult(code); } + int32_t Size() { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test + bool HasKey(int) { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test + IMapView GetView() { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test + bool Insert(int, int) { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test + void Remove(int) { throw_hresult(code); } + void Clear() { throw_hresult(E_UNEXPECTED); } // shouldn't be called by the test }; - auto map = make(); + auto self = make_self(); + IMap map = *self; + + // Simulate a non-agile map that is being accessed from the wrong thread. + // "Try" operations should throw rather than erroneously report "not found". + // Because they didn't even try. The operation never got off the ground. + self->code = RPC_E_WRONG_THREAD; REQUIRE_THROWS_AS(map.TryLookup(123), hresult_wrong_thread); REQUIRE_THROWS_AS(map.TryRemove(123), hresult_wrong_thread); + + // Some implementations return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) + // or E_FAIL when the key is not present. + self->code = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + REQUIRE(!map.TryLookup(123)); + REQUIRE(!map.TryRemove(123)); + + self->code = E_FAIL; + REQUIRE(!map.TryLookup(123)); + REQUIRE(!map.TryRemove(123)); + } \ No newline at end of file From 5869973d47f77ea0da9b5005af757b39a4c7df8b Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Mon, 13 Jul 2020 09:06:00 -0700 Subject: [PATCH 037/400] diagnose throw_hresult(success_code) better (#689) --- strings/base_error.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/strings/base_error.h b/strings/base_error.h index f22214718..2b4eda9d8 100644 --- a/strings/base_error.h +++ b/strings/base_error.h @@ -183,17 +183,17 @@ WINRT_EXPORT namespace winrt return *this; } - explicit hresult_error(hresult const code) noexcept : m_code(code) + explicit hresult_error(hresult const code) noexcept : m_code(verify_error(code)) { originate(code, nullptr); } - hresult_error(hresult const code, param::hstring const& message) noexcept : m_code(code) + hresult_error(hresult const code, param::hstring const& message) noexcept : m_code(verify_error(code)) { originate(code, get_abi(message)); } - hresult_error(hresult const code, take_ownership_from_abi_t) noexcept : m_code(code) + hresult_error(hresult const code, take_ownership_from_abi_t) noexcept : m_code(verify_error(code)) { com_ptr info; WINRT_IMPL_GetErrorInfo(0, info.put_void()); @@ -306,6 +306,13 @@ WINRT_EXPORT namespace winrt WINRT_VERIFY(info.try_as(m_info)); } + static hresult verify_error(hresult const code) noexcept + { + WINRT_ASSERT(code < 0); + return code; + } + + #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-private-field" From 9ddc0bb4e767f9fc0c7d7cc2aafbdb1f909193e8 Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Tue, 14 Jul 2020 10:39:23 -0700 Subject: [PATCH 038/400] Don't free the apartment_context while we are still using it (#691) --- strings/base_coroutine_threadpool.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index acb03624f..2409aecf6 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -336,7 +336,8 @@ WINRT_EXPORT namespace winrt void await_suspend(std::experimental::coroutine_handle<> handle) const { - impl::resume_apartment(context, handle); + auto copy = context; // resuming may destruct *this, so use a copy + impl::resume_apartment(copy, handle); } impl::resume_apartment_context context; From aed79775b8ef0eb0d146f0856dc3ba88010845ea Mon Sep 17 00:00:00 2001 From: Raymond Chen Date: Tue, 21 Jul 2020 07:56:04 -0700 Subject: [PATCH 039/400] Add value type support to as() and try_as() (#695) --- strings/base_com_ptr.h | 13 +- strings/base_meta.h | 9 - strings/base_reference_produce.h | 142 ++++++++---- strings/base_windows.h | 36 ++- test/old_tests/UnitTests/Boxing2.cpp | 335 ++++++++++++--------------- test/old_tests/UnitTests/boxing.cpp | 129 ++++------- test/old_tests/UnitTests/com_ptr.cpp | 50 ++++ 7 files changed, 371 insertions(+), 343 deletions(-) diff --git a/strings/base_com_ptr.h b/strings/base_com_ptr.h index 3ae9d88cd..546910b82 100644 --- a/strings/base_com_ptr.h +++ b/strings/base_com_ptr.h @@ -133,8 +133,17 @@ WINRT_EXPORT namespace winrt template bool try_as(To& to) const noexcept { - to = try_as>(); - return static_cast(to); + if constexpr (impl::is_com_interface_v || !std::is_same_v>) + { + to = try_as>(); + return static_cast(to); + } + else + { + auto result = try_as(); + to = result.has_value() ? result.value() : impl::empty_value(); + return result.has_value(); + } } hresult as(guid const& id, void** result) const noexcept diff --git a/strings/base_meta.h b/strings/base_meta.h index 01e102b5b..d2b386442 100644 --- a/strings/base_meta.h +++ b/strings/base_meta.h @@ -226,15 +226,6 @@ namespace winrt::impl template using wrapped_type_t = typename wrapped_type::type; - template