c++ 将Vulkan的VkInstance Package 到unique_ptr中,无需额外的动态分配

z3yyvxxp  于 2023-02-26  发布在  其他
关注(0)|答案(4)|浏览(146)

我试图将VkInstance(不透明指针) Package 到unique_ptr中,但似乎无法实现。

...
    VkInstance instance;
    if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
        throw std::runtime_error("failed to create vulkan instance");
    }

    auto del = [](VkInstance* p) {
        DBG("release vk instance");
        vkDestroyInstance(*p, nullptr);
    };
    auto ptr = std::unique_ptr<VkInstance, decltype(del)>(instance, del);
...

错误:

no instance of constructor "std::unique_ptr<_Tp, _Dp>::unique_ptr [with _Tp=VkInstance, _Dp=lambda [](VkInstance *p)->void]" matches the argument list

我不知道为什么,VkInstance是一个指针,所以我传递它,deleter必须接受指向内存地址的指针,所以它仍然接收它,但是类型仍然不匹配。
&去引用它并将其传递给make_unique会导致segfault。
唯一的方法,我设法使它的工作只与额外的新调用,像这样:

...
    VkInstance* instance = new VkInstance;
    if (vkCreateInstance(&createInfo, nullptr, instance) != VK_SUCCESS) {
        throw std::runtime_error("failed to create vulkan instance");
    }

    auto del = [](VkInstance* p) {
        DBG("release vk instance");
        vkDestroyInstance(*p, nullptr);
    };
    auto ptr = std::unique_ptr<VkInstance, decltype(del)>(instance, del);
...

但这是一个有点荒谬的解决方案,因为我是动态分配的东西,应该放在一个CPU寄存器,并几乎立即转移到unique_ptr控制区。
那么,我能以某种方式实现我试图做的事情,而不需要进一步的过度设计吗?

3duebb1j

3duebb1j1#

std::unique_ptr可以处理甚至不是指针的类型;它当然可以与VkInstance这样的不透明指针类型一起工作。但是,您必须知道如何按照std::unique_ptr预期的方式做事。
关键在于:unique_ptr的deleter类型有很多有用的功能,如果你使用lambda类型,这些功能都不能用。另外,如果你想使用一个函子的类型名,必须做decltype体操,而不是仅仅给它一个名字,这有点俗气。
因此,做一个适当的删除器:

struct VkInstanceDeleter
{
  using pointer = VkInstance;

  void operator()(VkInstance inst) {vkDestroyInstance(inst, nullptr);}
};

using InstPtr = std::unique_ptr<VkInstance, VkInstanceDeleter>;

InstPtr现在可以用来管理VkInstance对象,唯一的问题是你不能在上面使用->,但这在Vulkan中没有任何意义。

cbjzeqam

cbjzeqam2#

我想您在将“instance”对象传递给std::unique_ptr构造函数时忘记了该对象上的&:

#include <iostream>
#include <memory>

struct Foo
{
};

int main()
{
    Foo f;
    auto deleter = [](Foo* f){std::cout << "deleting it!\n";};
    auto ptr = std::unique_ptr<Foo, decltype(deleter)>(&f, deleter);
}

您可以在这里尝试:https://tio.run/##RY1BDoIwFETX/FN8MTGtwQvQytJLqDGkFNIE2gq/C0I4e61E4vbNzBvl/aVTKsajsaoPjUZp3ESjrocK/mzQgxvnCiBFQRHenIMFVgFgLOFQG8t4Alni2ArI6kAOG91r0iNe8f5kKTljy5eJmrJULhBKiflWMbZDQ4eHzcW6bz19d1s5WPMO@pWITJIiaVVPs9fs5@cVO7XF/sYFrDF@AA

lhcgjxsq

lhcgjxsq3#

问题是VkInstance是一个指针,而不是“指向类型”,所以基本上你应该有类似std::unique_ptr<VkInstancePointee>的东西。但是因为我们没有这种类型,这基本上是Vulkan的实现细节,可以在不通知的情况下更改,所以最好不要使用它。所以如果你想要RAII行为,你只需要创建 Package 器。类似这样:

class VkInstanceWrapper
{
public:
  static VkInstanceWrapper Create(const VkInstanceCreateInfo* createInfo) {
    return VkInstanceWrapper(createInfo);
  }

  VkInstanceWrapper(const VkInstanceCreateInfo* createInfo) {
    if (vkCreateInstance(createInfo, nullptr, &instance) != VK_SUCCESS) {
      throw std::runtime_error("failed to create vulkan instance");
    }
  }

  ~VkInstanceWrapper() {
    vkDestroyInstance(&instance, nullptr);
  }

  // To use in other vulkan calls
  operator VkInstance *() {
    return &instance;
  }

  // You need to delete copy constructor/assignment
  VkInstanceWrapper(const VkInstanceWrapper&) = delete;
  VkInstanceWrapper& operator=(const VkInstanceWrapper&) = delete;

  // You can either define or delete move constructor/assignment
  VkInstanceWrapper(VkInstanceWrapper&&) = delete;
  VkInstanceWrapper& operator=(VkInstanceWrapper&&) = delete;

private:
  VkInstance instance;
};
wgx48brx

wgx48brx4#

所以,经过更多的研究,我想我需要这样的东西:

  1. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3677.html
  2. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3830.pdf
  3. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3949.pdf
    从这里找到了这些:非指针上RAII的一行程序?
    这些都没有实现,但是符合一般的RAII概念,所以,看起来唯一指针只用于指针分配的资源,而不是值资源,在我的例子中是不透明句柄。

相关问题