Skip to content

Instantly share code, notes, and snippets.

@jonwis
Created November 19, 2023 07:15
Show Gist options
  • Save jonwis/1354089c149f30e609d91e4156b5641e to your computer and use it in GitHub Desktop.
Save jonwis/1354089c149f30e609d91e4156b5641e to your computer and use it in GitHub Desktop.
Yet another enum iterator attempt
#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{}});
}
@oldnewthing
Copy link

oldnewthing commented Nov 21, 2023

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.

@asklar
Copy link

asklar commented Nov 26, 2023

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