I'm writing a large library of functions that act on IShellItemArray
s. Practically all of the functions need to access each IShellItem
(and more specifically, each IShellItem2
) in the array individually. So unless I'm overlooking something in the documentation, I believe I have to do something like this:
void SomeFunc(IShellItemArray* psia)
{
HRESULT hr;
DWORD cItems;
hr = psia->GetCount(&cItems);
if (FAILED(hr) || cItems == 0)
{
return;
}
for (UINT i = 0; i < cItems; ++i)
{
CComPtr<IShellItem> pShellItem;
hr = psia->GetItemAt(i, &pShellItem);
if (FAILED(hr))
{
continue;
}
CComPtr<IShellItem2> pShellItem2;
pShellItem->QueryInterface(&pShellItem2);
if (FAILED(hr))
{
continue;
}
// ...
}
}
Now I'm trying to abstract that iteration away so I don't have to write it every time. So far I've tried to create a ForEachShellItem
helper function that does the iteration and applies a desired function to each item, like this:
void ForEachShellItem(IShellItemArray* psia, HRESULT(*fn)(IShellItem2*))
{
HRESULT hr;
DWORD cItems;
hr = psia->GetCount(&cItems);
if (FAILED(hr) || cItems == 0)
{
return;
}
for (UINT i = 0; i < cItems; ++i)
{
CComPtr<IShellItem> pShellItem;
hr = psia->GetItemAt(i, &pShellItem);
if (FAILED(hr))
{
continue;
}
CComPtr<IShellItem2> pShellItem2;
pShellItem->QueryInterface(&pShellItem2);
if (FAILED(hr))
{
continue;
}
fn(pShellItem2);
}
}
The problem is that if the necessary function is of a different signature than that function pointer parameter, then that won't work. So is there a way to generalize or templatize this approach? Or is there any other strategy to avoid repetition of the iteration code? Thanks for any input.
There are things you can do with std::function
and a capturing lambda. Thus, given:
void ForEachShellItem(IShellItemArray*, std::function <HRESULT (IShellItem2 *)> fn)
{
...
fn (si);
}
Then to pass an additional parameter to your lambda, you can do:
void ForEachShellItem(IShellItemArray *isa, std::function <HRESULT (IShellItem2 *psi)> fn)
{
...
HRESULT hr = fn (psi);
}
IShellItemArray isa = /* ... */;
int additional_param = 42;
ForEachShellItem (&isa, [additional_param] (IShellItem2 *psi)
{ std::cout << additional_param; return 0; });
And to return an additional return value, you can do:
IShellItemArray isa = /* ... */;
int additional_return_value = 0;
ForEachShellItem (&isa, [&additional_return_value] (IShellItem2 *psi)
{ additional_return_value = 43; return 0; });
std::cout << additional_return_value << "\n";
You can also pass additional parameters and return values via a template. For example:
template <typename F, typename ... Args>
void ForEachShellItem(IShellItemArray*, F fn, Args && ... args)
{
...
fn (si, std::forward <Args> (args)...);
}
IShellItemArray isa = /* ... */;
int additional_return_value = 0;
ForEachShellItem (&isa, [] (IShellItem2 *, int additional_param, int &additional_return_value)
{ std::cout << additional_param << "\n"; additional_return_value = 43; return 0; },
42, additional_return_value);
std::cout << additional_return_value << "\n";