【问题标题】:Comparing two structs of the same type from external API从外部 API 比较两个相同类型的结构
【发布时间】:2020-04-24 21:49:11
【问题描述】:

所以基本上我试图比较两个来自 Vulkan 的 VkPhysicalDeviceFeatures,一个来自我正在查看的 VkPhysicalDevice,另一个对应于我实际需要的一组功能。 VkPhysicalDeviceFeatures struct only 包含 VkBool32 成员(它们是 uint32_t 的类型定义),但 vulkan 的每个次要版本都可以添加未知数量的这些功能。我想做的只是将每个结构的成员相互比较,而不是为了相等,更多的是逻辑比较。如果物理设备结构中的相应成员为假,但我的结构对该成员具有真,那么比较应该返回假。

我能想到的唯一方法就是this answer 发布的内容:

bool hasRequiredFeatures(VkPhysicalDevice physical_device,
                              VkPhysicalDeviceFeatures required_features) {

    VkPhysicalDeviceFeatures physical_device_features = getSupportedFeatures(physical_device);
    std::size_t struct_length = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32);

    auto physical_device_features_bool_ptr = reinterpret_cast<VkBool32*>(&physical_device_features);
    auto required_features_bool_ptr = reinterpret_cast<VkBool32*>(&required_features);
    for(std::size_t i = 0; i < struct_length; ++i){
        if(physical_device_features_bool_ptr[i] == VK_FALSE && required_features_bool_ptr[i] == VK_TRUE){
            return false;
        }
    }
    return true;
}

这可以满足我的要求(尽管有一种方法可以通过名称查看哪个特定成员未能通过比较,但我想如果没有反射这是不可能的)但我认为 C++ 不能保证严格对齐像这样?我有没有跨平台的方式来完成这个?

【问题讨论】:

  • 最安全的选择可能是magic_get。关于您的方法:虽然理论上不能保证缺少填充,但在这种情况下,编译器不需要添加它(除非以某种方式涉及alignas)。但如果我没记错的话,像这样在reinterpret_casted VkBool32 上进行指针运算就是UB。我认为在 char * 上进行算术运算(然后将指针转换为正确的类型)是明确定义的,但我不是 100% 确定。

标签: c++ struct c++17 vulkan struct-member-alignment


【解决方案1】:

您的方法存在一个大问题,它与 C++ 的关系不大,而与 Vulkan 的关系更大。具体来说:

小版本的 vulkan 可以添加未知数量的这些结构

这告诉我你打算将这种技术应用于VkPhysicalDeviceFeatures2,以及任何可以出现在其pNext 链中的特征结构。因此,“这些结构的数量未知”。

好吧,事情是这样的:VkPhysicalDeviceFeatures2 不仅仅是一堆VkBools。它是一个可扩展的 Vulkan 结构,这意味着它以此类结构共有的 sTypepNext 字段开头。所有 1.0 后的 Vulkan 设备功能结构也是如此。

可以执行您在 1.0 后特性结构中声明的代码必须能够获取整个 pNext 特性结构链并对其进行测试。为了做到这一点,您必须知道它们是什么。无法仅从指向任意数据的指针查询此数据包含 X 个 VkBools。

要完成这项工作,您需要能够将sType 值映射到该结构的大小。因此,它不能自动扩展(不,C++ 反射不能解决这个问题;它不知道 void *pNext 指向什么结构);这将需要一定程度的手动维护。

幸运的是,Vulkan XML specification description files 清楚地指定了哪些结构可以存在于特定的pNext 链中。因此,您可以编写一个工具来加载 XML,找到 VkPhysicalDeviceFeatures2,并处理出现在其 pNext 链中的所有结构(这部分说起来容易做起来难,因为 XML 格式只是为由 Khronos 处理而构建的自己的工具)来查找可用的结构,并生成您需要的 C++ 信息。我相信你可以用任何脚本语言相对轻松地编写这样的东西。

但是,既然您已经在(准合理的)XML 中获得了结构定义,并且您已经拥有了无论如何都会生成一些 C++ 代码的工具……您可以只生成 实际的比较逻辑。也就是说,不用手写成员对成员的比较,只需生成成员对成员的比较。您甚至可以获取不匹配的成员名称。

如果您要处理任意pNext-chain 功能,那么您将需要某种生成器工具。如果您仍然需要生成器工具,只需使用它来解决整个问题。

现在,重要的是要认识到假设的hasRequiredFeatures 实现的生成代码将不得不是半复杂的。如果您允许完整的pNext 结构链,那么您需要构建自己的相应等效结构链以用于从 Vulkan 进行查询。这并不是微不足道的。

您需要遍历pNext 链并检​​查每个结构的sType 字段。但是当然,pNextvoid*,所以你将不得不稍微撒谎/欺骗 C++ 的规则来读取sType 字段。基本上,您必须将reinterpret_castvoid* 转换为VkStructureType*,读取值,将其与您正在使用的所有可能性进行比较,然后从那里开始。您应该跳过任何您不知道的sType,这将需要更多的 C++ 技巧。

但是您使用的是低级 API;打破 C++ 的规则只是你必须习惯的事情。

对于每个这样的结构,您需要分配一个匹配的结构,适当地填写其sType,然后将其添加到您正在构建的pNext 链中。

一旦您构建了所有这些,您就可以进行 Vulkan 调用、进行比较、收集数据,最后删除整个结构链。


如果您的目标真的是只使用 VkPhysicalDeviceFeatures 而不是可扩展结构,并且您只想使用 C++ 可移植的方式来比较此类结构,那么只需将 memcpy 放入 VkBool 数组并比较两个数组不匹配。这两种类型都是可以简单复制的,所以从表面上看,这样做并不是非法的。

此代码尚未编译或测试。

bool hasRequiredFeatures(VkPhysicalDevice physical_device,
                              VkPhysicalDeviceFeatures required_features)
{

    constexpr auto feature_count = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32);
    using FeatureArray = std::array<VkBool, feature_count>;

    auto physical_device_features = getSupportedFeatures(physical_device);

    FeatureArray required;
    memcpy(&required, &required_features, sizeof(FeatureArray));

    FeatureArray retrieved;
    memcpy(&retrieved, &physical_device_features, sizeof(FeatureArray));

    bool did_mismatch = false;
    for(auto it_pair = std::mismatch(required.begin(), required.end(), retrieved.begin());
        it_pair.first != required.end();
        it_pair = std::mismatch(it_pair.first, required.end(), it_pair.second))
    {
        did_mismatch = true
        auto mismatch_index = it_pair.first - required.begin();
        //Do something with mismatch_index
    }

    return did_mismatch;
}

【讨论】:

  • 哇 +1,这实际上是我的一个错误,但无论如何我应该考虑 pNext 结构,我不知道 XML 的东西,那太棒了!并且您也涵盖了原始案例。
猜你喜欢
  • 1970-01-01
  • 2019-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-29
相关资源
最近更新 更多