Учитывая test.h, который выглядит так:
#include <stdio.h>
inline void setLogFile(FILE *fd) {
fprintf(fd, "Test\n");
fflush(fd);
}
Я вижу три подхода, которые вы могли бы выбрать, чтобы обернуть эту функцию:
Метод 1 - Передача String
из Java:
Предоставьте Java функцию, которая ожидает, что имя файла передается как String
, а не FILE*
:
%module method1
%{
#include "test.h"
%}
%inline %{
void setLogFile(const char *fn) {
FILE *f = fopen(fn, "w");
setLogFile(f);
}
%}
Используется %inline
, чтобы дать команду SWIG обернуть эту функцию одновременно с ее определением. Если вы по-прежнему используете %include "test.h"
, возможно, вы захотите скрыть оригинальную версию от SWIG .
Метод 2 - Оберните больше stdio.h:
Оберните больше, чем просто setLogFile
, оберните вещи как fopen
, fmemopen
и т. Д. В зависимости от ситуации. (Мне не очень нравится это решение, поэтому я не стал примером для него)
Метод 3 - Предоставьте интерфейс Java, который принимает FileOutputStream
:
%module method3
%{
#include "test.h"
#include <cassert>
%}
// 3:
%typemap(jni) FILE *fd "jobject"
// 1:
%typemap(jstype) FILE *fd "java.io.FileOutputStream"
// 2:
%typemap(jtype) FILE *fd "java.io.FileDescriptor"
// 4:
%typemap(in) (FILE *fd) {
jfieldID field_fd;
jclass class_fdesc;
int rawfd;
class_fdesc = jenv->FindClass("java/io/FileDescriptor");
assert(class_fdesc);
field_fd = jenv->GetFieldID(class_fdesc, "fd", "I");
assert(field_fd);
rawfd = jenv->GetIntField($input, field_fd);
$1 = fdopen(rawfd, "w");
// Add some code to throw a Java exception if $1 is NULL (i.e. error)
}
// 5:
%typemap(javain, pre=" retainFD = $javainput;",
throws="java.io.IOException") FILE *fd "$javainput.getFD()"
// 6:
%pragma(java) modulecode=%{
private static java.io.FileOutputStream retainFD;
%}
%include "test.h"
Это делает следующие вещи:
- Мы хотим, чтобы вход для фактической общедоступной части модуля был
java.io.FileOutputStream
.
- Однако на стороне Java кода JNI вместо этого будет взято
java.io.FileDescriptor
.
- Сторона C ++ кода JNI увидит это как
jobject
- На стороне C ++ мы собираемся сделать что-то немного злое - прочитайте личное поле
int
в классе FileDescriptor
( см. Здесь ). Это, вероятно, не переносимо, и чтение закрытых частей классов обычно считается плохим, но оно позволяет нам получить то, что мы можем передать fdopen()
, чтобы получить FILE*
для "настоящего" вызова
- В основном, эта карта типов берет
FileOutputStream
и вызывает getFD()
для получения объекта FileDescriptor
. Он также добавляет спецификацию исключения для соответствия getFD()
и выполняет еще одну функцию, которая является частью следующей точки
- Мы должны быть уверены, что Java не будет собирать мусор и завершать
FileOutputStream
, что закроет дескриптор файла и лишит законной силы наш FILE*
. Мы делаем это, сохраняя ссылку на FileOutputStream
, которую нам дали в переменной private static
. pre="...
из предыдущей карты типов приводит к тому, что самая последняя сохраняется до тех пор, пока мы не перейдем к другой. (Если мы дважды позвоним setLogFile
, то все в порядке и на самом деле хорошо, что мы выпустим нашу ссылку на предыдущий FileOutputStream
)