【问题标题】:How to create a variadic struct that takes a "variadic invocable" of the same arity as the ctor parameter?如何创建一个可变参数结构,它采用与 ctor 参数相同的数量的“可变参数调用”?
【发布时间】:2021-08-26 23:07:39
【问题描述】:

我试图概括这个 COM 对象自动管理类,但我什至不确定它是否可能。目前,我已经为 1、2 和 3 指针定义了它,但我想让它成为适用于所有情况的单一实现。

手动实施:

#include <concepts>
#include <type_traits>

#include <Unknwn.h>

template <std::derived_from<IUnknown> COMInterface>
struct AutoManagedCOMObj {
    COMInterface* ptr;

    template<std::invocable<COMInterface**> Invocable>
    AutoManagedCOMObj(Invocable initializer) {
        HRESULT hr = initializer(&ptr);
        if (FAILED(hr)) exit(hr);
    }
    ~AutoManagedCOMObj() {
        ptr->Release();
    }
};

template <
    std::derived_from<IUnknown> COMInterface1,
    std::derived_from<IUnknown> COMInterface2
>
struct AutoManaged2COMObjs {
    COMInterface1* ptr1;
    COMInterface2* ptr2;

    template<std::invocable<COMInterface1**, COMInterface2**> Invocable>
    AutoManaged2COMObjs(Invocable initializer) {
        HRESULT hr = initializer(&ptr1, &ptr2);
        if (FAILED(hr)) exit(hr);
    }
    ~AutoManaged2COMObjs() {
        ptr2->Release();
        ptr1->Release();
    }
};

template <
    std::derived_from<IUnknown> COMInterface1,
    std::derived_from<IUnknown> COMInterface2,
    std::derived_from<IUnknown> COMInterface3
>
struct AutoManaged3COMObjs {
    COMInterface1* ptr1;
    COMInterface2* ptr2;
    COMInterface3* ptr3;

    template<std::invocable<COMInterface1**, COMInterface2**, COMInterface3**> Invocable>
    AutoManaged3COMObjs(Invocable initializer) {
        HRESULT hr = initializer(&ptr1, &ptr2, &ptr3);
        if (FAILED(hr)) exit(hr);
    }
    ~AutoManaged3COMObjs() {
        ptr3->Release();
        ptr2->Release();
        ptr1->Release();
    }
};

一般解决方案的初始尝试

template<
    std::derived_from<IUnknown> ... T
>
struct AutoManagedCOMObjs {};

template<
    std::derived_from<IUnknown> COMInterface,
    std::derived_from<IUnknown>... Rest
>
struct AutoManagedCOMObjs<COMInterface, Rest ...> {
    COMInterface* first;
    AutoManagedCOMObjs<Rest ...> rest;

    template<std::invocable<COMInterface**, Rest** ...> Invocable>
    AutoManagedCOMObjs(Invocable initializer) {
        HRESULT hr = initializer(&first, &rest...);
        if (FAILED(hr)) exit(hr);
    }
    ~AutoManagedCOMObjs() {
        // the dtor is an unsolved problem as well
        //ptr2->Release();
        //ptr1->Release();
    }
};

我无法找到将结构声明中的 ... Restinvocable 参数签名联系起来的方法。

如果有帮助的话。 VS 在告诉我

错误“...”:没有可用的参数包 展开(指 ctor 的第一行)。

对于上下文,这就是我使用这些类之一的方式:

AutoManagedCOMObj<ID3D11Texture2D> back_buffer_interface([](ID3D11Texture2D** ptr){
    return swap_chain->GetBuffer(0, uuid(ptr), (void**)ptr);        
});

【问题讨论】:

  • 你的问题很清楚,但如果你发minimal reproducible example 会很有帮助。如果无法访问您未显示的标题,我们必须自己重新创建您的情况。
  • (1) 有什么理由使用原始指针(例如COMInterface1* ptr1;)而不是一些智能指针(RAII)? (2) 不初始化指向nullptr 的指针? (3) 不检查它们不是空值就释放它们? (4) 在发生错误时调用exit,而不是抛出异常并进行适当的清理。 (5) 要使用 C 样式转换而不是 reinterpret_cast&lt;void **&gt;(ptr)(6) 在使用代码 (back_buffer_interface) 时不要将该函数作为模板以避免强制转换? (7) 在掌握基本良好实践之前尝试编写高级 C++ 可变参数模板?

标签: c++ variadic-templates c++-concepts


【解决方案1】:

std::tuple(与std::apply)可能会有所帮助:

template <std::derived_from<IUnknown>... Ts>
struct AutoManagedCOMObj
{
    std::tuple<Ts*...> tuple_ptrs;

    template<std::invocable<Ts**...> Invocable>
    AutoManagedCOMObj(Invocable initializer)
    {
        HRESULT hr = std::apply([&](auto*&... ptrs){ return initializer(&ptrs...);},
                                tuple_ptrs);
        if (FAILED(hr)) exit(hr);
    }
    ~AutoManagedCOMObj()
    {
        std::apply([](auto*... ptrs){ (ptrs->Release(), ...); }, tuple_ptrs);
    }
};

【讨论】:

  • 哇,元组比我最初意识到的要灵活得多,非常感谢!
猜你喜欢
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2018-10-05
  • 1970-01-01
  • 2015-01-02
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多