Created
November 19, 2023 07:15
-
-
Save jonwis/1354089c149f30e609d91e4156b5641e to your computer and use it in GitHub Desktop.
Yet another enum iterator attempt
This file contains 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
#include <wil/com.h> | |
// Wraps a type like IEnumIUknown and exposes it as a forward iterator, | |
// or like IEnumIDList and exposes it as a forward iterator of type TStoredType. | |
// The IEnumType must have the following methods: | |
// HRESULT Next(ULONG celt, T* rgelt, ULONG* pceltFetched) | |
template <typename IEnumType, typename TStoredType> | |
struct iterator | |
{ | |
wil::com_ptr<IEnumType> m_enum{}; | |
uint32_t m_offset{0}; | |
bool m_isEnd{false}; | |
TStoredType m_currentValue{}; | |
struct end_tag_t{ }; | |
iterator(iterator&&) = default; | |
iterator(iterator const&) = delete; | |
iterator& operator=(iterator&&) = default; | |
iterator& operator=(iterator const&) = delete; | |
iterator(IEnumType* enumPtr) : m_enum(enumPtr) | |
{ | |
FetchNext(); | |
} | |
explicit iterator(end_tag_t) : m_isEnd(true) | |
{ | |
} | |
auto operator->() | |
{ | |
return wistd::addressof(m_currentValue); | |
} | |
auto& operator*() | |
{ | |
return m_currentValue; | |
} | |
iterator& operator++() | |
{ | |
// If we're already at the end, don't try to advance. Otherwise, use Next to advance. | |
if (!m_isEnd) | |
{ | |
FetchNext(); | |
} | |
return *this; | |
} | |
bool operator!=(iterator const& other) const | |
{ | |
return !(*this == other); | |
} | |
bool operator==(iterator const& other) const | |
{ | |
return (m_isEnd == other.m_isEnd) || | |
(m_enum == other.m_enum && m_offset == other.m_offset); | |
} | |
private: | |
void FetchNext() | |
{ | |
if (!m_isEnd) | |
{ | |
m_currentValue = {}; | |
auto hr = m_enum->Next(1, &m_currentValue, nullptr); | |
if (hr == S_FALSE) | |
{ | |
m_isEnd = true; | |
} | |
else if (FAILED(hr)) | |
{ | |
throw winrt::hresult_error(hr, L"Failed to get next"); | |
} | |
else | |
{ | |
++m_offset; | |
} | |
} | |
} | |
}; | |
auto make_range(IEnumUnknown* enumPtr) | |
{ | |
using i = iterator<IEnumUnknown, wil::com_ptr<::IUnknown>>; | |
return std::make_pair<i, i>(enumPtr, i{i::end_tag_t{}}); | |
} |
Aw man, gists don't have reactions. Take this 🎉 instead.
I was confused by the comment at the top "exposes it as a forward iterator of type TStoredType.", because the iterator<Interface, Result>
is not a forward iterator, doesn't even try to be. It's not copyable or assignable. Really, it's an input iterator not a forward iterator.
this is kind of what I had in the original PR: microsoft/wil#234
the problem is that we need a bit more traits for the output type for Next: e.g. PIDLs aren't COM, but should be wrapped in something like a wil::unique_cotaskmemptr to not be leaked. So perhaps something with a traits that can be specialized would work?
Or maybe I'm overthinking and this can be
wil::com_ptr<IEnumIDList> enumPidls;
folder>EnumObjects(..., &enumPidls);
for (auto pidl : enumPidls)
{
// use the pidl
ILFree(pidl);
}
thoughts?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Now you can create an
iterator<IEnumXxx>
and we will deduceTStoredType
to be the thing thatIEnumXxx::Next
produces, and wrap it in acom_ptr
if it's a COM interface. (If not, and there is cleanup necessary, then you must specify aunique_mystruct
as the result type so the results aren't leaked.)We can get rid of
is_end
by settingm_enum = nullptr
when we reach the end. The empty iterator then acts as theend
iterator.We can get rid of
m_offset
. Input iterators are not required to support meaningful==
for non-end iterators, because applying++
to an input iterator invalidates any copies of the unincremented iterator (so they are not legal to pass to==
).