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

发布于 2024-12-18 11:38:19 字数 853 浏览 0 评论 0原文

如何配置 SWIG .i 文件来处理 C FILE * 类型?下面的函数设置一个文件,以便可以将日志输出写入其中。我需要从 Java 类中调用 if 。目前,当我仅将 C 头文件包含在以下函数中时,SWIG 会生成 public static 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"

Exception:

[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

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 Function:

void setLogFile(FILE *fd);

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"

Exception:

[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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风为裳 2024-12-25 11:38:19

给定 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" 那么您可能需要 从 SWIG 隐藏原始版本


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

包装不仅仅是 setLogFile,适当包装 fopenfmemopen 等内容。 (我个人不太喜欢这个解决方案,所以我没有为其提供示例)


方法 3 - 公开一个采用 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< /code>,这将关闭文件句柄并使我们的 FILE* 无效。我们通过在 private static 变量中保留对 FileOutputStream 的引用来实现此目的。前一个类型映射的 pre="... 会导致保留最新的类型映射,直到我们更改为另一个类型映射。(如果我们确实调用 setLogFile 两次就可以了,事实上,我们发布了对之前的 FileOutputStream 的引用,这很好)

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:

  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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文