我正在编写一个对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);
}
}
问题在于,如果必需的函数具有与该函数指针参数不同的签名,则该函数将不起作用。那么,有没有一种方法可以概括或模板化此方法?还是有其他策略可以避免重复执行迭代代码?感谢您的输入。
答案 0 :(得分:2)
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; });
要返回其他返回值,您可以执行以下操作:
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";
您还可以通过模板传递其他参数并返回值。例如:
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";
答案 1 :(得分:0)
这是一种非模板方法。
您可以实现模板设计模式,并重载调用运算符http://sohaib94.github.io/cv/
。
这里是一个例子:
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
对所有类都是通用的,因此将始终被调用。请注意,最后将调用派生的call运算符。
魔术是将参数列表从调用站点移到派生类构造函数中。请注意,GenericCaller
和Derived1
具有不同的“参数列表”。