2016-07-14 24 views
-1

Ich habe den folgenden C-Code:Wie eine Struktur mit einer Struktur in jnr ffi verwenden

#include <stdio.h> 

struct Second { 
    int a_number; 
}; 

struct Top { 
    struct Second second; 
}; 

void lets_go(struct Top *top) { 
    printf("The number is %d\n", top->second.a_number); 
} 

Und ich möchte, dies zu tun, außer von Java:

int main(void) { 
    struct Top top = {{8}}; 
    lets_go(&top); 
} 

Ich möchte auch verwenden das super cool jnr-ffi Sachen suchen, so sah ich die Tests an und gebastelt und am Ende mit diesem:

package structs.playing; 

import structs.playing.Program.Test.Top; 
import structs.playing.Program.Test.Second; 
import jnr.ffi.LibraryLoader; 
import jnr.ffi.Runtime; 
import jnr.ffi.Struct; 

public final class Program { 

    public static interface Test { 

     void lets_go(Top top); 

     public static final class Second extends Struct {    
      public final Signed32 a_number = new Signed32();     
      public Second(final Runtime runtime) { 
       super(runtime); 
      } 
     } 

     public static final class Top extends Struct {    
      public Second second;       
      public Top(final Runtime runtime) { 
       super(runtime); 
      } 
     } 
    } 

    public static void main(final String[] args) { 

     Test test = LibraryLoader.create(Test.class).load("test"); 
     Runtime runtime = Runtime.getRuntime(test);   
     Top top = new Top(runtime); 
     Second second = new Second(runtime); 
     top.second = second; 
     second.a_number.set(7);   
     test.lets_go(top); 
    }  
} 

das Problem ist, dass th e-Wert von a_number überhaupt so nicht gesetzt ich einen Junk-Wert in der Ausgabe zu erhalten, zum Beispiel:

The number is 46645760 

Also wie bekomme ich die gleiche wie in meinem C-Code?

+0

Warum verwenden Sie nicht 'int' im Java-Code als auch? – Shark

+0

@Shark, weil ein int würde nicht richtig zum ursprünglichen Code marshalled – kmp

Antwort

1

Ich habe es herausgefunden (übrigens, ich bin mir bewusst, dass die Mitglieder privat sein sollten und in Eigenschaften verpackt, aber ich wollte das Code-Snippet so klein wie möglich machen, das ist keine Produktionsqualitätscode) ...

Wenn Sie eine Pointer Membervariable in die Struktur legen können Sie es verwenden, Speicher ist, wenn Sie die struct untergeordneter konstruieren wie so ...

package structs.playing; 

import structs.playing.Program.Test.Top; 
import jnr.ffi.LibraryLoader; 
import jnr.ffi.Runtime; 
import jnr.ffi.Struct; 

public final class Program { 

    public static interface Test { 

     void lets_go(Top top); 

     public static final class Second extends Struct { 

      public final Signed32 a_number = new Signed32(); 

      public Second(final Runtime runtime) { 
       super(runtime); 
      }   
     } 

     public static final class Top extends Struct { 

      private final Pointer secondPointer = new Pointer();    
      public final Second second; 

      public Top(final Runtime runtime) { 
       super(runtime);        
       second = new Second(runtime); 
       second.useMemory(secondPointer.getMemory()); 
      }   
     } 
    } 

    public static void main(final String[] args) { 

     Test test = LibraryLoader.create(Test.class).load("test"); 
     Runtime runtime = Runtime.getRuntime(test);   
     Top top = new Top(runtime); 
     top.second.a_number.set(8);   
     test.lets_go(top); 
    } 
} 
2

Wenn structs zugeordnet ist, wie in der Linie

top.second = second; 

in Ihrem Java-Code, die Struktur ist von second in top.second kopiert, so dass sie in verschiedenen Bereichen des Speichers getrennte Einheiten werden. Später, wenn Sie 7 auf die a_number Eigenschaft second in der folgenden Zeile zuweisen:

second.a_number.set(7); 

die entsprechende Eigenschaft von top.second unverändert gelassen wird, weil sie nicht das gleiche Objekt sind.

Um mit den gleichen Ergebnissen wie Ihr C-Code, um am Ende, versuchen Sie main Methode diese zu ändern:

public static void main(final String[] args) { 

    Test test = LibraryLoader.create(Test.class).load("test"); 
    Runtime runtime = Runtime.getRuntime(test);   
    Top top = new Top(runtime); 
    top.second.a_number.set(8);   
    test.lets_go(top); 
} 

ein neues Second Objekts Initialisierung ist nicht notwendig, da der Speicher bereits für top.second zugewiesen wurde Teil der Initialisierung des Objekts Top.

+0

