Add helper class to RAII manage OpenXR handles
authorJulian Eisel <eiseljulian@gmail.com>
Wed, 17 Jul 2019 11:35:14 +0000 (13:35 +0200)
committerJulian Eisel <eiseljulian@gmail.com>
Wed, 17 Jul 2019 11:35:14 +0000 (13:35 +0200)
Adds generic unique_oxr_ptr to wrap xrCreate and xrDestroy functions of
OpenXR handles into a unique_ptr like RAII interface.

While for most cases, OpenXR resources can be freed by their owning
object, sometimes errors may occor before final ownership is established.
E.g. swapchain ownership is only transfered to the session object once
its swapchain-images are created - which may fail. With this RAII
wrapper, the swapchain would be freed on error (as this triggers stack
unwinding through an exception), no matter who holds ownership to it
currently.
There are other solutions to this problem, e.g. by establishing final
ownership right after/upon creation, or by explicit freeing in case an
error is spotted; it's too easy to make mistakes here though. Plus, we
may want to experiment with using this API for all OpenXR resources, to
entirely avoid the possibility of them leaking.

intern/ghost/intern/GHOST_Xr_intern.h

index c495a6c4468aa371f5c2b4f0bf3f468e95e68cd7..b0258ae36a160d842b5cfcdc69131332a3f0d66f 100644 (file)
   } \
   (void)0
 
+/**
+ * Helper for RAII usage of OpenXR handles (defined with XR_DEFINE_HANDLE). This is based on
+ * `std::unique_ptr`, to give similar behavior and usage (e.g. functions like #get() and #release()
+ * are supported).
+ */
+template<typename _OXR_HANDLE> class unique_oxr_ptr {
+ public:
+  using xr_destroy_func = XrResult (*)(_OXR_HANDLE);
+
+  unique_oxr_ptr(xr_destroy_func destroy_fn) : m_destroy_fn(destroy_fn)
+  {
+  }
+
+  unique_oxr_ptr(unique_oxr_ptr &&other) : m_ptr(other.m_ptr), m_destroy_fn(other.m_destroy_fn)
+  {
+  }
+
+  /**
+   * Construct the pointer from a xrCreate function passed as \a create_fn, with additional
+   * arguments forwarded from \a args.
+   * For example, this:
+   * \code
+   * some_unique_oxr_ptr.contruct(xrCreateFoo, some_arg, some_other_arg);
+   * \endcode
+   * effectively results in this call:
+   * \code
+   * xrCreateFoo(some_arg, some_other_arg, &some_unique_oxr_ptr.m_ptr);
+   * \endcode
+   */
+  template<typename _create_func, typename... _Args>
+  XrResult construct(_create_func create_fn, _Args &&... args)
+  {
+    assert(m_ptr == XR_NULL_HANDLE);
+    return create_fn(std::forward<_Args>(args)..., &m_ptr);
+  }
+
+  ~unique_oxr_ptr()
+  {
+    if (m_ptr != XR_NULL_HANDLE) {
+      m_destroy_fn(m_ptr);
+    }
+  }
+
+  _OXR_HANDLE get()
+  {
+    return m_ptr;
+  }
+
+  _OXR_HANDLE release()
+  {
+    _OXR_HANDLE ptr = get();
+    m_ptr = XR_NULL_HANDLE;
+    return ptr;
+  }
+
+  /* operator= defines not needed for now. */
+
+  unique_oxr_ptr(const unique_oxr_ptr &) = delete;
+  unique_oxr_ptr &operator=(const unique_oxr_ptr &) = delete;
+
+ private:
+  _OXR_HANDLE m_ptr{XR_NULL_HANDLE};
+  xr_destroy_func m_destroy_fn;
+};
+
 #endif /* __GHOST_XR_INTERN_H__ */