From 661e9805df0360de78ac0aea6dbc8a3de212a948 Mon Sep 17 00:00:00 2001 From: Ben Stolovitz Date: Fri, 6 Sep 2024 09:46:27 -0400 Subject: [PATCH 1/4] compilable stub --- include/wil/cppwinrt_authoring.h | 26 +++++++++++++++++++++++ tests/CppWinRTAuthoringTests.cpp | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/include/wil/cppwinrt_authoring.h b/include/wil/cppwinrt_authoring.h index 6b85beb2..42374654 100644 --- a/include/wil/cppwinrt_authoring.h +++ b/include/wil/cppwinrt_authoring.h @@ -330,5 +330,31 @@ struct single_threaded_notifying_property : single_threaded_rw_property */ #define INIT_NOTIFYING_PROPERTY(NAME, VALUE) NAME(&m_propertyChanged, *this, L"" #NAME, VALUE) +namespace details +{ +#ifdef WINRT_Microsoft_UI_Xaml_Data_H + using Xaml_DepencyProperty = winrt::Microsoft::UI::Xaml::DependencyProperty; +#elif defined(WINRT_Windows_UI_Xaml_Data_H) + using Xaml_DepencyProperty = winrt::Windows::UI::Xaml::DependencyProperty; +#endif +} + +#define WIL_DEFINE_DP(type, name) \ + static wil::details::Xaml_DepencyProperty s_##name##Property; \ + static wil::details::Xaml_DepencyProperty name##Property() \ + { \ + /* You need to initialize (register) this dependency property */ \ + FAIL_FAST_HR_IF_MSG(E_NOTIMPL, s_##name##Property == nullptr, "##name##"); \ + return s_##name##Property; \ + } \ + auto name() const \ + { \ + return winrt::unbox_value(GetValue(s_##name##Property)); \ + } \ + void name(type value) const \ + { \ + SetValue(s_##name##Property, winrt::box_value(value)); \ + } \ + #endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H)) } // namespace wil diff --git a/tests/CppWinRTAuthoringTests.cpp b/tests/CppWinRTAuthoringTests.cpp index e08e3606..00324fde 100644 --- a/tests/CppWinRTAuthoringTests.cpp +++ b/tests/CppWinRTAuthoringTests.cpp @@ -7,6 +7,7 @@ #if _MSVC_LANG >= 201703L #include #include +#include #include #include #endif @@ -173,6 +174,41 @@ TEST_CASE("CppWinRTAuthoringTests::InStruct", "[property]") REQUIRE(test.Prop2() == 33); } +struct my_dependency_properties +{ + winrt::Windows::Foundation::IInspectable GetValue(winrt::Windows::UI::Xaml::DependencyProperty const&) const + { + // Stub + return nullptr; + } + void SetValue(winrt::Windows::UI::Xaml::DependencyProperty const&, winrt::Windows::Foundation::IInspectable const&) const + { + // Stub + } + + WIL_DEFINE_DP(int, MyProperty); +}; + +// TODO: export +#define REQUIRE_FAILFAST_MSG(hr, lambda) \ + REQUIRE(VerifyResult(__LINE__, EType::FailFastMacro | EType::Msg, hr, [&] { \ + auto fn = (lambda); \ + fn(); \ + return hr; \ + })) + +TEST_CASE("CppWinRTAuthoringTests::DependencyProperties", "[property]") +{ + // Throws if not registered + auto obj = my_dependency_properties{}; + //REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(); }); + //REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(42); }); + + // Register the dependency property + +} + + #ifdef WINRT_Windows_Foundation_H TEST_CASE("CppWinRTAuthoringTests::Events", "[property]") { From 3cb550fae1d7c2f3c54896432b8dc69465ed0960 Mon Sep 17 00:00:00 2001 From: Ben Stolovitz Date: Fri, 6 Sep 2024 11:39:43 -0400 Subject: [PATCH 2/4] local static? --- include/wil/cppwinrt_authoring.h | 84 ++++++++++++++++++++++++++++---- tests/CppWinRTAuthoringTests.cpp | 26 ++++++---- 2 files changed, 92 insertions(+), 18 deletions(-) diff --git a/include/wil/cppwinrt_authoring.h b/include/wil/cppwinrt_authoring.h index 42374654..7a08ef95 100644 --- a/include/wil/cppwinrt_authoring.h +++ b/include/wil/cppwinrt_authoring.h @@ -333,28 +333,94 @@ struct single_threaded_notifying_property : single_threaded_rw_property namespace details { #ifdef WINRT_Microsoft_UI_Xaml_Data_H - using Xaml_DepencyProperty = winrt::Microsoft::UI::Xaml::DependencyProperty; + using Xaml_DependencyProperty = winrt::Microsoft::UI::Xaml::DependencyProperty; + using Xaml_PropertyChangedCallback = winrt::Microsoft::UI::Xaml::PropertyChangedCallback; + using Xaml_PropertyMetadata = winrt::Microsoft::UI::Xaml::PropertyMetadata; #elif defined(WINRT_Windows_UI_Xaml_Data_H) - using Xaml_DepencyProperty = winrt::Windows::UI::Xaml::DependencyProperty; + using Xaml_DependencyProperty = winrt::Windows::UI::Xaml::DependencyProperty; + using Xaml_PropertyChangedCallback = winrt::Windows::UI::Xaml::PropertyChangedCallback; + using Xaml_PropertyMetadata = winrt::Windows::UI::Xaml::PropertyMetadata; #endif + + using Xaml_DependencyObject_GetValue = std::function; + using Xaml_DependencyObject_SetValue = std::function; + + template + inline Xaml_DependencyProperty register_dependency_property( + const std::wstring_view& propertyNameString) + { + return Xaml_DependencyProperty::Register( + propertyNameString, + winrt::template xaml_typename(), + winrt::template xaml_typename(), + nullptr); + } + + template + inline Xaml_DependencyProperty register_dependency_property( + const std::wstring_view& propertyNameString, + const T& defaultValue = nullptr, + const Xaml_PropertyChangedCallback& propertyChangedCallback = nullptr) + { + // TODO: assert T and PropertyType are compatible + return Xaml_DependencyProperty::Register( + propertyNameString, + winrt::template xaml_typename(), + winrt::template xaml_typename(), + Xaml_PropertyMetadata{winrt::box_value(defaultValue), propertyChangedCallback}); + } } -#define WIL_DEFINE_DP(type, name) \ - static wil::details::Xaml_DepencyProperty s_##name##Property; \ - static wil::details::Xaml_DepencyProperty name##Property() \ +//template +//struct single_threaded_dependency_property +//{ +// using Type = T; +// +// single_threaded_dependency_property( +// wil::details::Xaml_DependencyProperty const& dp, +// wil::details::Xaml_DependencyObject_GetValue const& getValue, +// wil::details::Xaml_DependencyObject_SetValue const& setValue) : +// m_dp(dp), m_getValue(getValue), m_setValue(setValue) +// { +// } +// +// template +// auto& operator()(Q&& q) +// { +// return winrt::unbox_value(m_getValue(m_dp)); +// } +// +// template +// auto& operator=(Q&& q) +// { +// m_setValue(m_dp, winrt::box_value(value)); +// } +// +//private: +// wil::details::Xaml_DependencyProperty const& m_dp{ nullptr }; +// wil::details::Xaml_DependencyObject_GetValue m_getValue{nullptr}; +// wil::details::Xaml_DependencyObject_SetValue m_setValue{nullptr}; +//}; + +#define WIL_DEFINE_DP(baseClass, type, name) \ + static wil::details::Xaml_DependencyProperty name##Property() \ { \ - /* You need to initialize (register) this dependency property */ \ - FAIL_FAST_HR_IF_MSG(E_NOTIMPL, s_##name##Property == nullptr, "##name##"); \ + static wil::details::Xaml_DependencyProperty s_##name##Property = \ + wil::details::register_dependency_property(L"" #name); \ return s_##name##Property; \ } \ auto name() const \ { \ - return winrt::unbox_value(GetValue(s_##name##Property)); \ + return winrt::unbox_value(GetValue(name##Property())); \ } \ void name(type value) const \ { \ - SetValue(s_##name##Property, winrt::box_value(value)); \ + SetValue(name##Property(), winrt::box_value(value)); \ } \ + static void Ensure##name##Property() \ + { \ + name##Property(); \ + } #endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H)) } // namespace wil diff --git a/tests/CppWinRTAuthoringTests.cpp b/tests/CppWinRTAuthoringTests.cpp index 00324fde..54904458 100644 --- a/tests/CppWinRTAuthoringTests.cpp +++ b/tests/CppWinRTAuthoringTests.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #endif #include @@ -186,26 +187,33 @@ struct my_dependency_properties // Stub } - WIL_DEFINE_DP(int, MyProperty); + WIL_DEFINE_DP(my_dependency_properties, int32_t, MyProperty); }; -// TODO: export -#define REQUIRE_FAILFAST_MSG(hr, lambda) \ - REQUIRE(VerifyResult(__LINE__, EType::FailFastMacro | EType::Msg, hr, [&] { \ - auto fn = (lambda); \ - fn(); \ - return hr; \ - })) +namespace winrt +{ + // Fake the xaml_typename specialization + template<> + inline Windows::UI::Xaml::Interop::TypeName xaml_typename() + { + static const Windows::UI::Xaml::Interop::TypeName name{hstring{L"my_dependency_properties"}, Windows::UI::Xaml::Interop::TypeKind::Custom}; + return name; + } +} TEST_CASE("CppWinRTAuthoringTests::DependencyProperties", "[property]") { - // Throws if not registered + // Throws if not ensured (?) auto obj = my_dependency_properties{}; //REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(); }); //REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(42); }); // Register the dependency property + my_dependency_properties::EnsureMyPropertyProperty(); + // Now it should work + obj.MyProperty(42); + REQUIRE(obj.MyProperty() == 42); } From f454113dec087099b228c39ef25c494d3c573a9e Mon Sep 17 00:00:00 2001 From: Ben Stolovitz Date: Fri, 6 Sep 2024 12:10:45 -0400 Subject: [PATCH 3/4] support default value too --- include/wil/cppwinrt_authoring.h | 20 +++++++++++++++++++ tests/CppWinRTAuthoringTests.cpp | 34 +++++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/include/wil/cppwinrt_authoring.h b/include/wil/cppwinrt_authoring.h index 7a08ef95..8aa0734c 100644 --- a/include/wil/cppwinrt_authoring.h +++ b/include/wil/cppwinrt_authoring.h @@ -422,5 +422,25 @@ namespace details name##Property(); \ } +#define WIL_DEFINE_DP_WITH_DEFAULT_VALUE_AND_CALLBACK(baseClass, type, name, defaultValue, propertyChangedCallback) \ + static wil::details::Xaml_DependencyProperty name##Property() \ + { \ + static wil::details::Xaml_DependencyProperty s_##name##Property = \ + wil::details::register_dependency_property(L"" #name, defaultValue, propertyChangedCallback); \ + return s_##name##Property; \ + } \ + auto name() const \ + { \ + return winrt::unbox_value(GetValue(name##Property())); \ + } \ + void name(type value) const \ + { \ + SetValue(name##Property(), winrt::box_value(value)); \ + } \ + static void Ensure##name##Property() \ + { \ + name##Property(); \ + } + #endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H)) } // namespace wil diff --git a/tests/CppWinRTAuthoringTests.cpp b/tests/CppWinRTAuthoringTests.cpp index 54904458..c8d82dcb 100644 --- a/tests/CppWinRTAuthoringTests.cpp +++ b/tests/CppWinRTAuthoringTests.cpp @@ -175,19 +175,28 @@ TEST_CASE("CppWinRTAuthoringTests::InStruct", "[property]") REQUIRE(test.Prop2() == 33); } -struct my_dependency_properties +// Simple mock of GetValue & SetValue for testing dependency properties. +struct mock_dependency_object { - winrt::Windows::Foundation::IInspectable GetValue(winrt::Windows::UI::Xaml::DependencyProperty const&) const + winrt::Windows::Foundation::IInspectable GetValue(winrt::Windows::UI::Xaml::DependencyProperty const& dp) const { - // Stub - return nullptr; + return m_values.at(dp); } - void SetValue(winrt::Windows::UI::Xaml::DependencyProperty const&, winrt::Windows::Foundation::IInspectable const&) const + void SetValue(winrt::Windows::UI::Xaml::DependencyProperty dp, winrt::Windows::Foundation::IInspectable const& value) const { - // Stub + // We have to do a const_cast because ordinarily SetValue is const--- + // it's delegated to WinRT, and C++/WinRT marks most of its functions + // const. + const_cast(this)->m_values[dp] = value; } + std::map m_values{}; +}; + +struct my_dependency_properties : mock_dependency_object +{ WIL_DEFINE_DP(my_dependency_properties, int32_t, MyProperty); + WIL_DEFINE_DP_WITH_DEFAULT_VALUE_AND_CALLBACK(my_dependency_properties, int32_t, MyPropertyWithDefault, 42, nullptr); }; namespace winrt @@ -205,17 +214,24 @@ TEST_CASE("CppWinRTAuthoringTests::DependencyProperties", "[property]") { // Throws if not ensured (?) auto obj = my_dependency_properties{}; - //REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(); }); - //REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(42); }); + // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(); }); + // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(42); }); + // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyPropertyWithDefault(); }); + // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyPropertyWithDefault(42); }); // Register the dependency property my_dependency_properties::EnsureMyPropertyProperty(); + my_dependency_properties::EnsureMyPropertyWithDefaultProperty(); // Now it should work obj.MyProperty(42); REQUIRE(obj.MyProperty() == 42); -} + // TODO: handle defaults + // REQUIRE(obj.MyPropertyWithDefault() == 42); + obj.MyPropertyWithDefault(43); + REQUIRE(obj.MyPropertyWithDefault() == 43); +} #ifdef WINRT_Windows_Foundation_H TEST_CASE("CppWinRTAuthoringTests::Events", "[property]") From 8124844872d849dcf8fc027a2b249ecf880916fb Mon Sep 17 00:00:00 2001 From: Ben Stolovitz Date: Fri, 6 Sep 2024 12:21:44 -0400 Subject: [PATCH 4/4] support strings --- include/wil/cppwinrt_authoring.h | 4 ++-- tests/CppWinRTAuthoringTests.cpp | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/wil/cppwinrt_authoring.h b/include/wil/cppwinrt_authoring.h index 8aa0734c..0111c04d 100644 --- a/include/wil/cppwinrt_authoring.h +++ b/include/wil/cppwinrt_authoring.h @@ -356,10 +356,10 @@ namespace details nullptr); } - template + template inline Xaml_DependencyProperty register_dependency_property( const std::wstring_view& propertyNameString, - const T& defaultValue = nullptr, + const DefaultValueType& defaultValue, const Xaml_PropertyChangedCallback& propertyChangedCallback = nullptr) { // TODO: assert T and PropertyType are compatible diff --git a/tests/CppWinRTAuthoringTests.cpp b/tests/CppWinRTAuthoringTests.cpp index c8d82dcb..06c74aff 100644 --- a/tests/CppWinRTAuthoringTests.cpp +++ b/tests/CppWinRTAuthoringTests.cpp @@ -197,6 +197,7 @@ struct my_dependency_properties : mock_dependency_object { WIL_DEFINE_DP(my_dependency_properties, int32_t, MyProperty); WIL_DEFINE_DP_WITH_DEFAULT_VALUE_AND_CALLBACK(my_dependency_properties, int32_t, MyPropertyWithDefault, 42, nullptr); + WIL_DEFINE_DP(my_dependency_properties, winrt::hstring, MyStringProperty); }; namespace winrt @@ -217,11 +218,14 @@ TEST_CASE("CppWinRTAuthoringTests::DependencyProperties", "[property]") // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(); }); // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyProperty(42); }); // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyPropertyWithDefault(); }); - // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyPropertyWithDefault(42); }); + // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyPropertyWithDefault(42); }); + // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyStringProperty(); }); + // REQUIRE_FAILFAST_MSG(E_NOTIMPL, [&obj] { obj.MyStringProperty(L"foo"); }); // Register the dependency property my_dependency_properties::EnsureMyPropertyProperty(); my_dependency_properties::EnsureMyPropertyWithDefaultProperty(); + my_dependency_properties::EnsureMyStringPropertyProperty(); // Now it should work obj.MyProperty(42); @@ -231,6 +235,9 @@ TEST_CASE("CppWinRTAuthoringTests::DependencyProperties", "[property]") // REQUIRE(obj.MyPropertyWithDefault() == 42); obj.MyPropertyWithDefault(43); REQUIRE(obj.MyPropertyWithDefault() == 43); + + obj.MyStringProperty(L"foo"); + REQUIRE(obj.MyStringProperty() == L"foo"); } #ifdef WINRT_Windows_Foundation_H