Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/141.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在迭代IShellItemArray时避免代码重复_C++_Templates_Winapi_Dry - Fatal编程技术网

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。”