Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vulkan-hpp is reinterpret_casting non-standard-layout class to another class. Is this legal?

So recently I have been working with Vulkan-Hpp (The official c++ bindings of Vulkan Api, Github Link).

Looking into the source, I have found that they create wrapper classes around native Vulkan structs (e.g. vk::InstanceCreateInfo wraps around VkInstanceCreateInfo). (Note: Wrapping around, not deriving from)

When calling native Vulkan API, the pointers to wrapper classes are reinterpret_cast ed into the native Vulkan structs. An example using vk::InstanceCreateInfo:

//definition of vk::InstanceCreateInfo
struct InstanceCreateInfo
{
    /*  member function omitted  */
private:
  StructureType sType = StructureType::eInstanceCreateInfo;
public:
  const void* pNext = nullptr;
  InstanceCreateFlags flags;
  const ApplicationInfo* pApplicationInfo;
  uint32_t enabledLayerCount;
  const char* const* ppEnabledLayerNames;
  uint32_t enabledExtensionCount;
  const char* const* ppEnabledExtensionNames;
};

//definition of VkInstanceCreateInfo
typedef struct VkInstanceCreateInfo {
    VkStructureType             sType;
    const void*                 pNext;
    VkInstanceCreateFlags       flags;
    const VkApplicationInfo*    pApplicationInfo;
    uint32_t                    enabledLayerCount;
    const char* const*          ppEnabledLayerNames;
    uint32_t                    enabledExtensionCount;
    const char* const*          ppEnabledExtensionNames;
} VkInstanceCreateInfo;

//And the usage where reinterpret_cast takes place
template<typename Dispatch>
VULKAN_HPP_INLINE ResultValueType<Instance>::type createInstance( const InstanceCreateInfo &createInfo, Optional<const AllocationCallbacks> allocator, Dispatch const &d )
{
  Instance instance;
  Result result = static_cast<Result>( d.vkCreateInstance( reinterpret_cast<const VkInstanceCreateInfo*>( &createInfo ), reinterpret_cast<const VkAllocationCallbacks*>( static_cast<const AllocationCallbacks*>( allocator ) ), reinterpret_cast<VkInstance*>( &instance ) ) );
  return createResultValue( result, instance, VULKAN_HPP_NAMESPACE_STRING"::createInstance" );
}

So my question is: vk::InstanceCreateInfo and VkInstanceCreateInfo are two different types. Moreover, VkInstanceCreateInfo is standard layout but vk::InstanceCreateInfo is not (since it has mixed access specifiers). Is reinterpret_casting between the pointer of those two types (as done by Vulkan-Hpp) legal? Does this violate the strict aliasing rule?


Note: you can assume that VkInstanceCreateFlags and vk::InstanceCreateFlags are interchangeable in this case (otherwise it would make my question recursive)

like image 409
ph3rin Avatar asked Jul 11 '19 09:07

ph3rin


1 Answers

As far as the standard is concerned, yes, accessing a vk::InstanceCreateInfo object through a pointer to a VkInstanceCreateInfo object violates strict aliasing. And it would still violate strict aliasing even if both types were standard layout with equivalent layout. The standard doesn't allow you to just pretend that one class type is another, even if they have equivalent layouts.

So this code is relying on some implementation-specific behavior.

Is that wrong? Would doing an additional copy for every single Vulkan interface function call make you feel better about it, even if it would work without it? It's ultimately up to you how much UB you feel like tolerating.

like image 124
Nicol Bolas Avatar answered Nov 14 '22 13:11

Nicol Bolas