Last active
January 22, 2023 12:04
-
-
Save kennykerr/db60f25afea7c42387df2742f714c394 to your computer and use it in GitHub Desktop.
How to implement IObservableVector<T> with C++/WinRT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace Windows::Foundation::Collections; | |
template <typename T> | |
struct observable_vector : implements<observable_vector<T>, | |
IObservableVector<T>, | |
IVector<T>, | |
IVectorView<T>, | |
IIterable<T>> | |
{ | |
event_token VectorChanged(VectorChangedEventHandler<T> const& handler) | |
{ | |
return m_changed.add(handler); | |
} | |
void VectorChanged(event_token const cookie) | |
{ | |
m_changed.remove(cookie); | |
} | |
T GetAt(uint32_t const index) const | |
{ | |
if (index >= m_values.size()) | |
{ | |
throw hresult_out_of_bounds(); | |
} | |
return m_values[index]; | |
} | |
uint32_t Size() const noexcept | |
{ | |
return static_cast<uint32_t>(m_values.size()); | |
} | |
IVectorView<T> GetView() | |
{ | |
return *this; | |
} | |
bool IndexOf(T const& value, uint32_t& index) const noexcept | |
{ | |
index = static_cast<uint32_t>(std::find(m_values.begin(), m_values.end(), value) - m_values.begin()); | |
return index < m_values.size(); | |
} | |
void SetAt(uint32_t const index, T const& value) | |
{ | |
if (index >= m_values.size()) | |
{ | |
throw hresult_out_of_bounds(); | |
} | |
++m_version; | |
m_values[index] = value; | |
m_changed(*this, make<args>(CollectionChange::ItemChanged, index)); | |
} | |
void InsertAt(uint32_t const index, T const& value) | |
{ | |
if (index > m_values.size()) | |
{ | |
throw hresult_out_of_bounds(); | |
} | |
++m_version; | |
m_values.insert(m_values.begin() + index, value); | |
m_changed(*this, make<args>(CollectionChange::ItemInserted, index)); | |
} | |
void RemoveAt(uint32_t const index) | |
{ | |
if (index >= m_values.size()) | |
{ | |
throw hresult_out_of_bounds(); | |
} | |
++m_version; | |
m_values.erase(m_values.begin() + index); | |
m_changed(*this, make<args>(CollectionChange::ItemRemoved, index)); | |
} | |
void Append(T const& value) | |
{ | |
++m_version; | |
m_values.push_back(value); | |
m_changed(*this, make<args>(CollectionChange::ItemInserted, Size() - 1)); | |
} | |
void RemoveAtEnd() | |
{ | |
if (m_values.empty()) | |
{ | |
throw hresult_out_of_bounds(); | |
} | |
++m_version; | |
m_values.pop_back(); | |
m_changed(*this, make<args>(CollectionChange::ItemRemoved, Size())); | |
} | |
void Clear() noexcept | |
{ | |
++m_version; | |
m_values.clear(); | |
m_changed(*this, make<args>(CollectionChange::Reset, 0)); | |
} | |
uint32_t GetMany(uint32_t const startIndex, array_view<T> values) const | |
{ | |
if (startIndex >= m_values.size()) | |
{ | |
return 0; | |
} | |
uint32_t actual = static_cast<uint32_t>(m_values.size() - startIndex); | |
if (actual > values.size()) | |
{ | |
actual = values.size(); | |
} | |
std::copy_n(m_values.begin() + startIndex, actual, values.begin()); | |
return actual; | |
} | |
void ReplaceAll(array_view<T const> value) | |
{ | |
++m_version; | |
m_values.assign(value.begin(), value.end()); | |
m_changed(*this, make<args>(CollectionChange::Reset, 0)); | |
} | |
IIterator<T> First() | |
{ | |
return make<iterator>(this); | |
} | |
private: | |
std::vector<T> m_values; | |
agile_event<VectorChangedEventHandler<T>> m_changed; | |
uint32_t m_version{}; | |
struct args : implements<args, IVectorChangedEventArgs> | |
{ | |
args(CollectionChange const change, uint32_t const index) : | |
m_change(change), | |
m_index(index) | |
{ | |
} | |
CollectionChange CollectionChange() const | |
{ | |
return m_change; | |
} | |
uint32_t Index() const | |
{ | |
return m_index; | |
} | |
private: | |
Windows::Foundation::Collections::CollectionChange const m_change{}; | |
uint32_t const m_index{}; | |
}; | |
struct iterator : implements<iterator, IIterator<T>> | |
{ | |
explicit iterator(observable_vector<T>* owner) noexcept : | |
m_version(owner->m_version), | |
m_current(owner->m_values.begin()), | |
m_end(owner->m_values.end()) | |
{ | |
m_owner.copy_from(owner); | |
} | |
void abi_enter() const | |
{ | |
if (m_version != m_owner->m_version) | |
{ | |
throw hresult_changed_state(); | |
} | |
} | |
T Current() const | |
{ | |
if (m_current == m_end) | |
{ | |
throw hresult_out_of_bounds(); | |
} | |
return*m_current; | |
} | |
bool HasCurrent() const noexcept | |
{ | |
return m_current != m_end; | |
} | |
bool MoveNext() noexcept | |
{ | |
if (m_current != m_end) | |
{ | |
++m_current; | |
} | |
return HasCurrent(); | |
} | |
uint32_t GetMany(array_view<T> values) | |
{ | |
uint32_t actual = static_cast<uint32_t>(std::distance(m_current, m_end)); | |
if (actual > values.size()) | |
{ | |
actual = values.size(); | |
} | |
std::copy_n(m_current, actual, values.begin()); | |
std::advance(m_current, actual); | |
return actual; | |
} | |
private: | |
com_ptr<observable_vector<T>> m_owner; | |
uint32_t const m_version; | |
typename std::vector<T>::const_iterator m_current; | |
typename std::vector<T>::const_iterator const m_end; | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment