Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nuget/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ void MyComponent::InitializeComponent()
}
```

InitializeComponent can also have parameters, the arguments passed to the constructor will also be attempted for use in calling InitializeComponent. If no such overload exists, it will fall back to calling InitializeComponent without arguments.

***[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 implementation for these interfaces:
Expand Down
18 changes: 16 additions & 2 deletions strings/base_implements.h
Original file line number Diff line number Diff line change
Expand Up @@ -1320,12 +1320,26 @@ WINRT_EXPORT namespace winrt::impl
static constexpr bool value = get_value<T>(0);
};

template<typename T, typename... Args>
class has_initializer_with_args
{
template <typename U, typename = decltype(std::declval<U>().InitializeComponent(std::declval<Args>()...))> static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }

public:
static constexpr bool value = get_value<T>(0);
};

template<typename T, typename... Args>
T* create_and_initialize(Args&&... args)
{
com_ptr<T> instance{ new heap_implements<T>(std::forward<Args>(args)...), take_ownership_from_abi };

if constexpr (has_initializer<T>::value)

if constexpr ((sizeof...(Args) != 0) && has_initializer_with_args<T, Args...>::value)
{
instance->InitializeComponent(std::forward<Args>(args)...);
}
else if constexpr (has_initializer<T>::value)
{
instance->InitializeComponent();
}
Expand Down
98 changes: 98 additions & 0 deletions test/test/initialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,56 @@ namespace
m_initialize_called = true;
}
};

template<typename D>
struct InitializeTWithArgs : implements<D, IStringable>
{
InitializeTWithArgs([[maybe_unused]] bool& initialize_called)
{
}

~InitializeTWithArgs()
{
}

void InitializeComponent(bool& initialize_called)
{
initialize_called = true;
throw some_exception();
}

hstring ToString()
{
return {};
}
};

struct InitializeWithArgs : InitializeTWithArgs<InitializeWithArgs>
{
InitializeWithArgs(bool& initialize_called) : InitializeTWithArgs(initialize_called)
{
}
};

struct ThrowingDerivedWithArgs : InitializeTWithArgs<ThrowingDerivedWithArgs>
{
ThrowingDerivedWithArgs(bool& initialize_called) : InitializeTWithArgs(initialize_called)
{
throw some_exception();
}
};

struct OverriddenInitializeWithArgs : InitializeTWithArgs<OverriddenInitializeWithArgs>
{
OverriddenInitializeWithArgs(bool& initialize_called) : InitializeTWithArgs(initialize_called)
{
}

void InitializeComponent(bool& initialize_called)
{
initialize_called = true;
}
};
}

TEST_CASE("initialize")
Expand Down Expand Up @@ -116,4 +166,52 @@ TEST_CASE("initialize")
REQUIRE(initialize_called);
REQUIRE(!exception_caught);
}

// Ensure that failure to initialize is failure to instantiate, with no side effects
{
bool initialize_called{};
bool exception_caught{};
try
{
make<InitializeWithArgs>(initialize_called);
}
catch (some_exception const&)
{
exception_caught = true;
}
REQUIRE(initialize_called);
REQUIRE(exception_caught);
}

// Ensure that base is never initialized if exception thrown from derived/base constructor
{
bool initialize_called{};
bool exception_caught{};
try
{
make<ThrowingDerivedWithArgs>(initialize_called);
}
catch (some_exception const&)
{
exception_caught = true;
}
REQUIRE(!initialize_called);
REQUIRE(exception_caught);
}

// Support for overriding initialization for post-processing (e.g., accessing Xaml properties)
{
bool initialize_called{};
bool exception_caught{};
try
{
make<OverriddenInitializeWithArgs>(initialize_called);
}
catch (some_exception const&)
{
exception_caught = true;
}
REQUIRE(initialize_called);
REQUIRE(!exception_caught);
}
}