Mam projekt C++ z niektórymi plikami .cpp i .h. Jeden z tych .cpp ma funkcję o tej sygnaturze:

int** verts(int L, int W, int l, int w)

Chciałbym użyć tej funkcji w projekcie Java. Tak, mógłbym przepisać to w Javie, ale jest to bardzo wyartykułowana funkcja. Byłoby wspaniale wywołać tę funkcję (i uzyskać macierz liczb całkowitych) z Javy.

Jakiś pomysł?
Proszę wziąć pod uwagę, że jestem programistą Java, jestem bardzo noobem w C++ :P

1
Oneiros 5 sierpień 2011, 18:06
1
Istnieje wiele przykładów na jni. Oto jeden java.sun.com/developer/onlineTraining/Programming/JDCBook/ …
 – 
Bala R
5 sierpień 2011, 18:09

2 odpowiedzi

Najlepsza odpowiedź

Możesz napisać bibliotekę zawierającą tę funkcję i owinąć ją opakowanie JNI, ale to nie jest trywialne. Najważniejsze pytanie to co naprawdę reprezentują int** i jak są zarządzane: czy i jak je usunąć i jak chcesz je reprezentować w Javie. Zgaduję, podejrzewam, że wskaźnik wskazuje na tablicę int*, z których każdy wskazuje na tablicę int; to jest powszechne reprezentacja dwuwymiarowej tablicy w C (ale nie to, co byśmy normalnie używane w C++). Jeśli chodzi o to, czy i jak je usunąć, to powinny być udokumentowane przez bibliotekę, która definiuje verts. Jeśli się uwzględni, najpierw musisz zadeklarować funkcję jako natywną w klasie Java:

class Verts
{
    static {
        System.loadLibrary( "<i>libraryName</i>" );
    }

    private static Integer[] nativeVerts( int L, int W, int l, int w );
    public Integer[][] verts( int L, int W, int l, int w )
    {
        Integer[] tmp = nativeVerts( L, W, l, w );
        //  reshape tmp...
        return reshapedData;
    }
}

(wolałem raczej zwrócić spłaszczoną, jednowymiarową tablicę niż Integer[][]. To znacznie uprości C++.)

Musisz napisać funkcję w następujący sposób:

#include "jni.h"
extern "C" JNIEXPORT jIntArray JNICALL Java_Verts_nativeVerts(
    JNIEnv* env,
    jclass,
    jint L,
    jint W,
    jint l,
    jint w)
{
    int** results = verts( L, W, l, w );
    std::vector<int> tmp;
    //  Fill tmp with the flattened results.
    //  Then do whatever you have to do to clean up the int**.
    jintArray jResults = env->newIntArray( tmp.size() );
    env->SetIntArrayRegion( jResults, 0, tmp.size(), &tmp[0] );
    return jResults;
}

Tyle że powyższe wymaga również znacznie większej kontroli błędów.

Na koniec skompiluj i połącz powyższe w plik dll o nazwie, którą nadałeś funkcja System.loadLibrary() po stronie Java. I upewnij się, że Twoje środowisko jest skonfigurowane tak, aby Java mogła znaleźć bibliotekę dll (albo poprzez $LD_LIBRARY_PATH (Unix) lub %path% (Windows) lub przekazując ścieżkę przez oznacza -Djava.library.path=... w wierszu poleceń Java).

2
James Kanze 5 sierpień 2011, 19:03
Bardzo dziękuję za odpowiedź. Chciałbym spróbować, ale pojawia się ten błąd podczas ładowania dll... "java.lang.UnsatisfiedLinkError: Nie można załadować IA 32-bitowego .dll na 64-bitowej platformie AMD"
 – 
Oneiros
5 sierpień 2011, 20:38
Korzystam z 64-bitowego systemu Windows 7, ale używam 32-bitowej wersji jdk
 – 
Oneiros
5 sierpień 2011, 20:39
Następnie musisz połączyć bibliotekę DLL jako 32-bitową bibliotekę DLL. (Nie mogę ci w tym pomóc — wszystkie moje komputery z systemem Windows obsługują 32-bitowe wersje systemu Windows).
 – 
James Kanze
5 sierpień 2011, 20:48
Ok, rozwiązałem ten problem ... ale teraz mam inny: java może załadować plik dll, ale kończy działanie z tym błędem: A fatal error has been detected by the Java Runtime Environment: EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x10009a7d, pid=2264, tid=3856. W ten sposób zaimplementowałem natywną metodę: jintArray jResults = env->NewIntArray( tmp.size() ); env->SetIntArrayRegion( jResults, 0, tmp.size(), &tmp[0] ); return (jobjectArray)env->NewObjectArray(n, env->GetObjectClass(jResults), 0);
 – 
Oneiros
8 sierpień 2011, 02:28
Wygląda na to, że masz gdzieś złą wskazówkę, prawdopodobnie w kodzie, który wypełnia tmp ze zwróconego int**. Ale twoja deklaracja zwrotu też nie ma sensu: nie chcesz zwrócić Object[], ale int[]. Twój zwrot tworzy nową tablicę n odniesień do int[], wszystkie zainicjowane do null, i całkowicie ignoruje dane zwrócone przez verts oraz tablicę, którą z niej utworzyłeś. Można zrobić coś takiego, aby skonstruować int[][], aby powrócić do Javy, ale jest to bardziej skomplikowane i nie odpowiada kodowi, który opublikowałem.
 – 
James Kanze
8 sierpień 2011, 11:55

Nie będziesz mógł użyć tej funkcji bezpośrednio (a jeśli to zrobisz, będziesz miał wyciek pamięci, ponieważ Java nie może wiedzieć, jak zwolnić pamięć przydzieloną i zwróconą przez verts). Powinieneś być jednak w stanie napisać prosty wrapper, który będzie przestrzegał zasad JNI.

Dzwoniąc w różnych językach, naprawdę musisz użyć buforów dostarczonych przez rozmówcę. W przypadku tej funkcji Twój wrapper będzie musiał skopiować wyniki do bufora dostarczonego przez wywołującego, a następnie użyć odpowiedniej funkcji C++, aby zwolnić pamięć zwracaną przez verts.

1
Ben Voigt 5 sierpień 2011, 18:15