Last time, we cloned a Windows Runtime vector in the face of possible concurrent modification, but we ran into trouble with std::vector<bool>.
As I noted some time ago, the C++ language defines a specialization std::vector<bool> which represents a packed bit array, rather than defining a separate type like std::bitvector. This has made a lot of people very angry and has been widely regarded as a bad move.
In our case, the specialization of std::vector<bool> breaks our clone_ function, since it needs a winrt::, which needs a C-style array of bool objects, not a packed bit array.
We’ll have to detect the bool case in our function and substitute a std::.
template<typename V>
auto clone_as_vector(V const& v)
-> std::vector<decltype(v.GetAt(0))>
{
using T = decltype(v.GetAt(0));
std::conditional_t<
std::is_same_v<T, bool>,
std::unique_ptr<bool[]>,
std::vector<T>> temp;
uint32_t expected;
uint32_t actual;
do {
expected = v.Size();
if constexpr (std::is_same_v<T, bool>) {
temp = std::make_unique<bool[]>(expected + 1);
actual = v.GetMany(0,
winrt::array_view(temp.get(), expected + 1));
} else {
temp.resize(expected + 1, winrt_empty_value<T>());
actual = v.GetMany(0, temp);
}
} while (actual > expected);
if constexpr (std::is_same_v<T, bool>) {
return std::vector(temp.get(), temp.get() + actual);
} else {
temp.erase(temp.begin() + actual, temp.end());
return temp;
}
}
If the value type of the vector is a bool, then the temp changes to a std::, and that means that we have to change how we resize the array (namely by replacing it with a new allocation) and how we generate the array_view<bool> (by using the pointer + size constructor).
After the loop has captured the elements, we convert our C-style array of bool into a std::.
Now that I looked at it some more, it seems that there is barely any shared code at all. May as well just make it two functions glued together.
template<typename V>
auto clone_as_vector(V const& v)
-> std::vector<decltype(v.GetAt(0))>
{
using T = decltype(v.GetAt(0));
uint32_t expected;
uint32_t actual;
if constexpr (std::is_same_v<T, bool>) {
std::unique_ptr<bool[]> temp;
do {
expected = v.Size();
temp = std::make_unique<bool[]>(expected + 1);
actual = v.GetMany(0,
winrt::array_view(temp.get(), expected + 1));
} while (actual > expected);
return std::vector(temp.get(), temp.get() + actual);
} else {
std::vector<T> temp;
do {
expected = v.Size();
temp.resize(expected + 1, winrt_empty_value<T>());
actual = v.GetMany(0, temp);
} while (actual > expected);
temp.erase(temp.begin() + actual, temp.end());
return temp;
}
}
Whew, we took care of the pesky std::.
Next time, we’ll look at the potential infinite loop and whether it offers a denial of service attack.
0 comments