C++17 introduced structured binding, which lets you assign an expression to multiple variables.
auto [a,b] = std::pair(1, "hello"); // int a = 1 // char const* b = "hello"
However, this is for creating new variables to hold the result. If you want to assign the result to existing variables, then you can use the old standby std::tie.
int a; char const* b; std::tie(a, b) = std::pair(1, "hello");
This comes in handy in C++/WinRT if you have a winrt::com_array<T> and you need to return it in its ABI form of a uint32_t coupled with a T*.
winrt::com_array<int32_t> CalculateResult();
HRESULT GetInt32Array(uint32_t* size, int32_t** value) try
{
*size = 0;
*value = nullptr;
std::tie(*size, *value) = winrt::detach_abi(CalculateResult());
return S_OK;
}
catch (...) { return winrt::to_hresult(); }
When applied to a com_array, the detach_abi function returns a std::pair representing the size of the conformant array and a pointer to the start of the array. This is a form ready to be assigned to the tie of the two output parameters.
The type of the pointer part of the return value of detach_abi(com_array<T> a) is a pointer to the C++/WinRT ABI representation of T. Here are some examples:
T |
detach_abi(com_array<T>) returns |
|---|---|
int32_t |
std::pair<uint32_t, int32_t*> |
hstring |
std::pair<uint32_t, void**> |
ISomething |
std::pair<uint32_t, mystery_abi*> |
- If you have a
com_arrayof a scalar type, then you will get a pointer to a conformant array of that scalar type. - If you have a
com_arrayof a string type, then you will get a pointer to a conformant array ofvoid*. - If you have a
com_arrayof a reference type, then you will get a pointer to a conformant array of mystery pointers.
In the last case, you should just treat the resulting pointer as if it were a void**.
HRESULT GetNames(uint32_t* size, HSTRING** value) try
{
*size = 0;
*value = nullptr;
std::tie(*size, reinterpret_cast<void*&>(*value)) =
winrt::detach_abi(CalculateNames());
return S_OK;
}
catch (...) { return winrt::to_hresult(); }
HRESULT GetSomethingArray(uint32_t* size, ISomething*** value) try
{
*size = 0;
*value = nullptr;
std::tie(*size, reinterpret_cast<void*&>(*value)) =
winrt::detach_abi(CalculateSomethings());
return S_OK;
}
catch (...) { return winrt::to_hresult(); }
Note that in both cases we reinterpret-cast the output pointer to just void*. Any pointer type can be assigned to void*, so we just use that to soak up the C++/WinRT ABI pointer, without needing to know what it actually is.¹
¹ The C++/WinRT ABI requires that all data pointers have the same size and representation, so this sort of type pun is legal from an ABI point of view.
0 comments