// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_WIN_VECTOR_H_ #define BASE_WIN_VECTOR_H_ #include #include #include #include #include #include #include #include "base/base_export.h" #include "base/containers/flat_map.h" #include "base/logging.h" namespace base { namespace win { template class Vector; namespace internal { template using Complex = typename ABI::Windows::Foundation::Collections::IVector::T_complex; template using Logical = typename ABI::Windows::Foundation::Internal::GetLogicalType< Complex>::type; template using Abi = typename ABI::Windows::Foundation::Internal::GetAbiType>::type; class BASE_EXPORT VectorChangedEventArgs : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRtClassicComMix | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IVectorChangedEventArgs> { public: VectorChangedEventArgs( ABI::Windows::Foundation::Collections::CollectionChange change, unsigned int index) : change_(change), index_(index) {} ~VectorChangedEventArgs() override = default; // ABI::Windows::Foundation::Collections::IVectorChangedEventArgs: IFACEMETHODIMP get_CollectionChange( ABI::Windows::Foundation::Collections::CollectionChange* value) override; IFACEMETHODIMP get_Index(unsigned int* value) override; private: const ABI::Windows::Foundation::Collections::CollectionChange change_; const unsigned int index_; }; template class VectorView : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRtClassicComMix | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IVectorView>, ABI::Windows::Foundation::Collections::VectorChangedEventHandler< Logical>> { public: using LogicalT = Logical; using AbiT = Abi; explicit VectorView(Microsoft::WRL::ComPtr> vector) : vector_(std::move(vector)) { vector_->add_VectorChanged(this, &vector_changed_token_); } ~VectorView() { if (vector_) vector_->remove_VectorChanged(vector_changed_token_); } // ABI::Windows::Foundation::Collections::IVectorView: IFACEMETHODIMP GetAt(unsigned index, AbiT* item) override { return vector_ ? vector_->GetAt(index, item) : E_CHANGED_STATE; } IFACEMETHODIMP get_Size(unsigned* size) override { return vector_ ? vector_->get_Size(size) : E_CHANGED_STATE; } IFACEMETHODIMP IndexOf(AbiT value, unsigned* index, boolean* found) override { return vector_ ? vector_->IndexOf(std::move(value), index, found) : E_CHANGED_STATE; } IFACEMETHODIMP GetMany(unsigned start_index, unsigned capacity, AbiT* value, unsigned* actual) override { return vector_ ? vector_->GetMany(start_index, capacity, value, actual) : E_CHANGED_STATE; } // ABI::Windows::Foundation::Collections::VectorChangedEventHandler: IFACEMETHODIMP Invoke( ABI::Windows::Foundation::Collections::IObservableVector* sender, ABI::Windows::Foundation::Collections::IVectorChangedEventArgs* e) override { DCHECK_EQ(vector_.Get(), sender); vector_.Reset(); sender->remove_VectorChanged(vector_changed_token_); return S_OK; } private: Microsoft::WRL::ComPtr> vector_; EventRegistrationToken vector_changed_token_; }; template HRESULT CopyTo(const T& value, T* ptr) { *ptr = value; return S_OK; } template HRESULT CopyTo(const Microsoft::WRL::ComPtr& com_ptr, T** ptr) { return com_ptr.CopyTo(ptr); } template HRESULT CopyN(typename std::vector::const_iterator first, unsigned count, T* result) { std::copy_n(first, count, result); return S_OK; } template HRESULT CopyN( typename std::vector>::const_iterator first, unsigned count, T** result) { for (unsigned i = 0; i < count; ++i) CopyTo(*first++, result++); return S_OK; } template bool IsEqual(const T& lhs, const T& rhs) { return lhs == rhs; } template bool IsEqual(const Microsoft::WRL::ComPtr& com_ptr, const T* ptr) { return com_ptr.Get() == ptr; } } // namespace internal // This file provides an implementation of Windows::Foundation::IVector. It // functions as a thin wrapper around an std::vector, and dispatches method // calls to either the corresponding std::vector API or appropriate // std::algorithms. Furthermore, it notifies its observers whenever its // observable state changes. A base::win::Vector can be constructed for any type // T, and is implicitly constructible from a std::vector. In the case where T is // a pointer derived from IUnknown, the std::vector needs to be of type // Microsoft::WRL::ComPtr. This enforces proper reference counting and // improves safety. template class Vector : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>, ABI::Windows::Foundation::Collections::IVector>, ABI::Windows::Foundation::Collections::IObservableVector< internal::Logical>> { public: // windows.foundation.collections.h defines the following template and // semantics in Windows::Foundation::Internal: // // template // struct AggregateType; // // LogicalType - the Windows Runtime type (eg, runtime class, interface // group, etc) being provided as an argument to an _impl // template, when that type cannot be represented at the ABI. // AbiType - the type used for marshalling, ie "at the ABI", for the // logical type. using LogicalT = internal::Logical; using AbiT = internal::Abi; using StorageT = std::conditional_t::value, Microsoft::WRL::ComPtr>, AbiT>; Vector() = default; explicit Vector(const std::vector& vector) : vector_(vector) {} explicit Vector(std::vector&& vector) : vector_(std::move(vector)) {} // ABI::Windows::Foundation::Collections::IVector: IFACEMETHODIMP GetAt(unsigned index, AbiT* item) override { if (index >= vector_.size()) return E_BOUNDS; return internal::CopyTo(vector_[index], item); } IFACEMETHODIMP get_Size(unsigned* size) override { *size = vector_.size(); return S_OK; } IFACEMETHODIMP GetView( ABI::Windows::Foundation::Collections::IVectorView** view) override { return Microsoft::WRL::Make>(this).CopyTo( view); } IFACEMETHODIMP IndexOf(AbiT value, unsigned* index, boolean* found) override { auto iter = std::find_if(vector_.begin(), vector_.end(), [&value](const StorageT& elem) { return internal::IsEqual(elem, value); }); *index = iter != vector_.end() ? std::distance(vector_.begin(), iter) : 0; *found = iter != vector_.end(); return S_OK; } IFACEMETHODIMP SetAt(unsigned index, AbiT item) override { if (index >= vector_.size()) return E_BOUNDS; vector_[index] = std::move(item); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemChanged, index); return S_OK; } IFACEMETHODIMP InsertAt(unsigned index, AbiT item) override { if (index > vector_.size()) return E_BOUNDS; vector_.insert(std::next(vector_.begin(), index), std::move(item)); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemInserted, index); return S_OK; } IFACEMETHODIMP RemoveAt(unsigned index) override { if (index >= vector_.size()) return E_BOUNDS; vector_.erase(std::next(vector_.begin(), index)); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved, index); return S_OK; } IFACEMETHODIMP Append(AbiT item) override { vector_.push_back(std::move(item)); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemInserted, vector_.size() - 1); return S_OK; } IFACEMETHODIMP RemoveAtEnd() override { if (vector_.empty()) return E_BOUNDS; vector_.pop_back(); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_ItemRemoved, vector_.size()); return S_OK; } IFACEMETHODIMP Clear() override { vector_.clear(); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0); return S_OK; } IFACEMETHODIMP GetMany(unsigned start_index, unsigned capacity, AbiT* value, unsigned* actual) override { if (start_index > vector_.size()) return E_BOUNDS; *actual = std::min(vector_.size() - start_index, capacity); return internal::CopyN(std::next(vector_.begin(), start_index), *actual, value); } IFACEMETHODIMP ReplaceAll(unsigned count, AbiT* value) override { vector_.assign(value, std::next(value, count)); NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange_Reset, 0); return S_OK; } // ABI::Windows::Foundation::Collections::IObservableVector: IFACEMETHODIMP add_VectorChanged( ABI::Windows::Foundation::Collections::VectorChangedEventHandler< LogicalT>* handler, EventRegistrationToken* token) override { token->value = handler_id_++; handlers_.emplace_hint(handlers_.end(), token->value, handler); return S_OK; } IFACEMETHODIMP remove_VectorChanged(EventRegistrationToken token) override { handlers_.erase(token.value); return S_OK; } void NotifyVectorChanged( ABI::Windows::Foundation::Collections::CollectionChange change, unsigned int index) { auto args = Microsoft::WRL::Make(change, index); // Invoking the handlers could result in mutations to the map, thus we make // a copy beforehand. auto handlers = handlers_; for (auto& handler : handlers) handler.second->Invoke(this, args.Get()); } const std::vector& vector_for_testing() { return vector_; } private: ~Vector() override { // Handlers should not outlive the Vector. Furthermore, they must ensure // they are unregistered before the the handler is destroyed. This implies // there should be no handlers left when the Vector is destructed. DCHECK(handlers_.empty()); } std::vector vector_; base::flat_map*> handlers_; int64_t handler_id_ = 0; }; } // namespace win } // namespace base #endif // BASE_WIN_VECTOR_H_