Vielen Dank für die Antwort. Leider ist top.second gleich Null, daher erhalte ich eine Laufzeitausnahme mit Ihrem Vorschlag. Muss ich eine Anmerkung darüber schreiben oder etwas, um jnr zu sagen, es für mich zu konstruieren? – kmp

+0

@kmp Versuchen Sie diese Zeile vor der Zeile, die a_number setzt: 'top.second = new Second (Laufzeit);' Ich habe keine Erfahrung mit jnr-ffi, nur mit C und Java allein. – edwardfanboy

+0

Danke edwardfanboy, aber das macht keinen Unterschied zu meinem ursprünglichen Code – kmp

3

EDIT: ich habe ein wenig mehr Zeit damit verbracht suchen am Code und mein Verständnis über das Erstellen von Strukturen hat sich leicht verändert.

Ich glaube, Sie sollten alles über eine Struktur deklarieren und es endgültig machen, denn jedes Mal, wenn Sie ein neues Mitglied deklarieren, registriert es sich selbst bei der Struktur, der es angehört.

Es gibt einige Hilfsfunktionen in struct für jeden Anwendungsfall. Mit der überladenen Methode array() können Sie ein Array von Membern oder Strukturen registrieren. Mit der Methode inner() können Sie eine einzelne Struktur registrieren. Ansonsten definieren Sie einfach neue Member-Objekte und sie werden sich automatisch registrieren.

Zum Beispiel:

struct Second { 
    int a_number; 
}; 

struct Top { 
    struct Second second; 
    struct Second seconds[5]; 
    int another_number; 
    int more_numbers[5]; 
}; 

wird dargestellt als:

public final class Second extends Struct { 
    public final Signed32 a_number = new Signed32(); 
    public Second(final Runtime runtime) { 
     super(runtime); 
    } 
} 

public final class Top extends Struct {    
    public final Second second = inner(new Second(getRuntime())); 
    public final Second[] seconds = array(new Second[5]); 
    public final Signed32 another_number = new Signed32(); 
    public final Signed32[] more_numbers = array(new Signed32[5]); 
    public Top(final Runtime runtime) { 
     super(runtime); 
    } 
} 

ORIGINAL: ich die richtige Art und Weise glaube, dass dies zu tun, das überladene Struct-Konstruktor verwendet, die (Runtime, Struct) akzeptiert. https://github.com/jnr/jnr-ffi/blob/master/src/main/java/jnr/ffi/Struct.java#L129

protected Struct(Runtime runtime, Struct enclosing) { 
    this(runtime); 
    __info.alignment = enclosing.__info.alignment; 
} 

Dieser Konstruktor erzwingt die umschließende Struktur seinen Speicher zu teilen. Also in Ihrem Beispiel finde ich es etwas würde wie folgt aussehen:

package structs.playing; 

import structs.playing.Program.Test.Top; 
import structs.playing.Program.Test.Second; 
import jnr.ffi.LibraryLoader; 
import jnr.ffi.Runtime; 
import jnr.ffi.Struct; 

public final class Program { 

    public static interface Test { 

     void lets_go(Top top); 

     public static final class Second extends Struct {    
      public final Signed32 a_number = new Signed32();     
      public Second(final Runtime runtime, final Struct enclosing) { 
       super(runtime, enclosing); 
      } 
     } 

     public static final class Top extends Struct {    
      public Second second;       
      public Top(final Runtime runtime) { 
       super(runtime); 
      } 
     } 
    } 

    public static void main(final String[] args) { 

     Test test = LibraryLoader.create(Test.class).load("test"); 
     Runtime runtime = Runtime.getRuntime(test);   
     Top top = new Top(runtime); 
     Second second = new Second(runtime, top); 
     top.second = second; 
     second.a_number.set(7);   
     test.lets_go(top); 
    }  
} 

Beachten Sie die Änderungen an der zweiten Konstruktor und bemerke, dass ich das Top-Objekt zum zweiten Objekt übergeben, so dass es bekannt ist, dass oben ist umschließt. Dies wird nicht getestet, ich teile nur das, was ich gefunden habe, während ich versucht habe, den Code zu verstehen.

Ich denke, was in Ihrem Beispiel passiert ist, dass das zweite Objekt seinen eigenen Speicher reserviert, von dem Top nichts weiß.

Wenn dies nicht funktioniert, würde ich empfehlen, einen Blick in so etwas wie dies zu tun:

public static final class Top extends Struct {    
    public Second second = new Second(getRuntime(), this);       
    public Top(final Runtime runtime) { 
     super(runtime); 
    } 
} 
+0

Wenn jemand herausfindet, wie man einen Zeiger auf ein Array deklariert, lass es mich wissen. Ich denke, es wäre eine Kombination der Struct.arrayOf() -Methode und des Address-Members. –