2012-03-22 14 views
8

[OK]を、私は意図的にタグとタイトルから離してきたキーワードがあります。それは「Android」ですが、それはプロジェクトがAndroidにあるにもかかわらず、私の質問はそれと関係がないと思うし、Androidの経験がない人を恐れたくないからです。SWIG Java C++からバウンスするオブジェクトのクラス情報を保持

だから、swigの通常の問題です。私はクラスにdirector機能を追加することで、Javaでオーバーロード可能にしたC++クラスの仮想メソッドを持っています。問題は、このメソッドがJava側でも拡張された多相引数を受け取り、Javaの仮想メソッド呼び出し中にオブジェクトにすべての多型情報が取り除かれていることです。

正確な状況を提示するには、私はC++でゲームエンジンを作っています。私はそれをJavaで楽しく使いたいと思っています。ゲームエンジンはGameObjectクラスを持ち、CollisionListenerを登録します。衝突エンジンが衝突イベントを検出すると、collidedWith(GameObject & collidee)のすべての登録済みメソッド(collisionListener)が衝突したオブジェクトを渡します。私は、Javaと過負荷collidedWithCollisionListenerから継承したときに

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

は、私は、次のインターフェイスファイルBridge.i

%module(directors="1") Bridge 

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

を使用してJavaにGameObjectクラスと一緒に、このクラスを露出してる、それが呼ばれるのを取得しますJava側ではGameObjectオブジェクトです。たとえば、GameObjectクラスから継承してクラスを定義すると、この呼び出しがリスナーを持つ別のオブジェクトと衝突したときに、collidedWithメソッド呼び出しで、私が受け取るものはすべてGameObjectなので、(object instanceof Bullet)は機能しません。私はSWIGに掘ってきた驚き、何がBridgeJNI.javaを生成しないと、この発見:だから、それはJavaのオーバーロードを呼び出す前にポインタの周りに新しいオブジェクトをラップ

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

を。

したがって、主な問題は、衝突が発生したときにBulletオブジェクトを受け取る方法です。

私は簡単にそれを達成する方法を考え出しましたが、自動生成されたファイルを修正する必要があります。これは悪い考えです。だから私はいくつかのswig masterがswigで生成されたファイルに変更を加えるのを助けることを望んでいます。

私の小さなハックは、すべてのC++側GameObjectオブジェクトにjobject * selfを維持し、実際のJava側GameObject(単にポインタをラップしていない1)の建設中に実際のJavaオブジェクトのアドレスを割り当てることです。このようにして、C++側で多形のgetSelfメソッドを定義し、GameObjectをJavaでうまく使用することができます。 swig生成ファイルに必要なコードを挿入する方法はありますか?

おかげ

注:あなたは、Android上で取締役を試してみましたが、彼らは働いていない場合は、現在の安定版がそれをサポートしていないので、それはです。 swigウェブサイトからBleeding Edgeをダウンロードしてください。しかし、私は22/03/2012にこれを書いています。このメモは間もなく不要になります。デストラクタが仮想ではない理由は、Bleeding Edgeのバージョンがデストラクタでプログラムをクラッシュさせ、非バーチャルにすると、今のところそれを制御しているように見えるからです。

+0

Javaの 'GameObject'を派生させ、その派生型が' collidedWith'のJava実装に渡されたときに、Javaでキャストできるようにしたいと思っていますか?そうであれば、あなたの小さなハックをタイプマップにラップすることができます。 – Flexo

+0

正確に!私はswigがコードを挿入する方法を持っていると思っていましたが、私は泳ぐのがとても新しいです。私はタイプマップをチェックします。 – enobayram

+0

明日はうまくいけば答えを書きます。 – Flexo

答えて

8

私はこの問題を解決しました。あなたの疑問にお答えしたのはそれほどの解決策ではありませんが、Java側ではより多くのコードがあり、JNI/C++側では余分なコードはありません。 (可能性のあるすべてのケースで正しいことをあなたが非常にトリッキーに示唆したようにしていることがわかりました)。

私は1つのヘッダーファイルに至るまで、あなたのクラスを簡素化:

class GameObject { 
}; 

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

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

も、実際に問題を明らかにするためにmakeCallを追加しました。

私が使用したトリックは、GameObjectのすべてのJava派生インスタンスを作成時に自動的にHashMapに登録することです。ディレクターコールをディスパッチするときは、HashMapでそれを探すだけです。

そして、モジュールファイル:すべてのJavaインスタンスがHashMapに格納されているので、私たちは自分たちの生活を延長してから、ガベージコレクションを防止していないことを確認するためにWeakReferenceを使用する必要があることを

%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" 

注意ハプニング。スレッドを気にしている場合は、必要に応じて同期を追加してください。

私はこれをテストした:

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

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

JGameObject次のとおりです。

public class JGameObject extends GameObject { 
} 

(参考のためにあなたが他のアプローチにあなたをやってみたかった場合directorinタイプマップを書くことを検討しています)。

+0

うわー!とても努力と素晴らしい返事。これは私の問題を解決すると同時に私にswigについてたくさん教えてくれるようです。私たちが時々必要とするものを得るためにswigの半分を無効にする必要があるように見えます。どうもありがとう。 – enobayram

+0

SWIGの素晴らしい点は、あなたが望むときにこのようなことをすることができますが、「奇妙な」ビットで作業するだけで済むようにすべてをカプセル化しています。 – Flexo

+0

ソフトウェアエンジニアの決して終わらない意思決定:「どこに私の時間を投資すべきですか?」 SWIGは言語間の問題へのアプローチが一般的であるように見えるので、良い取り引きであるようです。つまり、外部コードジェネレータとして、純粋にライブラリベースのソリューション(boost pythonのような)が苦しむというほとんどの制限がありません。 – enobayram

関連する問題