diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index b44516d60..d4885c94a 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -1327,6 +1327,12 @@ namespace cppwinrt return result; } } + + template + auto LookupAs(param_type const& key) const; + + template + auto TryLookupAs(param_type const& key) const; )"); } else if (type_name == "Windows.Foundation.Collections.IMap`2") @@ -1358,6 +1364,12 @@ namespace cppwinrt { return 0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Remove(get_abi(key))); } + + template + auto LookupAs(param_type const& key) const; + + template + auto TryLookupAs(param_type const& key) const; )"); } else if (type_name == "Windows.Foundation.IAsyncAction") diff --git a/strings/base_collections_map.h b/strings/base_collections_map.h index 877b5bf30..dc3ff6bdc 100644 --- a/strings/base_collections_map.h +++ b/strings/base_collections_map.h @@ -26,6 +26,34 @@ namespace winrt::impl Container m_values; }; + + template template + auto consume_Windows_Foundation_Collections_IMap::LookupAs(param_type const& key) const + { + static_assert(std::is_same_v); + return unbox_value(static_cast(*this).Lookup(key)); + } + + template template + auto consume_Windows_Foundation_Collections_IMap::TryLookupAs(param_type const& key) const + { + static_assert(std::is_same_v); + return try_unbox_value(static_cast(*this).TryLookup(key)); + } + + template template + auto consume_Windows_Foundation_Collections_IMapView::LookupAs(param_type const& key) const + { + static_assert(std::is_same_v); + return unbox_value(static_cast(*this).Lookup(key)); + } + + template template + auto consume_Windows_Foundation_Collections_IMapView::TryLookupAs(param_type const& key) const + { + static_assert(std::is_same_v); + return try_unbox_value(static_cast(*this).TryLookup(key)); + } } WINRT_EXPORT namespace winrt diff --git a/strings/base_reference_produce.h b/strings/base_reference_produce.h index 4ad39c5b9..fd8858e8f 100644 --- a/strings/base_reference_produce.h +++ b/strings/base_reference_produce.h @@ -377,6 +377,56 @@ WINRT_EXPORT namespace winrt return default_value; } + template + auto try_unbox_value(Windows::Foundation::IInspectable const& value) + { + if constexpr (std::is_base_of_v) + { + return value.try_as(); + } + else + { + if (!value) + { + return std::optional(); + } + + if constexpr (std::is_enum_v) + { + if (auto temp = value.try_as>()) + { + return std::optional(temp.Value()); + } + + if (auto temp = value.try_as>>()) + { + return std::optional(static_cast(temp.Value())); + } + + return std::optional(); + } +#ifdef WINRT_IMPL_IUNKNOWN_DEFINED + else if constexpr (std::is_same_v) + { + if (auto temp = value.try_as>()) + { + return std::optional(temp.Value()); + } + + return std::optional(); + } +#endif + else if (auto temp = value.try_as>()) + { + return std::optional(temp.Value()); + } + else + { + return std::optional(); + } + } + } + template using optional = Windows::Foundation::IReference; } diff --git a/test/test/lookup_as.cpp b/test/test/lookup_as.cpp new file mode 100644 index 000000000..c7add7fb8 --- /dev/null +++ b/test/test/lookup_as.cpp @@ -0,0 +1,82 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; + +namespace +{ + template + void test(M const& m) + { + // Good LookupAs + + REQUIRE(m.LookupAs(L"uri").Domain() == L"kennykerr.ca"); + REQUIRE(m.LookupAs(L"uri").ToString() == L"http://kennykerr.ca/"); + REQUIRE(m.LookupAs(L"enum") == AsyncStatus::Completed); + REQUIRE(m.LookupAs(L"underlying_type") == AsyncStatus::Error); + REQUIRE(m.LookupAs(L"guid") == guid_of()); + REQUIRE(m.LookupAs(L"guid") == static_cast(guid_of())); + REQUIRE(m.LookupAs(L"hstring") == L"Test"); + + // LookupAs edge cases + + REQUIRE_THROWS_AS(m.LookupAs(L"INVALID"), hresult_out_of_bounds); + REQUIRE(m.LookupAs(L"nullptr") == nullptr); + REQUIRE_THROWS_AS(m.LookupAs(L"nullptr"), hresult_no_interface); + + // Bad LookupAs + + REQUIRE_THROWS_AS(m.LookupAs(L"uri"), hresult_no_interface); + REQUIRE_THROWS_AS(m.LookupAs(L"uri"), hresult_no_interface); + REQUIRE_THROWS_AS(m.LookupAs(L"uri"), hresult_no_interface); + REQUIRE_THROWS_AS(m.LookupAs(L"uri"), hresult_no_interface); + REQUIRE_THROWS_AS(m.LookupAs(L"uri"), hresult_no_interface); + + // Good TryLookupAs (these tests don't need `.value()` for comparison but it + // helps to assert that those variants return std::optional) + + REQUIRE(m.TryLookupAs(L"uri").Domain() == L"kennykerr.ca"); + REQUIRE(m.TryLookupAs(L"uri").ToString() == L"http://kennykerr.ca/"); + REQUIRE(m.TryLookupAs(L"enum").value() == AsyncStatus::Completed); + REQUIRE(m.TryLookupAs(L"underlying_type").value() == AsyncStatus::Error); + REQUIRE(m.TryLookupAs(L"guid").value() == guid_of()); + REQUIRE(m.TryLookupAs(L"guid").value() == static_cast(guid_of())); + REQUIRE(m.TryLookupAs(L"hstring").value() == L"Test"); + + // TryLookupAs edge cases + + REQUIRE_FALSE(m.TryLookupAs(L"INVALID").has_value()); + REQUIRE(m.TryLookupAs(L"nullptr") == nullptr); + REQUIRE_FALSE(m.TryLookupAs(L"nullptr").has_value()); + + // Bad TryLookupAs + + REQUIRE(m.TryLookupAs(L"uri") == nullptr); + REQUIRE_FALSE(m.TryLookupAs(L"uri").has_value()); + REQUIRE_FALSE(m.TryLookupAs(L"uri").has_value()); + REQUIRE_FALSE(m.TryLookupAs(L"uri").has_value()); + REQUIRE_FALSE(m.TryLookupAs(L"uri").has_value()); + REQUIRE_FALSE(m.TryLookupAs(L"uri").has_value()); + } +} + +TEST_CASE("LookupAs") +{ + auto map = single_threaded_map(); + map.Insert(L"uri", Uri(L"http://kennykerr.ca")); + map.Insert(L"enum", box_value(AsyncStatus::Completed)); + map.Insert(L"underlying_type", box_value(static_cast(AsyncStatus::Error))); + map.Insert(L"guid", box_value(guid_of())); + map.Insert(L"hstring", box_value(L"Test")); + map.Insert(L"nullptr", nullptr); + + test(map); + test(map.GetView()); + + // Just making these helpers work inherited + ValueSet vs; + vs.Insert(L"key", box_value(L"value")); + REQUIRE(vs.LookupAs(L"key") == L"value"); + REQUIRE(vs.TryLookupAs(L"key") == L"value"); +} diff --git a/test/test/test.vcxproj b/test/test/test.vcxproj index f29835de6..e84498fb0 100644 --- a/test/test/test.vcxproj +++ b/test/test/test.vcxproj @@ -433,6 +433,7 @@ +