2012-07-20 2 views
8

로 감싸 함수에 배열을 전달 헤더 같은 : 감안할 때 포인터 + 크기 또는 범위

#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")); 
} 

(나는,이 C 또는 중 하나가 될 수 편의를 위해 여기에 C++ (11)를 사용하여 C++ 당신이를 변경 한 경우 구현)하지만

어떻게하면 자바 측에서 배열을 취하고이 함수의 두 번째 매개 변수를 제공하기 위해 배열의 (알려진) 크기를 사용할 수 있습니까?

+0

Java에서 수동으로 래퍼 메서드를 제공하는 것은 어떻습니까? 자바에서 배열을 사용하는 메소드와 마찬가지로'int offset, int length' 매개 변수도 없다 ... –

+0

@SamuelAudet - 그렇게 할 수는 있지만, 잘 설계된 인터페이스가 아니라는 점을 배웠습니다. 문제는 만약 여러분이'byte []'를 가지고 있다면 그것을 typedap (대부분의 시간)을 써서'signed char * '로 변환하거나'% array_class'와'for'를 사용해야 할 것입니다 루프는 어쨌든 복사를 할 수 있습니다. 둘 다 꽤 못생긴다. – Flexo

+0

@SamuelAudet - 수동 래퍼 메서드로 답변을 업데이트했습니다. 내 견해가 꽤 추해. – Flexo

답변

12

이러한 핵심 기능 중 하나를 포장하려면 multi-argument typemap을 사용하는 것이 좋습니다.

프리앰블은 SWIG의 표준입니다. 당신은 모두의 유형으로 byte[]를 사용하는 SWIG을 지시하는 몇 Java typemaps을 사용해야하지만

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

을 먼저 : 나는 알고 자동으로 필요로하는 인터페이스의 사용자없이 공유 라이브러리를로드하는 내 개인 좋아하는 prgama을 사용 자바 인터페이스의 일부 - JNI와이를 호출하는 래퍼. 생성 모듈 파일에서 우리는 JNI 유형 jbyteArray을 사용할 것입니다. SWIG 인터페이스에서 직접 생성 한 JNI로 입력을 전달합니다.

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

이 우리가 다 인수 타입 맵을 작성할 수 있습니다 완료되면 :

%typemap(in,numinputs=1) (const signed char *arr, size_t sz) { 
    $1 = JCALL2(GetByteArrayElements, jenv, $input, NULL); 
    $2 = JCALL1(GetArrayLength, jenv, $input); 
} 
타입 맵에서의 작업이 우리가 무엇을 실제에 JNI 호출에 의해 주어진하는지 변환하는 것입니다

함수는 실제로 입력으로 기대합니다. 두 개의 실제 함수 인수가 Java 측에서 하나의 입력 만 받는다는 것을 나타 내기 위해 numinputs=1을 사용했지만,이 값은 어쨌든 기본값이므로 명시 적으로 명시 할 필요는 없습니다.

이 typemap에서 $1은 typemap의 첫 번째 인수, 즉이 경우이 함수의 첫 번째 인수입니다. Java 배열의 기본 저장소에 대한 포인터를 묻는 것으로 설정합니다 (실제로 복사 일 수도 복사 일 수도 있습니다). $2 두 번째 typemap 인수는 배열의 크기가되도록 설정합니다.

여기서 JCALLn 매크로는 typemap이 C 및 C++ JNI로 컴파일 될 수 있는지 확인합니다. 언어에 대한 적절한 호출로 확장됩니다.

우리는 실제 함수 호출이 반환되면 정리하기 위해 다른 타입 맵이 필요합니다

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

이것은 우리가 배열을 수행하고있는 JVM에게 ReleaseByteArrayElements를 호출합니다. 포인터 이 필요합니다. 우리가 얻은 자바 배열 객체입니다. 또한 내용을 다시 복사해야하는지 여부를 나타내는 매개 변수를 취합니다. iff이 수정되었고 처음에 가져온 포인터가 사본이었습니다. (전달 된 인수는 NULL이 우리가 사본을 받았는지 여부를 나타내는 jboolean에 대한 선택적 포인터입니다).제 변종

적인 typemap은 실질적으로 유사하다 :

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

유일한 차이점은 begin 포인터를 사용 end arugment을 계산하는 로컬 변수 sz 사용되는. 와 내가 테스트

%include "test.hh" 

이 두 기능 :

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

유일한 것은 우리가 작성한적인 typemap를 사용하여 헤더 파일 자체를 포장 꿀꺽 꿀꺽에게 할 일은 남아

예상대로 작동했습니다.

편의상 나는 이것을 작성하는 데 사용한 파일을 my site에 공유했습니다. 해당 아카이브에있는 모든 파일의 모든 행은이 응답을 순차적으로 따라 가면서 재구성 될 수 있습니다. 우리가 어떤 JNI없이 모든 일을 할 수 있었 참고로


는 우리가 실제 기능이 기대하는 형태로 (순수 자바) 입력을 변환 사용 과부하를 생성하는 %pragma(java) modulecode를 사용하여 호출합니다. (SWIGTYPE_p_signed_charbyte[]에서 갈 사소한 방법이 없습니다) 적절한 형식으로 데이터를 가져 오기 위해 필요한 명백한 (2 개) 사본 게다가

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

다시이 또 다른 단점이있다 : 들어 모듈 파일이되었을 것 - 함수 foobar에 특유한 것입니다. 이전에 쓴 typemaps는 특정 함수에만 국한된 것은 아니며, 두 함수를 사용하는 경우 동일한 함수에서 여러 번 적용될 수도 있습니다. 범위 또는 두 포인터 + 길이 조합. 이런 식으로하는 것의 한 가지 장점은 여러분이 다른 포장 된 함수를 가지고있는 경우에 SWIGTYPE_p_signed_char을주고있는 경우, 원하는 경우 사용할 수있는 과부하가 여전히 남아 있다는 것입니다. %array_class에서 ByteArray이있는 경우에도 여전히 Java에서 포인터 산술을 수행 할 수 없어 end을 생성해야합니다.

표시된 원래 방법은 과도한 복사본을 만들지 않고 재사용 할 수 있다는 장점이있는 Java에서 더 깨끗한 인터페이스를 제공합니다.

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

이 자바 인터페이스의 과부하로 표시되지만, 특정 모듈 아직도 :


그러나 포장에 대한 또 다른 접근 방식은 foobar%inline 과부하를 작성하는 것 게다가 여기에 필요한 JNI는 그렇지 않은 경우보다 복잡합니다. jenv을 잡아 주어야 할 필요가 있습니다. 기본적으로 액세스 할 수 없습니다. 옵션은 느리게 호출하거나 매개 변수를 자동으로 채우는 유형 맵 numinputs=0입니다. 어느 쪽의 방법이라도 복수 인수 typemap은 훨씬 좋게 보인다.