用于处理Java中的FILE * C输入参数的SWIG配置

时间:2011-11-30 03:00:31

标签: java swig

如何配置SWIG .i文件来处理C FILE *类型?下面的函数设置一个文件,以便可以将日志输出写入它。我需要从Java类调用。目前,当我只包含带有以下函数的C头文件时,SWIG会生成一个公共静态void setLogFile(SWIGTYPE_p_FILE fd)函数。有任何想法吗?

C函数:

void setLogFile(FILE *fd);

我使用下面的方法尝试了#1并获得了以下例外:

test.i:

%module Example
%{
#include "headerLogFile.h"
%}

%inline %{
void setLogFile(const char *fn) {
  FILE *f = fopen(fn, "w");
  setLogFile(f);
}
%}
%ignore setLogFile; 
%include "headerLogFile.h"

例外:

[exec] test_wrap.c:193: error: conflicting types for 'setLogFile'
[exec] /test/include/headerLogFile.h:96: error: previous declaration of 'setLogFile' was here
[exec] test_wrap.c: In function `setLogFile':
[exec] test_wrap.c:195: warning: passing arg 1 of `setLogFile' from incompatible pointer type

1 个答案:

答案 0 :(得分:2)

给出test.h看起来像:

#include <stdio.h>

inline void setLogFile(FILE *fd) {
  fprintf(fd, "Test\n");
  fflush(fd);
}

我可以看到你可以选择采用三种方法来包装这个功能:

方法1 - 从Java传递String

向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",那么你可能想要hide the original version from SWIG


方法2 - 包含更多stdio.h:

包裹的不只是setLogFile,请根据需要包含fopenfmemopen等内容。 (我不太喜欢这个解决方案,所以我没有为它做出一个例子)


方法3 - 公开一个采用FileOutputStream

的Java接口
%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"

这可以做到以下几点:

  1. 我们希望模块的实际公共部分的输入为java.io.FileOutputStream
  2. 然而,JNI代码的Java端将取代java.io.FileDescriptor
  3. JNI代码的C ++方面将其视为jobject
  4. 在C ++方面,我们将做一些有点邪恶的事情 - 阅读int类(see here)中的私有FileDescriptor字段。这可能是不可移植的,阅读私有部分的课程通常被认为是不好的,但它允许我们得到一些我们可以传递给fdopen()以获得FILE*“真实”电话的内容
  5. 这个类型地图主要使用FileOutputStream并在其上调用getFD()来获取FileDescriptor对象。它还添加了一个匹配getFD()的异常规范,并执行另一个函数,它是下一个点的一部分
  6. 我们需要确保Java不会垃圾收集并最终确定FileOutputStream,这将关闭文件句柄并使FILE*无效。我们通过保留对FileOutputStream变量中给出的private static的引用来实现此目的。前一个typemap的pre="...会导致保留最新的一个,直到我们更改为另一个。 (如果我们两次调用setLogFile就可以了,事实上我们发布了对前一个FileOutputStream的引用