[英]JNA maps Java boolean to -1 integer?
當我在JNA結構中傳遞一個boolean
值時,我正在使用的本機庫中收到一個令人驚訝的警告:
value of pCreateInfo->clipped (-1) is neither VK_TRUE nor VK_FALSE
在此庫中, VK_TRUE
和VK_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布爾默認值true
?
如果是這樣,我將如何過度使用類型映射?
或者我應該只使用int
'手動'?
JNA通過libffi
映射到本機庫。 libffi
沒有bool
類型,因此必須使用其他映射 - JNA的默認類型映射選擇將boolean
映射到ffi_type_uint32
。 這在結構中起作用,因為它恰好匹配32位映射大小,但不匹配定義:在C中,0為false,任何非零都為真。 僅當本機類型也是boolean
,此0 /非零解釋才重新獲得false / true。
使用Web搜索FFI
或JNI
和boolean
關鍵字可以發現多個例子如這一個和這一個 ,其中當庫經由FFI或JNI訪問和不符合為布爾值0/1的要求無法預料的結果發生。 后一個示例與此情況非常相似,其中真正的Java boolean
被解釋為具有非1值的C int
。
在FFI和您的庫之間的某個地方,可能在編譯的字節代碼和/或平台/編譯器相關的類型轉換中,可能是按位“不”應用於0x00000000
,將其轉換為0xffffffff
仍然是'true '在C.
最重要的是,JNA默認將Java布爾值false
映射到32位本機值0,並將Java布爾true
設置為32位本機值(非0),這就是可以假設的全部值。 如果您的庫要求為true
,則整數值為1,或者使用您可以專門設置的整數類型,或者使用自定義類型映射為boolean
,將int
設置為0或1。 JNA的W32APITypeMapper有一個示例,即Windows BOOL
類型轉換為1或0。
在您的情況下,假設您正在映射此處定義的VkSwapchainCreateInfoKHR結構,則clipped
的類型為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
值是很簡單的!
(當我查看類型映射時,您可能會發現 (您的映射對於可變長度的IntByReference
比pQueueFamilyIndices
字段的Pointer
更好的映射。)int
數組是正確的。)
事實上,事實證明,在各種原生圖書館結構中有很多布爾,其實幾百個! 保留布爾字段的意圖是很好的,而不是僅僅因為實現強制執行該限制而將它們全部替換為int
。 所以我花了一些時間研究JNA類型轉換......
JNA支持在創建本機庫時使用作為附加參數傳遞給Native::load
的TypeMapper
映射自定義類型。 使用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);
如果一個人寫一個小庫結構的極少數(通常情況下),但有點頭疼的,當你有幾個微不足道的-如果有問題的結構(s)是JNA庫接口
中定義然而這僅適用一百種方法和~500種結構(代碼生成)。
或者,可以在結構構造函數中指定類型映射器,但這需要:
檢測
每個需要自定義映射的結構。
每個自定義類型都必須另外實現
NativeMapped
以便JNA可以確定自定義類型的本機大小(不知道為什么基本上必須指定兩次相同的信息)。
每個自定義類型都必須支持默認構造函數。
這些都不是特別令人愉快的選擇,如果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
- nice。
即
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; ... }
(最近在這里找到了一個完整的例子)。
希望所有這些莫名其妙的幫助有人試圖了解JNA的變幻莫測。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.