2012-04-18 39 views
11

Tôi đã không thấy điều này ở bất cứ nơi nào (hoặc có lẽ tôi là jsut đơn giản không nhìn thấy nó) nhưng có cách nào để sử dụng JNI để trả lại c/c + + đối tượng và sử dụng đối tượng trong java?Tôi có thể tham khảo các đối tượng C++ trong Mã Java bằng JNI không?

Ví dụ (rất đơn giản):

class simpleClass{ 
... 
private: 
int intVar; 
public: 
int getIntVar(); 
void setIntVar(int someNum); 
... 
} 

Trong mã java của tôi, làm thế nào tôi sẽ đi về làm một cái gì đó như:

... 
simpleClass sc = new simpleClass(); 
sc.setIntVar(9); 
System.out.println(sc.getIntVar()); 
... 

Tôi nhận ra đây là một ví dụ rất đơn giản nhưng tôi m chỉ cần tìm khái niệm - lớp tôi có trong tâm trí đó là trong c + + là rất lớn và đang tìm cách để tránh tạo ra một TÔN của các phương pháp bao bọc ...

Nếu nó không thể là tốt, chỉ cần hop ing để tiết kiệm một vài ngày mã hóa lol

Trả lời

7

Không, bạn không thể. Các C++ và Java ABIs hoàn toàn khác nhau - cho một, C++ không định nghĩa một. Và thực sự C++ có rất nhiều tính năng mà không thể được ánh xạ tới Java ở tất cả điều này chỉ không thể làm việc. Làm thế nào để bạn mong đợi Java sẽ xử lý các mẫu C++? Con trỏ đến nguyên thủy? Đối tượng không phải là con trỏ?

Bây giờ những gì bạn có thể làm, là sử dụng SWIG để tạo ra các phương pháp wrapper phù hợp với bạn - đó thực sự sẽ làm việc và không phải là công việc nhiều hơn so với những gì bạn có kế hoạch :)

+4

Lưu ý rằng các mẫu C++ không liên quan đến câu hỏi này, vì các mẫu được biên dịch trong C++. JNI sẽ phải hỗ trợ các lớp được khởi tạo từ các khuôn mẫu đó, trông không khác gì so với các lớp thông thường. –

+0

@Dan Nếu bạn viết một lớp mẫu không chỉ tiêu đề, bạn phải có khả năng báo cho trình biên dịch tạo một phiên bản mới của lớp trong mã máy khách, vì vậy tôi nghĩ rằng trình biên dịch cũng phải biết về các khuôn mẫu (mặc dù không có ý tưởng nếu đó là yêu cầu của tiêu chuẩn - không bao giờ phải làm điều đó trong thực tế). – Voo

+0

@Voo Nó cần định nghĩa của mẫu tại thời gian biên dịch để tạo ra sự khởi tạo. Một khi được tạo cho mỗi loại cần thiết sử dụng mã đối tượng cũng giống như khi bạn đã viết ra một lớp cho từng loại. AFAIK mà không bao gồm tệp cpp bạn không thể tham chiếu mẫu không có trong đơn vị dịch của bạn trừ khi định nghĩa của nó hoàn toàn nội tuyến vì cùng một lý do. – AJG85

0

JNI định nghĩa giao diện của nó cho nguyên thủy (hoặc khá nguyên thủy) các loại chỉ, cũng như bộ nhớ đệm qua/chức năng quản lý. Không có khả năng ánh xạ các kiểu đối tượng phức tạp. Tuy nhiên, bạn có thể đạt được hiệu ứng này bằng cách viết các hàm tuần tự hóa/đơn hóa của riêng bạn, như trong Returning a C++ class to Java via JNI.

+0

thú vị ... đây là một khái niệm rất thú vị. Tôi đánh giá cao liên kết! Cảm ơn! – redhotspike

12

Phiên bản Java của SimpleClass phải làm hai việc. Một, giữ một giá trị dài riêng để lưu trữ giá trị của con trỏ C++ đến đối tượng gốc sao lưu (bạn có thể phải sử dụng BigInteger tùy thuộc vào cách một con trỏ bản địa lớn có thể được - không ký dài lâu?). Thứ hai, tạo các phương pháp công khai (ví dụ: setIntVal) gốc.

public class SimpleClass { 
    private long nativePtr; 

    public SimpleClass() { 
     nativePtr = initNativeSimpleClass(); 
    } 

    public void destroy() { 
     destroyNativeSimpleClass(); 
     nativePtr = 0L; 
    } 

    protected void finalize() throws Throwable { 
     destroyNativeSimpleClass(); 
     nativePtr = 0L; 
    } 

    public native int getIntVal(); 
    public native void setIntVal(int val); 

    private native long initNativeSimpleClass(); 
    private native void destroyNativeSimpleClass(); 
} 

Sau đó, triển khai các phương pháp gốc đó trong mã JNI của bạn. Phương thức initNativeSimpleClass() sẽ mới một bản sao C++ của SimpleClass. Phương pháp destroyNativeSimpleClass() sau đó sẽ xóa xóa ví dụ đó. Các phương thức accessor sẽ sử dụng giá trị nativePtr, đưa nó vào một con trỏ thực và thực hiện các thao tác thích hợp trên cá thể sao lưu gốc.

Thành ngữ này đặt ra một nguy cơ thực sự của bộ nhớ bị rò rỉ vì người dùng của lớp PHẢI gọi phá hủy khi chúng được thực hiện với một cá thể. Nếu không, bản sao gốc có thể không được hủy đúng cách. Bạn có thể, như tôi đã chỉ ra trong ví dụ, ghi đè lên finalize để gọi hàm hủy diệt gốc, nhưng tất cả các thông báo về cách hoàn thành không thể dựa vào vẫn áp dụng. Bằng cách đặt giá trị nativePtr thành 0 khi hủy, bạn tránh lỗi seg nếu phá hủy được gọi nhiều lần (an toàn trong C++ để xóa NULL).

+0

Điều này không tránh việc tạo các hàm bao bọc nhưng nó thực sự là cách duy nhất để sử dụng JNI để đặt mặt trước Java vào các lớp gốc. – pedorro

+0

Tôi chắc chắn như thế này đưa vào những gì tôi đề xuất ... Tôi phải nói rằng tôi như thế này tốt hơn so với chỉ "sử dụng" đối tượng C++: nó cung cấp một cách để tài liệu mà tôi đang sử dụng các phương pháp bản địa. Cảm ơn! – redhotspike

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