2012-07-20 44 views
8

Cho một tiêu đề như:Vượt qua một mảng tới một hàm bọc như kích thước con trỏ + hoặc phạm vi

#include <iostream> 
#include <algorithm> 
#include <iterator> 

inline void foo(const signed char *arr, size_t sz) { 
    std::copy_n(arr, sz, std::ostream_iterator<int>(std::cout, "\n")); 
} 

inline void bar(const signed char *begin, const signed char *end) { 
    std::copy(begin, end, std::ostream_iterator<int>(std::cout, "\n")); 
} 

(tôi đã sử dụng C++ 11 ở đây cho thuận tiện, điều này có thể là một trong hai C hoặc C++ nếu bạn đã thay đổi triển khai mặc dù)

Làm cách nào để bọc các chức năng này để chỉ lấy một mảng ở phía Java và sử dụng kích thước (đã biết) của mảng để cung cấp thông số thứ hai cho các hàm này?

+0

Chỉ cần cung cấp phương pháp trình bao bọc theo cách thủ công trong Java? Nó không giống như các phương thức lấy một mảng trong Java cũng không đi kèm với các tham số 'int offset, int length' ... –

+0

@SamuelAudet - bạn có thể làm điều đó, nhưng tôi cho rằng đó không phải là một giao diện được thiết kế tốt (nhân đôi thông tin chỉ vì niềm vui của nó). Vấn đề là mặc dù nếu bạn có 'byte []' bạn sẽ cần phải viết một bản đồ (phần lớn thời gian) để chuyển đổi nó thành 'ký char *', hoặc sử dụng '% array_class' và' for' lặp lại để làm một bản sao anyway. Cả hai đều khá xấu. – Flexo

+0

@SamuelAudet - Tôi đã cập nhật câu trả lời của mình bằng phương pháp trình bao bọc thủ công. Nó khá xấu trong quan điểm của tôi. – Flexo

Trả lời

12

Điểm mấu chốt của việc này là để bọc một trong các chức năng này bạn sẽ muốn sử dụng multi-argument typemap.

Phần mở đầu khá chuẩn cho SWIG. Tôi đã từng prgama yêu thích cá nhân của tôi để tự động tải các thư viện chia sẻ mà không cần người dùng của giao diện cần phải biết:

%module 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); 
    } 
    } 
%} 

Đầu tiên mặc dù bạn sẽ cần sử dụng một vài Java typemaps hướng dẫn sử dụng SWIG byte[] như các loại của cả hai các bộ phận của giao diện Java - JNI và trình bao bọc gọi nó. Trong tệp mô-đun tạo, chúng tôi sẽ sử dụng loại JNI jbyteArray. Chúng ta đang truyền đầu vào trực tiếp từ giao diện SWIG tới JNI mà nó tạo ra.

%typemap(jtype) (const signed char *arr, size_t sz) "byte[]" 
%typemap(jstype) (const signed char *arr, size_t sz) "byte[]" 
%typemap(jni) (const signed char *arr, size_t sz) "jbyteArray" 
%typemap(javain) (const signed char *arr, size_t sz) "$javainput" 

Khi điều này được thực hiện, chúng tôi có thể viết một typemap đa đối số:

%typemap(in,numinputs=1) (const signed char *arr, size_t sz) { 
    $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); 
    $2 = JCALL1(GetArrayLength, jenv, $input); 
} 

Công việc của trong typemap là để chuyển đổi từ những gì chúng ta đang đưa ra bởi các cuộc gọi JNI để những gì thực chức năng thực sự mong đợi như là một đầu vào. Tôi đã sử dụng numinputs=1 để chỉ ra rằng hai đối số thực sự chỉ lấy một đầu vào ở phía Java, nhưng đây là giá trị mặc định, vì vậy không bắt buộc phải nêu rõ điều đó.

Trong typemap $1 là đối số đầu tiên của typemap, tức là đối số đầu tiên của hàm của chúng ta trong trường hợp này. Chúng tôi thiết lập điều đó bằng cách yêu cầu một con trỏ đến kho lưu trữ cơ bản của mảng Java (có thể hoặc không thực sự là một bản sao). Chúng tôi đặt $2, đối số typemap thứ hai là kích thước của mảng.

Các macro JCALLn ở đây đảm bảo rằng sơ đồ trang web có thể biên dịch với cả C và C++ JNI. Nó mở rộng đến cuộc gọi thích hợp cho ngôn ngữ.

Chúng ta cần typemap khác để làm sạch một lần gọi hàm thực đã trở lại:

%typemap(freearg) (const signed char *arr, size_t sz) { 
    // Or use 0 instead of ABORT to keep changes if it was a copy 
    JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT); 
} 

này gọi ReleaseByteArrayElements nói với JVM chúng tôi đang thực hiện với mảng. Nó cần con trỏ đối tượng mảng Java mà chúng ta thu được từ đó. Ngoài ra, phải có tham số cho biết liệu nội dung có được sao chép lại iff chúng đã được sửa đổi hay không và con trỏ chúng tôi nhận được là bản sao ở vị trí đầu tiên. (Đối số chúng ta đã thông qua NULL là một con trỏ tùy chọn đến một số jboolean cho biết chúng ta đã được cung cấp một bản sao).

Đối với các biến thể thứ hai typemaps tương đồng đáng kể:

%typemap(in,numinputs=1) (const signed char *begin, const signed char *end) { 
    $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); 
    const size_t sz = JCALL1(GetArrayLength, jenv, $input); 
    $2 = $1 + sz; 
} 

