2012-03-22 25 views
8

Ok, có một từ khóa mà tôi đã cố tình tránh xa các thẻ và tiêu đề. Đó là "Android", nhưng đó là bởi vì mặc dù dự án là trong Android, tôi không nghĩ rằng câu hỏi của tôi có liên quan gì đến nó, và tôi không muốn dọa mọi người mà không có kinh nghiệm trong Android.SWIG Java giữ lại thông tin lớp của các đối tượng nảy từ C + +

Vì vậy, vấn đề thường gặp với swig. Tôi đã có một phương pháp ảo trong một lớp C++, mà tôi đã làm cho nó quá tải trong Java bằng cách thêm các tính năng director cho lớp và nó hoạt động. Vấn đề là phương thức nhận được một đối số đa hình cũng được mở rộng ở phía java, và trong khi gọi phương thức ảo trong Java, đối tượng đi kèm với tất cả các thông tin đa hình bị tước bỏ.

Để trình bày tình hình chính xác; Tôi đang viết một công cụ trò chơi trong C++ và tôi muốn sử dụng nó một cách vui vẻ trong Java. Công cụ trò chơi có lớp GameObject, đăng ký CollisionListener s và khi động cơ va chạm phát hiện sự kiện va chạm, nó gọi phương thức collidedWith(GameObject & collidee) của tất cả các đăng ký collisionListener s truyền chúng với đối tượng mà chúng đã va chạm.

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

Tôi đang phơi bày lớp này, cùng với các lớp GameObject để java bằng cách sử dụng tập tin giao diện sau Bridge.i

%module(directors="1") Bridge 

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

Bây giờ, khi tôi kế thừa từ CollisionListener trong java và quá tải collidedWith, nó lấy được gọi là với một đối tượng java GameObject. Ví dụ, nếu tôi kế thừa từ lớp java GameObject và xác định một lớp Bullet, khi viên đạn này va chạm với một đối tượng khác với một người nghe, trong cuộc gọi phương thức collidedWith, tất cả tôi nhận được là GameObject, để (object instanceof Bullet) không hoạt động. Không ngạc nhiên, tôi đã đào vào uống một lân tạo BridgeJNI.java và thấy điều này:

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

Vì vậy, nó kết thúc tốt đẹp một đối tượng mới xung quanh con trỏ trước khi gọi quá tải java.

Vì vậy, câu hỏi chính là cách nhận đối tượng Bullet khi có xung đột?

Tôi đã tìm ra cách dễ dàng đạt được điều đó nhưng tôi cần sửa đổi các tệp được tạo tự động, đó là một ý tưởng tồi. Vì vậy, tôi hy vọng một số chủ swig có thể giúp tôi tiêm các sửa đổi để các tập tin tạo ra swig.

Hack nhỏ của tôi là giữ jobject * self trong mọi đối tượng bên cạnh C++ GameObject và gán địa chỉ của đối tượng java thực trong khi xây dựng mặt java thực GameObject (và không phải chỉ đơn thuần là kết thúc con trỏ). Bằng cách này, tôi có thể định nghĩa một phương pháp đa hình getSelf ở bên C++ GameObject và sử dụng kết quả một cách vui vẻ trong java. Có cách nào để tiêm mã cần thiết để các tập tin tạo ra swig?

Cảm ơn

Lưu ý: Nếu bạn đã thử đạo diễn trên Android và họ chưa làm việc, đó là vì phiên bản ổn định hiện tại không hỗ trợ. Tải xuống Bleeding Edge từ trang web swig. Nhưng tôi đang viết điều này vào ngày 22/03/2012 và ghi chú này sẽ sớm không cần thiết. Lý do tại sao các destructor không phải là ảo là phiên bản Bleeding Edge làm cho chương trình sụp đổ trong destructor, và làm cho nó không ảo dường như giữ cho nó dưới sự kiểm soát cho bây giờ.

+0

Vì vậy, phiên bản ngắn của câu hỏi là bạn muốn có thể (ví dụ) lấy được 'GameObject' trong Java và vẫn có thể truyền trong Java khi kiểu dẫn xuất đó được chuyển đến thực thi Java 'collidedWith'? Khá chắc chắn hack nhỏ của bạn có thể được bọc trong một typemap nếu đó là trường hợp. – Flexo

+0

Chính xác! Tôi nghĩ rằng swig sẽ có một cách để tiêm mã, nhưng tôi khá mới để swig. Tôi sẽ kiểm tra các bản đồ. – enobayram

+0

Tôi sẽ viết câu trả lời vào ngày mai. – Flexo

Trả lời

8

Tôi đã tập hợp một giải pháp cho vấn đề này. Nó không hoàn toàn là giải pháp mà bạn đã đề xuất trong câu hỏi của bạn mặc dù, nó là mã nhiều hơn ở phía Java và không có thêm trên bên JNI/C + +. (Tôi thấy làm theo cách bạn đề xuất khá phức tạp để có được chính xác trong tất cả các trường hợp có thể).

tôi đơn giản hóa các lớp học của bạn xuống đến một tập tin tiêu đề duy nhất:

class GameObject { 
}; 

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

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

mà cũng nói thêm makeCall để thực sự làm cho vấn đề rõ ràng.

Bí quyết tôi đã sử dụng là đăng ký tất cả các cá thể Java bắt nguồn từ GameObject trong một số HashMap tự động tại thời điểm tạo. Sau đó, khi phái giám đốc gọi nó chỉ là một câu hỏi tìm kiếm nó trong HashMap.

Sau đó, các tập tin mô-đun:

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

Lưu ý rằng kể từ khi tất cả các trường Java đang được lưu trữ trong một HashMap chúng ta cần phải sử dụng một WeakReference để chắc chắn rằng chúng tôi không kéo dài cuộc sống của họ và ngăn chặn thu gom rác thải từ xảy ra. Nếu bạn quan tâm đến chủ đề thì hãy thêm đồng bộ hóa nếu thích hợp.

Tôi đã thử nghiệm này với:

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

đâu JCollisionListener là:

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 là:

public class JGameObject extends GameObject { 
} 

(Để tham khảo nếu bạn muốn thực hiện cách tiếp cận khác mà bạn sẽ xem xét việc viết một bản đồ kiểu directorin).

+0

Thật tuyệt vời! Rất nhiều nỗ lực và một câu trả lời tuyệt vời. Điều này dường như giải quyết vấn đề của tôi, cũng như dạy tôi rất nhiều về swig. Có vẻ như chúng ta đôi khi cần phải ghi đè một nửa số người để có được những gì chúng tôi muốn. Cảm ơn rất nhiều. – enobayram

+0

Điều tốt đẹp về SWIG là nó cho phép bạn làm những việc như thế này khi bạn muốn, nhưng đóng gói nó tất cả để bạn chỉ phải làm việc trên các bit "lẻ" và tất cả các công việc grunt được thực hiện cho bạn. – Flexo

+0

Việc ra quyết định không bao giờ kết thúc của các kỹ sư phần mềm: "Tôi nên đầu tư thời gian của mình vào đâu?". SWIG có vẻ là một việc tốt vì cách tiếp cận của nó đối với vấn đề liên quan đến ngôn ngữ chéo có vẻ như chung chung. Tôi có nghĩa là, như một máy phát điện mã bên ngoài, nó miễn phí của hầu hết các hạn chế mà hoàn toàn dựa trên thư viện giải pháp (như tăng python) bị. – enobayram

Các vấn đề liên quan