Given test.h which looks like:
#include <stdio.h>
inline void setLogFile(FILE *fd) {
fprintf(fd, "Test\n");
fflush(fd);
}
I can see three approaches you might chose to take to wrapping this function:
Method 1 - Pass a String from Java:
Expose a function to Java that expects a file name passed as a String, not a FILE*:
%module method1
%{
#include "test.h"
%}
%inline %{
void setLogFile(const char *fn) {
FILE *f = fopen(fn, "w");
setLogFile(f);
}
%}
This uses %inline to instruct SWIG to wrap this function at the same time as defining it. If you still use %include "test.h" then you'd probably want to hide the original version from SWIG.
Method 2 - Wrap more of stdio.h:
Wrap more than just setLogFile, wrap things like fopen, fmemopen etc. as appropriate. (I don't like this solution much personally so I've not made an example for it)
Method 3 - Expose a Java interface that takes a 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"
This does the following things:
- We want the input to the actual public part of the module to be
java.io.FileOutputStream.
- The the Java side of the JNI code is going to take a
java.io.FileDescriptor instead however.
- The C++ side of the JNI code is going to see this as a
jobject
- On the C++ side we're going to do something that's a little evil - read a private
int field in the FileDescriptor class (see here). This is probably not portable and reading private parts of classes is generally considered bad, but it allows us to get something we can pass to fdopen() to get a FILE* for the "real" call
- Mostly this typemap takes the
FileOutputStream and calls getFD() on it to get the FileDescriptor object for it. It also adds an exception specification to match getFD() and performs one other function which is part of the next point
- We need to be sure that Java won't garbage collect and finalize the
FileOutputStream, which would close the file handle and invalidate our FILE*. We do this by keeping a reference to the FileOutputStream we were given in a private static variable. The pre="... of the previous typemap causes the most recent one to be retained until we change to another one. (If we do call setLogFile twice it's OK and in fact good that we release our reference to the previous FileOutputStream)