Vulkan-Hpp: Add RAII support with VULKAN_HPP_NO_EXCEPTIONS

Google / Android does not use exceptions (see here). However, the RAII classes are very useful!

Could we update the RAII generator to guard the throwing constructors for handle types using:

#ifndef VULKAN_HPP_NO_EXCEPTIONS
  Image(...)
  {
     ...
     if (result != success)
     {
       throwResultException(...);
     }
  }
#endif

guard the throwResultException() function definition, and introduce static “create” functions that return a std::expected<HandleType, vk::Result> using:

  static std::expected<Image, VULKAN_HPP_NAMESPACE::Result> create(...)
  {
    ...
    if (result != success)
    {
      return std::unexpected(result);
    }
    return Image(...);
  }

Bonus points if we can use custom defines such as VULKAN_HPP_RAII_EXPECTED_CLASS /VULKAN_HPP_RAII_UNEXPECTED_CLASS to use custom “expected” implementations when compiling for C++ version < 23 (for example, android::base::expected). If these VULKAN_HPP_RAII_EXPECTED_CLASS /VULKAN_HPP_RAII_UNEXPECTED_CLASS defines are not defined, potentially need to guard the create() functions based on the version.

See https://android-review.googlesource.com/c/device/google/cuttlefish/+/2404731 for a quick attempt at this.

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 26 (21 by maintainers)

Most upvoted comments

What if RAII provided it’s own vk::ValueResult (or whatever it’s named) that implements a usable subset of the std::expected interface.

Yep, that’s the way to go!

I might have a go at implementing it and see if I get something workable.

vk::ResultValue already exists, so we might need name it vk::Expected.

I figure keep it out of the vk::raii namespace because it’s not really an RAII object, and maybe we can eventually backport it to Vulkan.hpp as a configurable replacement for ResultValue.

I did some experiments on top of @jmacnak’s code (see my WIP branch is here. No guarantee it actually runs)

With a careful choice of defines, it’s possible to make the it work with both the std::expected interface and something more basic like a std::tuple

// with tuple
#define VULKAN_HPP_RAII_EXPECTED_CLASS std::tuple
#define VULKAN_HPP_RAII_EXPECTED(obj) std::make_tuple(obj, VULKAN_HPP_NAMESPACE::Result::eSuccess )
#define VULKAN_HPP_RAII_UNEXPECTED(type, error) std::make_tuple(type(nullptr), error)
// with std::expect (untested)
#define VULKAN_HPP_RAII_EXPECTED_CLASS std::expect
#define VULKAN_HPP_RAII_EXPECTED(obj) obj
#define VULKAN_HPP_RAII_UNEXPECTED(type, error) std::unexpected(error)

However, I spent some time porting my codebase over using tuples for error returns, and it’s less than ideal. Very tempting to use std::tie, which made it very easy for the nullptr constructed RAII objects to escape into the rest of application’s codebase. Feels unclean and I’m very tempted to just borrow android’s implementation of std::expected.

But an alternative proposal for supporting older versions of C++ What if RAII provided it’s own vk::ValueResult (or whatever it’s named) that implements a usable subset of the std::expected interface. Make it hard-coded to vk::Result and supply the basic accessors. Then when the headers are compiled with a c++23 compiler (or a supplied alternative implementation of std::expected) they can implement vk::ValueResult as just a type alias and the user will get the full functionality of std::expected, without breaking old code.

why so many macros instead of using namespace alias, constexpr functions, templated type aliases, etc.

My understanding is that this is fairly common in library code. Macros are the most straightforward way to provide similar functionality across various C++ versions. They also make generated code easier to deal with, from what I’ve seen.

Templated types can potentially explode compile times, and Vulkan-Hpp is already huge as it is (I’ve literally never seen a larger header file, we are approaching 100K lines).

We actually do have constexpr functions, but these are for the users to use.