tangguo

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

java

您将如何配置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

阅读 300

收藏
2020-12-01

共1个答案

小编典典

给定的test.h看起来像:

#include <stdio.h>

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

我可以看到您可能会选择使用三种方法来包装此函数:

方法1-String从Java传递a :
向Java公开一个函数,该函数期望文件名以而String不是a形式传递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"

这将执行以下操作:

  1. 我们希望输入到模块的实际公共部分java.io.FileOutputStream
  2. 但是,JNI代码的Java端将改为使用java.io.FileDescriptor
  3. JNI代码的C ++端将其视为 jobject
  4. 在C ++方面,我们将做一些有点邪恶的事情-读取类中的私有int字段FileDescriptor(请参见此处)。这可能不是便携式和阅读类私处通常被认为是不好的,但它使我们能够得到的东西我们可以通过fdopen()得到一个FILE*“真正”的号召
  5. 通常,此类型图接受FileOutputStream并对其进行调用getFD()以获取其FileDescriptor对象。它还添加了一个异常规范以匹配getFD()并执行另一个功能,这是下一点的一部分
  6. 我们需要确保Java不会垃圾回收并完成FileOutputStream,这将关闭文件句柄并使我们的无效FILE*。我们通过FileOutputStreamprivate static变量中引用我们来实现。在pre=”…以前的类型映射的原因最近的一次被保留,直到我们切换到另一个。(如果我们打电话setLogFile两次,那没关系,实际上释放我们对前一个的引用是件好事FileOutputStream
2020-12-01