microsoft/wil

Feature request: when built with `/std:c++latest` or future `/std:c++23`, support `std::expected<>` for `wil::reg::try_*`

fredemmott opened this issue · 3 comments

Something like std::expected<std::wstring, HRESULT> = wil::reg::try_get(...) would be a very handy API :)

Ooh, that's super cool! I didn't know about that API. @keith-horton, you might like this---it'd let us have safer nothrow variants!

Something like this, right?

auto value = wil::reg::try_get_dword(...);
if (value.has_value())
{
  std::cout << "value: " << *value << '\n';
}
else if (value.error() == E_FILE_NOT_FOUND)
{
  // ...
}
// ...

Edit: for clarity, I'm not sure I have the bandwidth to add this, and I'm not sure Keith does either. But I think it's a good suggestion :)

At some point, I used this with Boost Outcome's result type, similar to:

template <typename T>
using hr_result = result<T, HRESULT>

// ...

class thing
{
private:
    thing() nothrow;

public:
    thing(const thing &) = delete;
    thing(thing &&) = default;
    thing &operator(const thing &) = delete;
    thing &operator(thing &&) = default;

    static hr_result<thing> create();
}

This pattern allows me to avoid exceptions without the need to have out parameters for results. Without a result type like this, it actually wouldn't be possible to make the default constructor private and two-phase construction ends up being a requirement or an HRESULT out parameter is required.

Then I would have macros specifically designed to check the errors, log them if they failed, and return on failure. Back when I did this, I did it with clang-cl so it ended up looking pretty dang decent:

hr_result<DWORD> do_something()
{    
    return TRYX(do_a_thing()) + TRYX(do_another_thing());
}

which ends up pretty close to how Rust looks with ? syntax, just more verbose.

The MSVC alternative would be:

hr_result<DWORD> do_something()
{    
    DWORD a{};
    DWORD b{};

    TRY(a, do_a_thing());
    TRY(b, do_another_thing());

    return a + b;
}

or

hr_result<DWORD> do_something()
{
    TRY(DWORD a, do_a_thing());
    TRY(DWORD b, do_another_thing());

    return a + b;
}

In future projects, I stopped doing this because the logging of errors on the Windows platform is convoluted to say the least and I needed boilerplate to handle that every time, boilerplate that WIL already handles. I also had to recreate/drag the various check macros around specifically meant for Windows result types that had to wrap around boost outcomes result macros. I really like this pattern but the setup was very inconvenient, especially for small codebases.

Just some food for thought.

Is this something that could be done generically across all of WIL at once? Eg a helper of some sort.

Not sure how you could automatically “forward” outparams though…