在迭代 IShellItemArray 时避免代码重复
Avoiding code repetition when iterating through an IShellItemArray
我正在编写一个作用于 IShellItemArray
的大型函数库。实际上,所有函数都需要单独访问数组中的每个 IShellItem
(更具体地说,每个 IShellItem2
)。因此,除非我忽略了文档中的某些内容,否则我相信我必须这样做:
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;
}
// ...
}
}
现在我正在尝试抽象化该迭代,这样我就不必每次都编写它。到目前为止,我已经尝试创建一个 ForEachShellItem
辅助函数来执行迭代并将所需的函数应用于每个项目,如下所示:
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);
}
}
问题是,如果必要的函数与函数指针参数的签名不同,那将不起作用。那么有没有一种方法可以概括或模板化这种方法呢?或者是否有任何其他策略来避免重复迭代代码?感谢您的任何输入。
您可以使用 std::function
和捕获 lambda 来做一些事情。因此,给定:
void ForEachShellItem(IShellItemArray*, std::function <HRESULT (IShellItem2 *)> fn)
{
...
fn (si);
}
然后要将附加参数传递给您的 lambda,您可以这样做:
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; });
并且要 return 一个额外的 return 值,你可以这样做:
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";
您还可以通过模板传递额外的参数和 return 值。例如:
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";
这是一种非模板方法。
您可以实现模板设计模式,同时重载调用运算符,operator()
。
这是一个例子:
#include <iostream>
class Base
{
public:
virtual ~Base() {}
virtual void operator()() {}
void GenericCaller() // This would be your SomeFunc
{
std::cout << "this part is generic\n";
operator()(); // This would be the custom function call
}
};
class Derived : public Base
{
int parm1, parm2;
public:
Derived(int p1, int p2) : parm1(p1), parm2(p2) {}
void operator()()
{
std::cout << "From Derived: " << parm1 << " " << parm2 << "\n";
}
};
class Derived2 : public Base
{
int parm1;
public:
Derived2(int p1) : parm1(p1) {}
void operator()()
{
std::cout << "From Derived2: " << parm1 << "\n";
}
};
void caller(Base& b)
{
b.GenericCaller();
}
int main()
{
Derived d1(1, 2);
Derived2 d2(3);
caller(d1);
caller(d2);
}
输出:
this part is generic
From Derived: 1 2
this part is generic
From Derived2: 3
它的工作方式是 GenericCaller
对所有 classes 是通用的,因此将始终被调用。请注意,最后会调用派生的调用运算符。
神奇的是参数列表从调用站点移到了派生的 class 构造函数中。请注意 Derived1
和 Derived2
具有不同的“参数列表”。
我正在编写一个作用于 IShellItemArray
的大型函数库。实际上,所有函数都需要单独访问数组中的每个 IShellItem
(更具体地说,每个 IShellItem2
)。因此,除非我忽略了文档中的某些内容,否则我相信我必须这样做:
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;
}
// ...
}
}
现在我正在尝试抽象化该迭代,这样我就不必每次都编写它。到目前为止,我已经尝试创建一个 ForEachShellItem
辅助函数来执行迭代并将所需的函数应用于每个项目,如下所示:
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);
}
}
问题是,如果必要的函数与函数指针参数的签名不同,那将不起作用。那么有没有一种方法可以概括或模板化这种方法呢?或者是否有任何其他策略来避免重复迭代代码?感谢您的任何输入。
您可以使用 std::function
和捕获 lambda 来做一些事情。因此,给定:
void ForEachShellItem(IShellItemArray*, std::function <HRESULT (IShellItem2 *)> fn)
{
...
fn (si);
}
然后要将附加参数传递给您的 lambda,您可以这样做:
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; });
并且要 return 一个额外的 return 值,你可以这样做:
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";
您还可以通过模板传递额外的参数和 return 值。例如:
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";
这是一种非模板方法。
您可以实现模板设计模式,同时重载调用运算符,operator()
。
这是一个例子:
#include <iostream>
class Base
{
public:
virtual ~Base() {}
virtual void operator()() {}
void GenericCaller() // This would be your SomeFunc
{
std::cout << "this part is generic\n";
operator()(); // This would be the custom function call
}
};
class Derived : public Base
{
int parm1, parm2;
public:
Derived(int p1, int p2) : parm1(p1), parm2(p2) {}
void operator()()
{
std::cout << "From Derived: " << parm1 << " " << parm2 << "\n";
}
};
class Derived2 : public Base
{
int parm1;
public:
Derived2(int p1) : parm1(p1) {}
void operator()()
{
std::cout << "From Derived2: " << parm1 << "\n";
}
};
void caller(Base& b)
{
b.GenericCaller();
}
int main()
{
Derived d1(1, 2);
Derived2 d2(3);
caller(d1);
caller(d2);
}
输出:
this part is generic
From Derived: 1 2
this part is generic
From Derived2: 3
它的工作方式是 GenericCaller
对所有 classes 是通用的,因此将始终被调用。请注意,最后会调用派生的调用运算符。
神奇的是参数列表从调用站点移到了派生的 class 构造函数中。请注意 Derived1
和 Derived2
具有不同的“参数列表”。