2012-03-22 7 views
8

Ok, jest słowo kluczowe, które celowo trzymałem z dala od znaczników i tytułu. To "Android", ale to dlatego, że mimo że projekt jest w systemie Android, nie sądzę, że moje pytanie ma z tym coś wspólnego, i nie chcę przestraszyć ludzi bez doświadczenia w systemie Android.SWIG Informacje o klasie zachowania klasy Java obiektów skaczących z C++

Zwykły problem z łykiem. Mam wirtualną metodę w klasie C++, której nadałem w Javie, dodając do klasy funkcję director i to działa. Problem polega na tym, że metoda otrzymuje polimorficzny argument, który jest również rozszerzony po stronie Java, a podczas wywołania metody wirtualnej w Javie, obiekt jest usuwany z całej informacji polimorficznej.

Aby przedstawić dokładną sytuację; Piszę silnik gry w C++ i chcę go używać na szczęście w Javie. Silnik gry ma klasę GameObject, która rejestruje CollisionListener s, a gdy silnik kolizji wykryje zdarzenie kolizyjne, wywołuje metodę collidedWith(GameObject & collidee) dla wszystkich zarejestrowanych , przekazując im obiekt, który spowodował kolizję.

class CollisionListener { 
public: 
    virtual bool collidedWith(GameObject &){}; 
    ~CollisionListener(){} // I know this needs to be virtual but let's forget about that now 
}; 

ja wystawiając tej klasy, wraz z klasą GameObject Java stosując następujący plik interfejsu Bridge.i

%module(directors="1") Bridge 

%feature("director") CollisionListener; 
%include "CollisionListener"; 
%feature("director") GameObject; 
%include "GameObject.h" 

Teraz, kiedy dziedziczą CollisionListener w Java i przeciążeniu collidedWith, to się nazywa z obiektem po stronie Java GameObject. Na przykład, jeśli dziedziczę po klasie java GameObject i definiuję klasę Bullet, gdy ten punkt zderzy się z innym obiektem z detektorem, w wywołaniu metody collidedWith, wszystko, co otrzymam, to pusty GameObject, więc (object instanceof Bullet) nie działa. Nic dziwnego, ja wykutych w SWIG generowane BridgeJNI.java i stwierdziliśmy:

public static boolean SwigDirector_CollisionListener_collidedWith(CollisionListener self, long arg0) { 
    return self.collidedWith(new GameObject(arg0, false)); 
    } 

Więc to nowy obiekt owija wokół wskaźnika przed wywołaniem przeciążeń Java.

Głównym pytaniem jest zatem, jak otrzymać obiekt Bullet w przypadku kolizji?

Wymyśliłem sposób, aby łatwo to osiągnąć, ale muszę zmodyfikować automatycznie generowane pliki, co jest złym pomysłem. Mam więc nadzieję, że jakiś swig master pomoże mi wprowadzić modyfikacje plików generowanych przez swig.

Mój mały kilof jest utrzymanie jobject * self w każdym C++ strona GameObject obiektu i przypisanie adresu rzeczywistego obiektu java podczas budowy strony GameObject (a nie jednego, że tylko owija wskaźnik) real java. W ten sposób mógłbym zdefiniować metodę polimorficzną getSelf w języku C++ po stronie GameObject i używać wyniku szczęśliwie w java. Czy istnieje sposób na wstrzyknięcie niezbędnego kodu do wygenerowanych plików swig?

Dzięki

Uwaga: Jeśli próbowałeś dyrektorów na Androida i oni nie pracowali, to dlatego, że obecna stabilna wersja nie obsługuje. Pobierz Bleeding Edge ze strony internetowej. Ale piszę to w dniu 22.03.2012 r., A ta notatka wkrótce będzie niepotrzebna. Powodem, dla którego destruktor nie jest wirtualny, jest to, że wersja Bleeding Edge powoduje awarię programu w destruktorze, a sprawienie, że jest ona nie-wirtualna, wydaje się na razie kontrolować.

+0

Skrócona wersja twojego pytania jest taka, że ​​chcesz (na przykład) wyprowadzić 'GameObject' w Javie i nadal móc rzutować w Javie, gdy ten typ pochodny zostanie przekazany do implementacji Java' collidedWith'? Jestem pewien, że twój mały hack może być zawinięty w typografię, jeśli tak jest. – Flexo

+0

Precyzyjnie! Pomyślałem, że łyk będzie miał sposób na wstrzyknięcie kodu, ale jestem całkiem nowy, by łykać. Sprawdzę mapy typów. – enobayram

+0

Zapiszę jutro mam nadzieję. – Flexo

Odpowiedz

8

Połączyłem rozwiązanie tego problemu. Nie jest to jednak rozwiązanie, które zasugerowałeś w swoim pytaniu, ale jest to więcej kodu po stronie Java i nie ma żadnych dodatków po stronie JNI/C++. (Zauważyłem, że robisz to w sposób, który sugerowałeś, że jest on dość skomplikowany, aby uzyskać poprawność we wszystkich możliwych przypadkach).

I uprościć swoje klasy do pojedynczego pliku nagłówka:

class GameObject { 
}; 

class CollisionListener { 
public: 
    virtual bool collidedWith(GameObject &) { return false; } 
    virtual ~CollisionListener() {} 
}; 

