C++/WinRT provides multiple ways of auto-implementing delegates.
// Function pointer e.add(&MyClass::StaticMemberFunction); // Raw pointer + non-static member function pointer e.add({ this, &MyClass::MemberFunction }); // Strong pointer + non-static member function pointer e.add({ get_strong(), &MyClass::MemberFunction }); // Weak pointer + non-static member function pointer e.add({ get_weak(), &MyClass::MemberFunction }); // Lambda e.add(lambda);
Internally, all of the constructors are just shorthand for lambdas:
template <typename F> delegate_base(F* handler) : delegate_base( [=](auto&& ... args) { return handler(args...); }) {} template <typename O, typename M> delegate_base(O* object, M method) : delegate_base( [=](auto&& ... args) { return ((*object).*(method))(args...); }) {} template <typename O, typename M> delegate_base(com_ptr<O>&& object, M method) : delegate_base( [o = std::move(object), method](auto&& ... args) { return ((*o).*(method))(args...); }) {} template <typename O, typename M> delegate_base(winrt::weak_ref<O>&& object, M method) : delegate_base( [o = std::move(object), method](auto&& ... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) {}
One pattern that is not covered by the above is the case of a parameterized callback.
void MyClass::OnSomething(IInspectable const& sender,
IInspectable const& args,
int otherData)
{
...
}
You want to register OnSomething
as an event handler in multiple places, with each place having a different otherData
.
// When the Widget.Something event occurs, // call the OnSomething method with a specific otherData. void MyClass::RegisterSomething(int otherData) { // Doesn't work widget.Something({ get_weak(), &MyClass::OnSomething, otherData }); }
This doesn’t work because a pointer to member function doesn’t have a place to stick extra information. Instead, you have to use a lambda:
// When the Widget.Something event occurs, // call the OnSomething method with a specific otherData. void MyClass::RegisterSomething(int otherData) { // Raw pointer version widget.Something( [this, otherData] (auto&& sender, auto&& args) { this->OnSomething(sender, args, otherData); }); }
This version captures this
as a raw pointer, but we can teach it about the other two patterns:
void MyClass::RegisterSomething(int otherData) { // Strong pointer version widget.Something( [strong = get_strong(), otherData] (auto&& sender, auto&& args) { strong->OnSomething(sender, args, otherData); }); }
void MyClass::RegisterSomething(int otherData) { // Weak pointer version widget.Something( [weak = get_weak(), otherData] (auto&& sender, auto&& args) { if (auto strong = weak.get()) { strong->OnSomething(sender, args, otherData); } }); }
In practice, the work of the lambda is often inlined, so you end up with the following:
// When the Widget.Something event occurs, // call the OnSomething method with a specific otherData. void MyClass::RegisterSomething(int otherData) { // Raw pointer version widget.Something( [this, otherData] (auto&& sender, auto&& args) { DoThing1(sender); DoThing2(args); DoThing3(otherData); }); } void MyClass::RegisterSomething(int otherData) { // Strong pointer version, with bonus "this" capture // for convenience. widget.Something( [strong = get_strong(), this, otherData] (auto&& sender, auto&& args) { DoThing1(sender); DoThing2(args); DoThing3(otherData); }); }
void MyClass::RegisterSomething(int otherData) { // Weak pointer version widget.Something( [weak = get_weak(), this, otherData] (auto&& sender, auto&& args) { if (auto strong = weak.get()) { DoThing1(sender); DoThing2(args); DoThing3(otherData); } }); }
In the first two cases, you just write a lambda that does what you want to do. The third case, however, is annoying: You have to perform an extra step to resolve the weak reference. This extra step was handled by the C++/WinRT “weak pointer + non-static member function pointer” wrapper, but the lambda wrapper doesn’t provide the same courtesy.
Next time, we’ll look at extending the courtesy to lambdas.
0 comments