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