inline void makeCall(GameObject& o, CollisionListener& c) { 
    c.collidedWith(o); 
} 

który również dodaje makeCall faktycznie zrobić problem oczywiste.

Sztuczka, której użyłem, polega na automatycznym zarejestrowaniu wszystkich instancji pochodzących z Javy GameObject w HashMap w czasie tworzenia. Następnie, wysyłając wezwanie reżysera, jest to tylko kwestia znalezienia go w HashMap.

Następnie plik modułu:

%module(directors="1") Test 

%{ 
#include "test.hh" 
%} 

%pragma(java) jniclasscode=%{ 
    static { 
    try { 
     System.loadLibrary("test"); 
    } catch (UnsatisfiedLinkError e) { 
     System.err.println("Native code library failed to load. \n" + e); 
     System.exit(1); 
    } 
    } 
%} 

/* Pretty standard so far, loading the shared object 
    automatically, enabling directors and giving the module a name. */  

// An import for the hashmap type 
%typemap(javaimports) GameObject %{ 
import java.util.HashMap; 
import java.lang.ref.WeakReference; 
%} 

// Provide a static hashmap, 
// replace the constructor to add to it for derived Java types 
%typemap(javabody) GameObject %{ 
    private static HashMap<Long, WeakReference<$javaclassname>> instances 
         = new HashMap<Long, WeakReference<$javaclassname>>(); 

    private long swigCPtr; 
    protected boolean swigCMemOwn; 

    public $javaclassname(long cPtr, boolean cMemoryOwn) { 
    swigCMemOwn = cMemoryOwn; 
    swigCPtr = cPtr; 
    // If derived add it. 
    if (getClass() != $javaclassname.class) { 
     instances.put(swigCPtr, new WeakReference<$javaclassname>(this)); 
    } 
    } 

    // Just the default one 
    public static long getCPtr($javaclassname obj) { 
    return (obj == null) ? 0 : obj.swigCPtr; 
    } 

    // Helper function that looks up given a pointer and 
    // either creates or returns it 
    static $javaclassname createOrLookup(long arg) { 
    if (instances.containsKey(arg)) { 
     return instances.get(arg).get(); 
    } 
    return new $javaclassname(arg,false); 
    } 
%} 

// Remove from the map when we release the C++ memory 
%typemap(javadestruct, methodname="delete", 
     methodmodifiers="public synchronized") GameObject { 
    if (swigCPtr != 0) { 
    // Unregister instance 
    instances.remove(swigCPtr); 
    if (swigCMemOwn) { 
     swigCMemOwn = false; 
     $imclassname.delete_GameObject(swigCPtr); 
    } 
    swigCPtr = 0; 
    } 
} 

// Tell SWIG to use the createOrLookup function in director calls. 
%typemap(javadirectorin) GameObject& %{ 
    $javaclassname.createOrLookup($jniinput) 
%} 
%feature("director") GameObject; 

// Finally enable director for CollisionListener and include the header 
%feature("director") CollisionListener;  
%include "test.hh" 

Zauważ, że ponieważ wszystkie instancje Java są przechowywane w HashMap musimy użyć WeakReference, aby mieć pewność, że nie przedłuża ich życie i zapobieganie zbieranie śmieci z wydarzenie. Jeśli zależy Ci na wątkach, dodaj odpowiednio synchronizację.

testowałem to z:

public class main { 
    public static void main(String[] argv) { 
    JCollisionListener c = new JCollisionListener(); 
    JGameObject o = new JGameObject(); 
    c.collidedWith(o); 
    Test.makeCall(o,c); 
    } 
} 

Gdzie JCollisionListener jest:

public class JCollisionListener extends CollisionListener { 
    public boolean collidedWith(GameObject i) { 
    System.out.println("In collide"); 
    if (i instanceof JGameObject) { 
     System.out.println("Is J"); 
    } 
    else { 
     System.out.println("Not j"); 
    } 
    JGameObject o = (JGameObject)i; 
    return false; 
    } 
} 

i JGameObject jest:

public class JGameObject extends GameObject { 
} 

(Dla porównania, jeśli chciał zrobić drugi podejście ty chciałby napisać maszynę do pisania na maszynie directorin).

+0

Wow! Tyle wysiłku i wspaniała odpowiedź. To wydaje się rozwiązać mój problem, a także nauczyć mnie dużo o swig. Wygląda na to, że czasami potrzebujemy zastąpić połowę łyka, aby uzyskać to, czego chcemy. Wielkie dzięki. – enobayram

+0

Przyjemne w SWIG jest to, że pozwala ci robić takie rzeczy, kiedy chcesz, ale wszystko to zamyka, więc musisz tylko pracować nad "dziwnymi" bitami i cała praca jest wykonywana dla ciebie. – Flexo

+0

Nigdy nie kończący się proces podejmowania decyzji przez inżynierów oprogramowania: "Gdzie powinienem zainwestować swój czas?". SWIG wydaje się być dobrym rozwiązaniem, ponieważ jego podejście do problemu międzyjęzykowego wydaje się być tak ogólne, jak to tylko możliwe. Chodzi mi o to, że jako zewnętrzny generator kodu, jest on wolny od większości ograniczeń, że cierpią na nim wyłącznie rozwiązania oparte na bibliotekach (takie jak pyton doładowania). – enobayram

Powiązane problemy