JNA将Java布尔值映射到-1整数?

问题描述 投票:9回答:2

当我在JNA结构中传递boolean值时,我正在使用的本机库得到一个惊人的警告:

value of pCreateInfo->clipped (-1) is neither VK_TRUE nor VK_FALSE

在这个库中,VK_TRUEVK_FALSE的#defined分别为1和0。

结构本身并不是特别复杂,其他一切似乎都在工作(本机库似乎将'undefined'布尔值视为false),但无论如何它在这里:

public class VkSwapchainCreateInfoKHR extends Structure {
    public int sType;
    public Pointer pNext;
    public int flags;
    public Pointer surface;
    public int minImageCount;
    public int imageFormat;
    public int imageColorSpace;
    public VkExtent2D imageExtent;
    public int imageArrayLayers;
    public int imageUsage;
    public int imageSharingMode;
    public int queueFamilyIndexCount;
    public Pointer pQueueFamilyIndices;
    public int preTransform;
    public int compositeAlpha;
    public int presentMode;
    public boolean clipped;       // <--------- this is the field in question
    public Pointer oldSwapchain;
}

如果clipped字段为false则没有警告,如果它是真的那么我得到警告 - 看来JNA将true映射到整数-1?

这个库使用的本机布尔值不多,但只要一个设置为true,我就会得到相同的行为(并且其他一切工作正常)。

特别是,如果我将clipped更改为int并将值明确设置为1或0,一切正常!

-1是JNA boolean true的默认值吗?

如果是这样,我将如何过度使用类型映射?

或者我应该'手动'使用int

java jna
2个回答
5
投票

JNA通过libffi映射到本机库。在bool中没有libffi类型,因此必须使用其他映射 - JNA的default type mapping选择将boolean映射到ffi_type_uint32。这在结构中起作用,因为它恰好匹配32位映射大小,但不匹配定义:在C中,0为false,任何非零都为真。仅当本机类型也是boolean时,此0 /非零解释才重新获得含义为false / true。

使用FFIJNIboolean关键字的网络搜索可以发现多个示例,例如this onethis one,当通过FFI或JNI访问库时不会出现不可预测的结果,并且不符合布尔值的0/1要求。后一个例子看起来非常类似于这种情况,其中真正的Java boolean被解释为具有1以外的值的C int

在FFI和你的库之间的某个地方,可能在编译的字节代码和/或平台/编译器相关的类型转换中,可能是一个按位“not”被应用于0x00000000,把它变成0xffffffff仍然是'真的'在C.

最重要的是,JNA默认将Java boolean false映射到32位本机值0,将Java boolean true映射到非0的32位本机值,这就是所有可以假设的。如果您的库要求true的整数值为1,请使用您可以专门设置的整数类型,或者使用boolean的自定义类型映射,为您设置int为0或1。 JNA的W32APITypeMapper为Windows BOOL类型转换为1或0的示例。

在您的情况下,假设您正在映射VkSwapchainCreateInfoKHR结构defined hereclipped的类型是VkBool32:

typedef struct VkSwapchainCreateInfoKHR {
    VkStructureType                  sType;
    const void*                      pNext;
    VkSwapchainCreateFlagsKHR        flags;
    VkSurfaceKHR                     surface;
    uint32_t                         minImageCount;
    VkFormat                         imageFormat;
    VkColorSpaceKHR                  imageColorSpace;
    VkExtent2D                       imageExtent;
    uint32_t                         imageArrayLayers;
    VkImageUsageFlags                imageUsage;
    VkSharingMode                    imageSharingMode;
    uint32_t                         queueFamilyIndexCount;
    const uint32_t*                  pQueueFamilyIndices;
    VkSurfaceTransformFlagBitsKHR    preTransform;
    VkCompositeAlphaFlagBitsKHR      compositeAlpha;
    VkPresentModeKHR                 presentMode;
    VkBool32                         clipped;
    VkSwapchainKHR                   oldSwapchain;
} VkSwapchainCreateInfoKHR;

哪里...

typedef uint32_t VkBool32;

所以int在这里是正确的映射 - 你需要将clipped映射到32位整数编辑:正如你在答案中指出的那样,添加你自己的类型映射器以更好地处理这些int值很简单!

(虽然我正在审查类型映射,但您可能会发现IntByReferencePointer更适合pQueueFamilyIndices字段。) (您的映射对于可变长度的int数组是正确的。)


2
投票

事实上,事实证明,在各种原生图书馆结构中有很多布尔,其实几百个!保留布尔字段的意图是很好的,而不是仅仅因为实现强制执行该限制而将它们全部替换为int。所以我花了一些时间研究JNA类型转换......