%typemap(freearg) (const signed char *begin, const signed char *end) { 
    // Or use 0 instead of ABORT to keep changes if it was a copy 
    JCALL3(ReleaseByteArrayElements, jenv, $input, $1, JNI_ABORT); 
} 

%typemap(jtype) (const signed char *begin, const signed char *end) "byte[]" 
%typemap(jstype) (const signed char *begin, const signed char *end) "byte[]" 
%typemap(jni) (const signed char *begin, const signed char *end) "jbyteArray" 
%typemap(javain) (const signed char *begin, const signed char *end) "$javainput" 

Sự khác biệt duy nhất là sử dụng các biến địa phương sz để tính end arugment bằng cách sử dụng con trỏ begin.

Điều duy nhất còn lại để làm là nói với SWIG để bọc các tập tin tiêu đề riêng của mình, bằng cách sử dụng typemaps chúng ta vừa viết:

%include "test.hh" 

Tôi đã thử nghiệm cả các chức năng này với:

public class run { 
    public static void main(String[] argv) { 
    byte[] arr = {0,1,2,3,4,5,6,7}; 
    System.out.println("Foo:"); 
    test.foo(arr); 
    System.out.println("Bar:"); 
    test.bar(arr); 
    } 
} 

Làm việc như mong đợi.

Để thuận tiện, tôi đã chia sẻ các tệp tôi đã sử dụng bằng văn bản này trên my site. Mỗi dòng của mỗi tập tin trong kho lưu trữ đó có thể được xây dựng lại bằng cách làm theo câu trả lời này tuần tự.


Để tham khảo, chúng tôi đã có thể làm toàn bộ điều mà không bất kỳ JNI cuộc gọi, sử dụng %pragma(java) modulecode để tạo ra một tình trạng quá tải mà chúng ta sử dụng chuyển đổi các đầu vào (trong Java thuần túy) vào hình thức dự kiến ​​bởi các chức năng thực sự. Cho rằng các tập tin mô-đun có thể:

%module test 

%{ 
#include "test.hh" 
%} 

%include <carrays.i> 
%array_class(signed char, ByteArray); 

%pragma(java) modulecode = %{ 
    // Overload foo to take an array and do a copy for us: 
    public static void foo(byte[] array) { 
    ByteArray temp = new ByteArray(array.length); 
    for (int i = 0; i < array.length; ++i) { 
     temp.setitem(i, array[i]); 
    } 
    foo(temp.cast(), array.length); 
    // if foo can modify the input array we'll need to copy back to: 
    for (int i = 0; i < array.length; ++i) { 
     array[i] = temp.getitem(i); 
    } 
    } 

    // How do we even get a SWIGTYPE_p_signed_char for end for bar? 
    public static void bar(byte[] array) { 
    ByteArray temp = new ByteArray(array.length); 
    for (int i = 0; i < array.length; ++i) { 
     temp.setitem(i, array[i]); 
    } 
    bar(temp.cast(), make_end_ptr(temp.cast(), array.length)); 
    // if bar can modify the input array we'll need to copy back to: 
    for (int i = 0; i < array.length; ++i) { 
     array[i] = temp.getitem(i); 
    } 
    } 
%} 

// Private helper to make the 'end' pointer that bar expects 
%javamethodmodifiers make_end_ptr "private"; 
%inline { 
    signed char *make_end_ptr(signed char *begin, int sz) { 
    return begin+sz; 
    } 
} 

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

Bên cạnh (hai) bản rõ ràng cần thiết để có được các dữ liệu vào đúng loại (không có cách nào tầm thường để đi từ byte[] để SWIGTYPE_p_signed_char) và trở lại này có nhược điểm khác - nó đặc trưng cho các chức năng foobar, trong khi các typemaps chúng tôi đã viết trước đó không cụ thể cho một hàm nhất định - chúng sẽ được áp dụng ở bất cứ nơi nào chúng khớp, thậm chí nhiều lần trên cùng một hàm nếu bạn tình cờ có một hàm phạm vi hoặc hai kết hợp chiều dài con trỏ +. Một lợi thế của việc làm theo cách này là nếu bạn xảy ra để có các chức năng bọc khác được cung cấp cho bạn SWIGTYPE_p_signed_char trở lại sau đó bạn vẫn sẽ có quá tải có sẵn để sử dụng nếu bạn mong muốn. Ngay cả trong trường hợp bạn có ByteArray từ %array_class bạn vẫn không thể thực hiện số học con trỏ trong Java cần thiết để tạo ra end cho bạn.

Cách ban đầu được hiển thị cung cấp giao diện rõ ràng hơn trong Java, với các ưu điểm bổ sung không tạo bản sao quá mức và có thể sử dụng lại được nhiều hơn.


Tuy nhiên, một cách tiếp cận khác để gói sẽ được để viết một vài %inline quá tải cho foobar:

%inline { 
    void foo(jbyteArray arr) { 
    // take arr and call JNI to convert for foo 
    } 
    void bar(jbyteArray arr) { 
    // ditto for bar 
    } 
} 

Đây là những trình bày như quá tải trong giao diện Java, nhưng họ vẫn mô-đun cụ thể và bổ sung JNI yêu cầu ở đây là phức tạp hơn nó sẽ cần phải được - bạn cần phải sắp xếp để có được giữ của jenv bằng cách nào đó, mà không thể truy cập theo mặc định. Các tùy chọn là một cuộc gọi chậm để có được nó, hoặc một numinputs=0 typemap điền vào các tham số tự động. Dù bằng cách nào thì typemap đa đối số có vẻ đẹp hơn rất nhiều.

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