So far, we’ve looked at cloning a Windows Runtime vector in the face of possible concurrent modification, using C++/WinRT as our projection. Let’s extend our solution to C++/CX.
The initial translation to C++/CX is straightforward. It’s pretty much a literal translation. We can take advantage of the fact that the default constructor for hat pointers is a null pointer, so we don’t have to use winrt_
.
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; unsigned expected; unsigned actual; do { expected = v->Size; if constexpr (std::is_same_v<T, bool>) { temp = std::make_unique<bool[]>(expected + 1); actual = v->GetMany(0, Platform:: ArrayReference<T>(temp.get(), expected + 1)); } else { temp.resize(expected + 1); actual = v->GetMany(0, Platform:: ArrayReference<T>(temp.data(), expected + 1)); } } } while (actual > expected); if constexpr (std::is_same_v<T, bool>) { return std::vector(temp.get(), temp.get() + actual); } else { temp.resize(actual); return temp; } } template<typename V> auto CloneVector(V const& v) -> Windows::Foundation:: Collections::IVector<decltype(v->GetAt(0))>^ { return ref new Platform::Collections:: Vector<decltype(v->GetAt(0))>( clone_as_vector(v)); }
Or if you prefer the total separation version:
template<typename V> auto clone_as_vector(V const& v) -> std::vector<decltype(v->GetAt(0))> { using T = decltype(v->GetAt(0)); unsigned expected; unsigned 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, Platform:: ArrayReference<T>(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); actual = v->GetMany(0, Platform:: ArrayReference<T>(temp.data(), expected + 1)); } while (actual > expected); temp.erase(temp.begin() + actual, temp.end()); return temp; } }
However, there’s an addition wrinkle to C++/CX: C++/WinRT uses the ==
operator to implement IndexOf
, but C++/CX lets you pass a custom equality comparer. (The default comparer is std::equal_to<T>
, which uses ==
.) Therefore, to capture the full generality of Vector
, we allow the caller of CloneÂVector
to specify an equality comparer.
Since we want to let the V
be deduced, we resort to using type deduction from the future.¹
template<typename E = void, typename V> auto CloneVector(V const& v) -> Windows::Foundation:: Collections::IVector<decltype(v->GetAt(0))>^ { using T = decltype(v->GetAt(0)); using ActualE = std::conditional_t< std::conditional_t<std::is_same_v<E, void>, std::equal_to<T>, E>; return ref new Platform::Collections:: Vector<T, ActualE>(clone_as_vector(v)); }
There’s another customization point that we haven’t bothered with: std::
lets you provide a custom allocator. I omitted that because custom allocators are rather esoteric, and besides, the Platform::
requires the provided std::vector
to use the standard allocator.
Bonus chatter: You can see the implementation of the C++/CX collection types in the header file collection.h
that comes with the Visual C++ compiler. Other parts of the C++/CX implementation can be found in vccorlib.h
.
I’m not smart. I just know how to read.
¹ An alternative to type deduction from the future is overloading:
template<typename E, typename V> auto CloneVector(V const& v) -> Windows::Foundation:: Collections::IVector<decltype(v->GetAt(0))>^ { using T = decltype(v->GetAt(0)); return ref new Platform::Collections:: Vector<T, E>(clone_as_vector(v)); } template<typename V> auto CloneVector(V const& v) -> Windows::Foundation:: Collections::IVector<decltype(v->GetAt(0))>^ { using T = decltype(v->GetAt(0)); return CloneVector<std::equal_to<T>, V>(v); }
The risk here is that somebody might want E = V
.
// Error: Ambiguous call to overloaded function. CloneVector<V>(v);
This is trying to create a clone whose equality comparer is V
, but the compiler reports this as an ambiguous call because it matches both templates.
This could happen if V
is a tree-like structure, so each element is a collection of other instances of itself.
0 comments