2

How can I box a std::optional into a C++/WinRT IInspectable?

 1 year ago
source link: https://devblogs.microsoft.com/oldnewthing/20230330-00/?p=107986
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How can I box a std::optional into a C++/WinRT IInspectable?

RaymondChen_5in-150x150.jpg

Raymond Chen

March 30th, 20230 0

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::bad_optional_access 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_UNHANDLED_EXCEPTION. 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::nullopt to convert to a null pointer.

You could manually check for an empty std::optional:

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::optional<T> to IReference<T> which does the obvious thing: An empty std::optional becomes nullptr and a std::optional 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::IReference(...) and let the compiler infer that we are constructing a winrt::IReference<int>.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK