microsoft / cppwinrt

C++/WinRT
MIT License
1.64k stars 238 forks source link

Bug: Unable to perform custom, manual composition (aggregation) #1318

Closed amtopel closed 1 year ago

amtopel commented 1 year ago

Version

2.0.220110.5

Summary

Let’s say you declare a runtime class called MyControl, which derives from (i.e., composes) the base class, Microsoft.UI.Xaml.Controls.Control. C++/WinRT will set up the implementation and generate a file MyControl.g.h, which includes the implementation base class MyControl_base. The base class will contain the following constructor:

MyControl_base()
{
      impl::call_factory<winrt::Microsoft::UI::Xaml::Controls::Control, winrt::Microsoft::UI::Xaml::Controls::IControlFactory>([&](winrt::Microsoft::UI::Xaml::Controls::IControlFactory const& f) { [[maybe_unused]] auto winrt_impl_discarded = f.CreateInstance(*this, this->m_inner); });
}

The upshot is that the implementation base class will automatically activate the runtime base (i.e., composed) class when the derived class is constructed.

What if you want to have some sort of delayed and customized composition, though? Maybe I want to implement some logic as to what and how the base class is activated.

So I was wondering if there could be some flag or marker type—like, winrt::manual_compose or something, which indicates to the implementation base class that it should not activate the runtime base class, and that I'll do it on my own. So in pseudocode, the implementation base constructor might look something like this:

MyControl_base()
{
     if (not manual_compose)
            impl::call_factory<winrt::Microsoft::UI::Xaml::Controls::Control, winrt::Microsoft::UI::Xaml::Controls::IControlFactory>([&](winrt::Microsoft::UI::Xaml::Controls::IControlFactory const& f) { [[maybe_unused]] auto winrt_impl_discarded = f.CreateInstance(*this, this->m_inner); });
}

Note that ATL, for example, provides this flexibility. It offers both the COM_INTERFACE_ENTRY_AGGREGATE macro, as well as the COM_INTERFACE_ENTRY_AUTOAGGREGATE macro.

Reproducible example

namespace MyProject
{
    [default_interface]
    runtimeclass MyControl : Microsoft.UI.Xaml.Controls.Control
    {
    }
}

Expected behavior

I'd expect some sort of ability to peform manual, custom composition.

Actual behavior

We aren't afforded that opportunity.

Additional comments

Thanks for your time and consideration.

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 10 days with no activity. Remove stale label or comment or this will be closed in 5 days.

amtopel commented 1 year ago

One additional note: although you can technicallly do whatever you want with m_inner after the base constructor is called, it's inefficient to have the base constructor automatically activate m_inner, and then for your derived class to activate it with a different class.

kennykerr commented 1 year ago

Public composable types are only meant to accommodate Xaml, so I'd rather not complicate things further and make them more broadly applicable as they're quite inefficient, complex, and problematic.

amtopel commented 1 year ago

Thanks for your response. Well my suggestion is being made with an eye towards increasing efficiency and letting the derived class compose on its own terms.

Maybe we could get an ATL developer to offer their two cents? Because ATL recognized the value of allowing the derived class to aggregate manually.

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 10 days with no activity. Remove stale label or comment or this will be closed in 5 days.