이러한 핵심 기능 중 하나를 포장하려면 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_char
에 byte[]
에서 갈 사소한 방법이 없습니다) 적절한 형식으로 데이터를 가져 오기 위해 필요한 명백한 (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);
}
}
%}
다시이 또 다른 단점이있다 : 들어 모듈 파일이되었을 것 - 함수 foo
과 bar
에 특유한 것입니다. 이전에 쓴 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
}
}
이 자바 인터페이스의 과부하로 표시되지만, 특정 모듈 아직도 :
그러나 포장에 대한 또 다른 접근 방식은 foo
및 bar
몇 %inline
과부하를 작성하는 것 게다가 여기에 필요한 JNI는 그렇지 않은 경우보다 복잡합니다. jenv
을 잡아 주어야 할 필요가 있습니다. 기본적으로 액세스 할 수 없습니다. 옵션은 느리게 호출하거나 매개 변수를 자동으로 채우는 유형 맵 numinputs=0
입니다. 어느 쪽의 방법이라도 복수 인수 typemap은 훨씬 좋게 보인다.
Java에서 수동으로 래퍼 메서드를 제공하는 것은 어떻습니까? 자바에서 배열을 사용하는 메소드와 마찬가지로'int offset, int length' 매개 변수도 없다 ... –
@SamuelAudet - 그렇게 할 수는 있지만, 잘 설계된 인터페이스가 아니라는 점을 배웠습니다. 문제는 만약 여러분이'byte []'를 가지고 있다면 그것을 typedap (대부분의 시간)을 써서'signed char * '로 변환하거나'% array_class'와'for'를 사용해야 할 것입니다 루프는 어쨌든 복사를 할 수 있습니다. 둘 다 꽤 못생긴다. – Flexo
@SamuelAudet - 수동 래퍼 메서드로 답변을 업데이트했습니다. 내 견해가 꽤 추해. – Flexo