Say you have a std::optional<T> in your hand, and you want to convert it to an IInspectable, say because you want to put it inside a PropertySet.
void SaveHeight(winrt::PropertySet const& set)
{
std::optional<int> height = GetHeight();
set.Insert(L"height", /* what goes here? */);
}
Well, one thing you shouldn’t do is go right for the value:
// Code in italics is wrong.
void SaveHeight(winrt::PropertySet const& set)
{
std::optional<int> height = GetHeight();
set.Insert(L"height",
winrt::box_value(height.value()));
}
Calling value() on an empty std::optional throws the std:: exception. In that case, your SaveHeight method throws an exception instead of saving a nullptr into the property set. If this exception crosses an ABI boundary, C++/WinRT and C++/CX will convert it to E_FAIL, and WIL will convert it to ERROR_. But really, it doesn’t matter how the C++ exception is converted to an ABI HRESULT because you didn’t want an exception in the first place. You wanted the std:: to convert to a null pointer.
You could manually check for an empty std:::
void SaveHeight(winrt::PropertySet const& set)
{
std::optional<int> height = GetHeight();
set.Insert(L"height",
height ? winrt::box_value(*height) : nullptr);
}
But there’s an even easier way.
C++/WinRT provides a conversion operator from std:: to IReference<T> which does the obvious thing: An empty std:: becomes nullptr and a std:: with a value becomes an IReference that holds the value.
void SaveHeight(winrt::PropertySet const& set)
{
std::optional<int> height = GetHeight();
set.Insert(L"height",
winrt::IReference(height));
}
We are taking advantage here of class template argument deduction (CTAD), which lets us write just winrt:: and let the compiler infer that we are constructing a winrt::.
0 comments