用 Java 处理 FILE * C 输入参数的 SWIG 配置 [英] SWIG Configuration to Handle a FILE * C input Parameter in Java

查看:25
本文介绍了用 Java 处理 FILE * C 输入参数的 SWIG 配置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

How would you configure the SWIG .i file to handle the C FILE * type? The below function sets a file so that log output can be written to it. I need to call if from a Java class. Currently a public static void setLogFile(SWIGTYPE_p_FILE fd) function is generated by SWIG when I just include the C header file with the below function. Any ideas?

C 函数:

void setLogFile(FILE *fd);

我尝试使用下面的 #1 并得到以下异常:

I attempted #1 using the below and got the below exception:

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

推荐答案

鉴于 test.h 看起来像:

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:

向 Java 公开一个函数,该函数需要将文件名作为 String 传递,而不是 FILE*:

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);
}
%}

这使用 %inline 指示 SWIG 在定义它的同时包装这个函数.如果你仍然使用 %include "test.h" 那么你可能想要 在 SWIG 中隐藏原始版本.

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.

包装的不仅仅是 setLogFile,还要适当包装 fopenfmemopen 等.(我个人不太喜欢这个解决方案,所以我没有举例说明)

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)

%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++ 方面,我们将做一些有点邪恶的事情 - 读取 FileDescriptor 类中的私有 int 字段(见这里).这可能是不可移植的,读取类的私有部分通常被认为是不好的,但它允许我们获得一些可以传递给 fdopen() 的东西,以获得一个 FILE*真正的"电话
  5. 大多数情况下,此类型映射采用 FileOutputStream 并对其调用 getFD() 以获取 FileDescriptor 对象.它还添加了一个异常规范以匹配 getFD() 并执行另一个函数,这是下一点的一部分
  6. 我们需要确保 Java 不会垃圾回收和终结 FileOutputStream,这会关闭文件句柄并使我们的 FILE* 无效.我们通过保持对 FileOutputStream 的引用来实现这一点,我们在 private static 变量中给出了它.前一个类型映射的 pre="... 导致保留最新的一个,直到我们更改为另一个.(如果我们确实调用了两次 setLogFile事实上,我们释放了对之前的引用 FileOutputStream)
  1. We want the input to the actual public part of the module to be java.io.FileOutputStream.
  2. The the Java side of the JNI code is going to take a java.io.FileDescriptor instead however.
  3. The C++ side of the JNI code is going to see this as a jobject
  4. 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
  5. 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
  6. 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)

这篇关于用 Java 处理 FILE * C 输入参数的 SWIG 配置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