Java >> Java tutorial >  >> Tag >> boolean

JNA kortlægger Java boolean til -1 heltal?

JNA maps til oprindelige biblioteker via libffi . Der er ingen bool indtast libffi så andre kortlægninger skal bruges -- JNA's standardtypekortlægning vælger at kortlægge boolean til ffi_type_uint32 . Dette virker i strukturen/strukturerne, fordi det tilfældigvis matcher 32-bit kortlægningsstørrelsen, men ikke definitionen:i C er 0 falsk, og alt andet end nul er sandt. Kun hvis den oprindelige type også er boolean genvinder denne 0/ikke-nul fortolkning betydning som falsk/sand.

En websøgning ved hjælp af FFI eller JNI og boolean nøgleord kan afsløre flere eksempler, såsom dette og dette, hvor uforudsigelige resultater opstår, når biblioteker tilgås via FFI eller JNI og ikke overholder 0/1-kravet for booleske værdier. Sidstnævnte eksempel ligner meget dette tilfælde, hvor en ægte Java boolean tolkes som en C int med en anden værdi end 1.

Et sted under motorhjelmen mellem FFI og dit bibliotek, og muligvis i kompileret bytekode og/eller platform/compiler-afhængig type konverteringer, er det sandsynligt, at et bitvist "ikke" bliver anvendt på 0x00000000 , forvandler den til 0xffffffff hvilket stadig er 'sandt' i C.

Den nederste linje er, at JNA som standard vil kortlægge Java boolesk false til en 32-bit native værdi på 0 og en Java boolesk true til en 32-bit native værdi, der ikke er 0, og det er alt, der kan antages. Hvis dit bibliotek kræver true for at have en heltalsværdi på 1, skal du enten bruge en heltalstype, som du specifikt kan indstille, eller bruge en tilpasset typetilknytning til boolean der sætter en int til 0 eller 1 for dig. JNA's W32APITypeMapper har et eksempel på denne konvertering til 1 eller 0 for Windows BOOL type.

I dit tilfælde, forudsat at du kortlægger VkSwapchainCreateInfoKHR-strukturen defineret her, typen clipped er 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;

Hvor...

typedef uint32_t VkBool32;

int er den korrekte kortlægning her -- du skal kortlægge clipped til et 32-bit heltal Rediger: Som du har påpeget i dit svar, er det nemt at tilføje din egen typemapper for bedre at håndtere disse int værdier!

(Mens jeg gennemgår typetilknytningerne, finder du muligvis IntByReference en bedre kortlægning end Pointer for pQueueFamilyIndices felt.) (Din tilknytning er korrekt for en variabel længde int array.)


Faktisk som det viser sig, at der er en masse booleaner i de forskellige oprindelige biblioteksstrukturer, flere hundrede af dem faktisk! Det ville være rart at bevare intentionen med de boolske felter i stedet for at erstatte dem alle med int bare fordi implementeringen håndhæver den begrænsning. Så jeg brugte noget tid på at undersøge JNA-type konvertering...

JNA understøtter kortlægning af brugerdefinerede typer ved hjælp af en TypeMapper sendt som et ekstra argument til Native::load når det oprindelige bibliotek er oprettet. Brugerdefinerede typetilknytninger er defineret ved hjælp af Java-til/fra-native-konverteringsgrænsefladen TypeConverter .

Definere en brugerdefineret boolesk indpakning, der kortlægger Java boolean til/fra en C int med 1=sand og 0=falsk er ret ligetil:

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;
    }
}

Typekortlæggerne er registreret således:

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);

Dette virker dog kun, hvis den eller de pågældende strukturer er defineret indvendigt JNA-bibliotekets grænseflade - trivielt, hvis man skriver et lille bibliotek med en håndfuld strukturer (hvilket normalt er tilfældet), men lidt af en hovedpine, når man har flere hundrede metoder og ~500 strukturer (der er kodegenereret).

Alternativt kan typekortlæggeren angives i strukturkonstruktøren, men dette kræver:

  1. instrumentering hver struktur, der har brug for den eller de tilpassede kortlægninger.

  2. hver brugerdefineret type skal desuden implementere NativeMapped så JNA kan bestemme den indbyggede størrelse af den tilpassede type (ingen idé om, hvorfor i det væsentlige den samme information skal angives to gange).

  3. hver brugerdefineret type skal understøtte en standardkonstruktør.

Ingen af ​​disse er særlig behagelige muligheder, det ville rart, hvis JNA understøttede globale typekortlægninger, der dækkede begge tilfælde. Tror jeg er nødt til at genkode-generere alle strukturer med type-mapper. Suk.

Dette virker dog kun, hvis de(n) pågældende struktur(er) er defineret indvendigt JNA-bibliotekets grænseflade. En simpel løsning er at definere en basisklassestruktur i biblioteket og udvide alle de andre fra det:

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

public class VkSwapchainCreateInfoKHR extends VulkanStructure { ... }

Jeg har brugt den samme mekanisme til automagisk at kortlægge de ~300 kodegenererede opregninger til native int der i øjeblikket ser sådan ud:

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;
    }
}

I øjeblikket er alle strukturer, der refererer til en 'opregning', faktisk implementeret som en int . Med en brugerdefineret typekonverter til IntegerEnumeration på plads kan felttypen være den faktiske Java-opregning, og JNA vil håndtere konverteringen til/fra heltalsværdien (som jeg i øjeblikket skal bruge manuelt). Dette gør naturligvis strukturerne lidt mere typesikre, absolut klarere og refererer eksplicit til den faktiske opregning snarere end en int - dejligt.

dvs.

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;
    ...
}

(fandt for nylig et eksempel, der gør præcis det her).

Forhåbentlig hjælper al denne ævle nogen, der prøver at få hovedet uden om JNA's luner.


Java tag