JNA支持在创建本机库时使用作为附加参数传递给TypeMapperNative::load来映射自定义类型。使用Java-to / from-native转换器接口TypeConverter定义自定义类型映射。

定义一个自定义布尔包装器,它将Java boolean映射到C int,从1 = true和0 = false,这是非常简单的:

public final class VulkanBoolean {
    static final TypeConverter MAPPER = new TypeConverter() {
        @Override
        public Class<?> nativeType() {
            return Integer.class;
        }

        @Override
        public Object toNative(Object value, ToNativeContext context) {
            if(value == null) {
                return VulkanBoolean.FALSE.toInteger();
            }
            else {
                final VulkanBoolean bool = (VulkanBoolean) value;
                return bool.toInteger();
            }
        }

        @Override
        public Object fromNative(Object nativeValue, FromNativeContext context) {
            if(nativeValue == null) {
                return VulkanBoolean.FALSE;
            }
            else {
                final int value = (int) nativeValue;
                return value == 1 ? VulkanBoolean.TRUE : VulkanBoolean.FALSE;
            }
        }
    };

    public static final VulkanBoolean TRUE = VulkanBoolean(true);
    public static final VulkanBoolean FALSE = VulkanBoolean(false);

    private final boolean value;

    private VulkanBoolean(boolean value) {
        this.value = value;
    }

    public boolean value() {
        return value;
    }

    public int toInteger() {
        return value ? 1 : 0;
    }
}

因此注册了类型映射器:

final DefaultTypeMapper mapper = new DefaultTypeMapper();
mapper.addTypeConverter(VulkanBoolean.class, VulkanBoolean.MAPPER);
...

final Map<String, Object> options = new HashMap<>();
options.put(Library.OPTION_TYPE_MAPPER, mapper);
Native.load("vulkan-1", VulkanLibrary.class, options);

但是这只有在有问题的结构在JNA库接口中定义的情况下才有效 - 如果一个人正在编写一个带有少量结构的小型库(通常就是这种情况),那么这很简单,但是当你有几个结构时有点头疼一百种方法和~500种结构(代码生成)。

或者,可以在结构构造函数中指定类型映射器,但这需要:

  1. 检测每个需要自定义映射的结构。
  2. 每个自定义类型都必须另外实现NativeMapped,以便JNA可以确定自定义类型的本机大小(不知道为什么基本上必须指定两次相同的信息)。
  3. 每个自定义类型都必须支持默认构造函数。

这些都不是特别令人愉快的选择,如果JNA支持覆盖这两种情况的全局类型映射,那就太好了。我猜我需要使用type-mapper重新生成所有结构的代码。叹。

但是,只有在JNA库接口内定义了相关结构时,这才有效。一个简单的解决方法是在库中定义基类结构并从中扩展所有其他结构:

public interface Library {
    abstract class VulkanStructure extends Structure {
        protected VulkanStructure() {
            super(VulkanLibrary.TYPE_MAPPER);
        }
    }
...
}

public class VkSwapchainCreateInfoKHR extends VulkanStructure { ... }

我使用相同的机制自动神奇地将~300代码生成的枚举映射到当前如下所示的本机int

public enum VkSubgroupFeatureFlag implements IntegerEnumeration {
    VK_SUBGROUP_FEATURE_BASIC_BIT(1),   
    VK_SUBGROUP_FEATURE_VOTE_BIT(2),    
    ...

    private final int value;

    private VkSubgroupFeatureFlag(int value) {
        this.value = value;
    }

    @Override
    public int value() {
        return value;
    }
}

目前,所有引用“枚举”的结构实际上都是作为int实现的。使用IntegerEnumeration的自定义类型转换器,字段类型可以是实际的Java枚举,JNA将处理与整数值的转换(我现在必须手动)。这显然使结构稍微更安全,更明确,并且明确指的是实际的枚举而不是int - 很好。

public class VkSwapchainCreateInfoKHR extends VulkanStructure {
    ...
    public int flags;
    public Pointer surface;
    public int minImageCount;
    // The following fields were int but are now the Java enumerations
    public VkFormat imageFormat = VkFormat.VK_FORMAT_UNDEFINED;
    public VkColorSpaceKHR imageColorSpace;
    ...
}

(最近发现一个例子正是在做here)。

希望所有这些莫名其妙的帮助有人试图了解JNA的变幻莫测。

© www.soinside.com 2019 - 2024. All rights reserved.