C++ 在迭代IShellItemArray时避免代码重复
我正在编写一个作用于C++ 在迭代IShellItemArray时避免代码重复,c++,templates,winapi,dry,C++,Templates,Winapi,Dry,我正在编写一个作用于IShellItemArrays的大型函数库。实际上,所有函数都需要单独访问数组中的每个IShellItem(更具体地说,每个IShellItem2)。因此,除非我忽略了文档中的某些内容,否则我相信我必须这样做: void SomeFunc(IShellItemArray* psia) { HRESULT hr; DWORD cItems; hr = psia->GetCount(&cItems); if (FAILED(hr)
IShellItemArray
s的大型函数库。实际上,所有函数都需要单独访问数组中的每个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;
}
// ...
}
}
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);
}
void ForEachShellItem(IShellItemArray*,std::function 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";
void ForEachShellItem(IShellItemArray*isa,std::function fn)
{
...
HRESULT hr=fn(磅/平方英寸);
}
IShellItemArray isa=/*…*/;
int附加参数=42;
ForEachShellItem(&isa,[附加参数](IShellItem2*psi)
{std::cout您可以使用std::function
和捕获lambda来做一些事情。因此,给定:
void ForEachShellItem(IShellItemArray*, std::function <HRESULT (IShellItem2 *)> fn)
{
...
fn (si);
}
void ForEachShellItem(IShellItemArray*,std::function 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";
void ForEachShellItem(IShellItemArray*isa,std::function fn)
{
...
HRESULT hr=fn(磅/平方英寸);
}
IShellItemArray isa=/*…*/;
int附加参数=42;
ForEachShellItem(&isa,[附加参数](IShellItem2*psi)
{std::cout这里是一种非模板方法
您可以实现模板设计模式,同时重载调用操作符,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);
}
其工作方式是,GenericCaller
对于所有类都是通用的,因此将始终被调用
神奇之处在于参数列表从调用站点移到派生类构造函数中。请注意,Derived1
和Derived2
具有不同的“参数列表”。这里是一种非模板方法
您可以实现模板设计模式,同时重载调用操作符,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);
}
其工作方式是,GenericCaller
对于所有类都是通用的,因此将始终被调用
神奇之处在于参数列表从调用站点移到派生类构造函数中。请注意,Derived1
和Derived2
具有不同的“参数列表”.将第二个参数作为std::function
或functor。将第二个参数作为std::function
或functor。使用这种方法,命名函数(例如ProcessShelItem
)可能是一个更好的选择,因为可以将回调实现到现有类中并使代码更具可读性。请注意,这基本上是在重新创建std::function
,但有一些隐藏的陷阱(例如,如果复制Base
,则会得到一个片段,这不会很好地结束)。使用这种方法,命名函数(例如,ProcessShelItem
)可能是一个更好的选择,因为可以将回调实现到现有类中,并使代码更具可读性。请注意,这基本上是在重新创建std::function
,但有一些隐藏的陷阱(例如,如果复制Base
,则会得到一个片段,这不会很好地结束)。模板方法有什么缺点吗?你和雷蒙德似乎喜欢std::function
方法,所以也许我会使用它,但至少现在模板策略对我来说更具可读性。另外,std::function
方法是否可以与常规命名函数而不是lambda一起使用?是的,应该是但lambda的优点是,您可以从其封闭范围捕获变量,如我在示例中所示。抱歉,刚才看到了您的第一个问题。模板方法的缺点是,模板的每个不同实例化都会复制代码。但这可能没什么大关系,正如您所说,它更方便,它也可以与常规函数一起使用。@loop123123我不想让一个优于另一个。我说“你可以使用A或B。”模板方法有什么缺点吗?看起来你和Raymond喜欢std::function
方法,所以也许我会使用它,但至少现在模板策略对我来说更易读。另外,std::function
方法是否可以与常规命名函数一起使用,而不是lambda?是的,这应该可以rk,但lambda的优点是,您可以从其封闭范围捕获变量,如我在示例中所示。抱歉,刚才看到了您的第一个问题。模板方法的缺点是,模板的每个不同实例化都会复制代码。但这可能没什么大关系,正如您所说,更方便的是它也可以与常规函数一起使用。@loop123123我不想让一个优于另一个。我说“你可以使用A或B。”