Skip to content

Commit

Permalink
Add support for composed implementations of IComponentConnector (#1149)
Browse files Browse the repository at this point in the history
* add support for composed implementations of IComponentConnector

* document ComponentConnectorT helper

* revert change for deriving without interfaces - creates other breaks and very atypical scenario

* pr feedback

* pr feedback

* pr feedback

* http://slashslash.info/petition/ compliance
  • Loading branch information
Scottj1s authored May 24, 2022
1 parent 0058881 commit dcea885
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 29 deletions.
8 changes: 8 additions & 0 deletions cppwinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -3270,6 +3270,14 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable<T, D, %>
{
w.write(strings::base_xaml_typename);
}
else if (namespace_name == "Windows.UI.Xaml.Markup")
{
w.write(strings::base_xaml_component_connector, "Windows");
}
else if (namespace_name == "Microsoft.UI.Xaml.Markup")
{
w.write(strings::base_xaml_component_connector, "Microsoft");
}
}

static void write_namespace_special_1(writer& w, std::string_view const& namespace_name)
Expand Down
1 change: 1 addition & 0 deletions cppwinrt/component_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,7 @@ catch (...) { return winrt::to_hresult(); }
}
else
{
composable_base_name = w.write_temp("using composable_base = B;");
base_type_parameter = ", typename B";
base_type_argument = ", B";
no_module_lock = "no_module_lock, ";
Expand Down
3 changes: 2 additions & 1 deletion cppwinrt/cppwinrt.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
<ClInclude Include="..\strings\base_com_ptr.h" />
<ClInclude Include="..\strings\base_coroutine_foundation.h" />
<ClInclude Include="..\strings\base_coroutine_system.h" />
<ClInclude Include="..\strings\base_coroutine_system_winui.h" />
<ClInclude Include="..\strings\base_coroutine_threadpool.h" />
<ClInclude Include="..\strings\base_coroutine_ui_core.h" />
<ClInclude Include="..\strings\base_coroutine_system_winui.h" />
<ClInclude Include="..\strings\base_deferral.h" />
<ClInclude Include="..\strings\base_delegate.h" />
<ClInclude Include="..\strings\base_error.h" />
Expand Down Expand Up @@ -88,6 +88,7 @@
<ClInclude Include="..\strings\base_version_odr.h" />
<ClInclude Include="..\strings\base_weak_ref.h" />
<ClInclude Include="..\strings\base_windows.h" />
<ClInclude Include="..\strings\base_xaml_component_connector.h" />
<ClInclude Include="..\strings\base_xaml_typename.h" />
<ClInclude Include="cmd_reader.h" />
<ClInclude Include="code_writers.h" />
Expand Down
15 changes: 9 additions & 6 deletions cppwinrt/cppwinrt.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,6 @@
<ClInclude Include="..\strings\base_coroutine_foundation.h">
<Filter>strings</Filter>
</ClInclude>
<ClInclude Include="..\strings\base_coroutine_system.h">
<Filter>strings</Filter>
</ClInclude>
<ClInclude Include="..\strings\base_coroutine_threadpool.h">
<Filter>strings</Filter>
</ClInclude>
Expand Down Expand Up @@ -163,9 +160,6 @@
<ClInclude Include="..\strings\base_includes.h">
<Filter>strings</Filter>
</ClInclude>
<ClInclude Include="..\strings\base_coroutine_system_winui.h">
<Filter>strings</Filter>
</ClInclude>
<ClInclude Include="..\strings\base_iterator.h">
<Filter>strings</Filter>
</ClInclude>
Expand All @@ -175,6 +169,15 @@
<ClInclude Include="..\strings\base_stringable_format.h">
<Filter>strings</Filter>
</ClInclude>
<ClInclude Include="..\strings\base_xaml_component_connector.h">
<Filter>strings</Filter>
</ClInclude>
<ClInclude Include="..\strings\base_coroutine_system.h">
<Filter>strings</Filter>
</ClInclude>
<ClInclude Include="..\strings\base_coroutine_system_winui.h">
<Filter>strings</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="$(OutDir)version.rc" />
Expand Down
8 changes: 8 additions & 0 deletions nuget/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@ void MyComponent::InitializeComponent()
}
```

***[Windows|Microsoft]::UI::Xaml::Markup::ComponentConnectorT***

A consequence of calling InitializeComponent outside construction is that Xaml runtime callbacks to IComponentConnector::Connect and IComponentConnector2::GetBindingConnector are now dispatched to the most derived implementations. Previously, these calls were dispatched directly to the class under construction, as the vtable had yet to be initialized. For objects with markup that derive from composable base classes with markup, this is a breaking change. Derived classes must now implement IComponentConnector::Connect and IComponentConnector2::GetBindingConnector by explicitly calling into the base class. The ComponentConnectorT template provides a correct implemenation for these interfaces:

```cpp
struct DerivedPage : winrt::Windows::UI::Xaml::Markup::ComponentConnectorT<DerivedPageT<DerivedPage>>
```
## Troubleshooting
The msbuild verbosity level maps to msbuild message importance as follows:
Expand Down
98 changes: 76 additions & 22 deletions strings/base_implements.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,24 +253,24 @@ namespace winrt::impl
template <>
struct interface_list<>
{
template <typename T, typename Predicate>
static constexpr void* find(const T*, const Predicate&) noexcept
template <typename Traits>
static constexpr auto find(Traits const& traits) noexcept
{
return nullptr;
return traits.not_found();
}
};

template <typename First, typename ... Rest>
struct interface_list<First, Rest...>
{
template <typename T, typename Predicate>
static constexpr void* find(const T* obj, const Predicate& pred) noexcept
template <typename Traits>
static constexpr auto find(Traits const& traits) noexcept
{
if (pred.template test<First>())
if (traits.template test<First>())
{
return to_abi<First>(obj);
return traits.template found<First>();
}
return interface_list<Rest...>::find(obj, pred);
return interface_list<Rest...>::find(traits);
}
using first_interface = First;
};
Expand Down Expand Up @@ -362,34 +362,88 @@ namespace winrt::impl
using type = typename implements_default_interface<T>::type;
};

struct iid_finder
template<typename T>
struct find_iid_traits
{
const guid& m_guid;
T const* m_object;
guid const& m_guid;

template <typename I>
constexpr bool test() const noexcept
{
return is_guid_of<typename default_interface<I>::type>(m_guid);
}

template <typename I>
constexpr void* found() const noexcept
{
return to_abi<I>(m_object);
}

static constexpr void* not_found() noexcept
{
return nullptr;
}
};

template <typename T>
auto find_iid(const T* obj, const guid& iid) noexcept
auto find_iid(T const* obj, guid const& iid) noexcept
{
return static_cast<unknown_abi*>(implemented_interfaces<T>::find(obj, iid_finder{ iid }));
return static_cast<unknown_abi*>(implemented_interfaces<T>::find(find_iid_traits<T>{ obj, iid }));
}

struct inspectable_finder
template <typename I>
struct has_interface_traits
{
template <typename T>
constexpr bool test() const noexcept
{
return std::is_same_v<T, I>;
}

template <typename>
static constexpr bool found() noexcept
{
return true;
}

static constexpr bool not_found() noexcept
{
return false;
}
};

template <typename T, typename I>
constexpr bool has_interface() noexcept
{
return impl::implemented_interfaces<T>::find(has_interface_traits<I>{});
}

template<typename T>
struct find_inspectable_traits
{
T const* m_object;

template <typename I>
static constexpr bool test() noexcept
{
return std::is_base_of_v<inspectable_abi, abi_t<I>>;
}

template <typename I>
constexpr void* found() const noexcept
{
return to_abi<I>(m_object);
}

static constexpr void* not_found() noexcept
{
return nullptr;
}
};

template <typename T>
inspectable_abi* find_inspectable(const T* obj) noexcept
inspectable_abi* find_inspectable(T const* obj) noexcept
{
using default_interface = typename implements_default_interface<T>::type;

Expand All @@ -399,7 +453,7 @@ namespace winrt::impl
}
else
{
return static_cast<inspectable_abi*>(implemented_interfaces<T>::find(obj, inspectable_finder{}));
return static_cast<inspectable_abi*>(implemented_interfaces<T>::find(find_inspectable_traits<T>{ obj }));
}
}

Expand Down Expand Up @@ -502,7 +556,7 @@ namespace winrt::impl
template <typename D>
struct produce<D, INonDelegatingInspectable> : produce_base<D, INonDelegatingInspectable>
{
int32_t __stdcall QueryInterface(const guid& id, void** object) noexcept final
int32_t __stdcall QueryInterface(guid const& id, void** object) noexcept final
{
return this->shim().NonDelegatingQueryInterface(id, object);
}
Expand Down Expand Up @@ -910,7 +964,7 @@ namespace winrt::impl
return target;
}

int32_t __stdcall NonDelegatingQueryInterface(const guid& id, void** object) noexcept
int32_t __stdcall NonDelegatingQueryInterface(guid const& id, void** object) noexcept
{
if (is_guid_of<Windows::Foundation::IInspectable>(id) || is_guid_of<Windows::Foundation::IUnknown>(id))
{
Expand All @@ -932,13 +986,13 @@ namespace winrt::impl

int32_t __stdcall NonDelegatingGetIids(uint32_t* count, guid** array) noexcept
{
const auto& local_iids = static_cast<D*>(this)->get_local_iids();
const uint32_t& local_count = local_iids.first;
auto const& local_iids = static_cast<D*>(this)->get_local_iids();
uint32_t const& local_count = local_iids.first;
if constexpr (root_implements_type::is_composing)
{
if (local_count > 0)
{
const com_array<guid>& inner_iids = get_interfaces(root_implements_type::m_inner);
com_array<guid> const& inner_iids = get_interfaces(root_implements_type::m_inner);
*count = local_count + inner_iids.size();
*array = static_cast<guid*>(WINRT_IMPL_CoTaskMemAlloc(sizeof(guid)*(*count)));
if (*array == nullptr)
Expand Down Expand Up @@ -1189,7 +1243,7 @@ namespace winrt::impl
}

virtual unknown_abi* get_unknown() const noexcept = 0;
virtual std::pair<uint32_t, const guid*> get_local_iids() const noexcept = 0;
virtual std::pair<uint32_t, guid const*> get_local_iids() const noexcept = 0;
virtual hstring GetRuntimeClassName() const = 0;
virtual void* find_interface(guid const&) const noexcept = 0;
virtual inspectable_abi* find_inspectable() const noexcept = 0;
Expand Down Expand Up @@ -1450,7 +1504,7 @@ WINRT_EXPORT namespace winrt
return impl::find_inspectable(static_cast<const D*>(this));
}

std::pair<uint32_t, const guid*> get_local_iids() const noexcept override
std::pair<uint32_t, guid const*> get_local_iids() const noexcept override
{
using interfaces = impl::uncloaked_interfaces<D>;
using local_iids = impl::uncloaked_iids<interfaces>;
Expand Down
53 changes: 53 additions & 0 deletions strings/base_xaml_component_connector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

WINRT_EXPORT namespace winrt::%::UI::Xaml::Markup
{
template <typename D>
struct ComponentConnectorT : D
{
using composable_base = typename D::composable_base;

void InitializeComponent()
{
if constexpr (m_has_connectable_base)
{
m_dispatch_base = true;
composable_base::InitializeComponent();
m_dispatch_base = false;
}
D::InitializeComponent();
}

void Connect(int32_t connectionId, Windows::Foundation::IInspectable const& target)
{
if constexpr (m_has_connectable_base)
{
if (m_dispatch_base)
{
composable_base::Connect(connectionId, target);
return;
}
}
D::Connect(connectionId, target);
}

auto GetBindingConnector(int32_t connectionId, Windows::Foundation::IInspectable const& target)
{
if constexpr (m_has_connectable_base)
{
if (m_dispatch_base)
{
return composable_base::GetBindingConnector(connectionId, target);
}
}
return D::GetBindingConnector(connectionId, target);
}

private:
static constexpr bool m_has_connectable_base{
impl::has_initializer<composable_base>::value &&
impl::has_interface<D, IComponentConnector>() &&
impl::has_interface<D, IComponentConnector2>() };

bool m_dispatch_base{};
};
}

0 comments on commit dcea885

Please sign in to comment.