我有一个带有虚方法的 C++ 类。我正在使用 directors 并且我在 Java 中对 C++ 类进行了子类化。此类用于接收来自 C++ 代码的回调。因此 Java 类随后被传递给 C++,C++ 调用它的方法(向上调用 Java)。有数组参数(或指针数组,我都试过了),它们被转换为 SWIGTYPE_p_double。
我想要一个 Java 端类型签名 double[] 并且当然在那个 double[] 参数中有数组的内容(复制内容没问题)。
我该怎么做?
我已经使用从一些电子邮件列表中提取的以下代码进行了尝试:
c_backend.i:
%module(directors="1") c_backend
%{
#include "c_backend.h"
%}
%typemap(directorin, descriptor="[D") (double *DOUBLE, size_t LENGTH) {
jdoubleArray jd = (jenv)->NewDoubleArray($2);
(jenv)->SetDoubleArrayRegion(jd, 0, $2, (jdouble *)$1);
$input = jd;
}
%typemap(directorargout) (double *DOUBLE, size_t LENGTH)
%{(jenv)->GetDoubleArrayRegion($input, 0, $2, (jdouble *)$1); %}
%feature("director") CallbackHandler;
%include "c_backend.h"
c_backend.h:
#ifndef CALLBACK_HANDLER_H
#define CALLBACK_HANDLER_H
#include <stdio.h>
class CallbackHandler {
public:
virtual ~CallbackHandler() {}
virtual void statusUpdate( double *params, size_t size ) {
printf("in C++ statusUpdate\n");
}
};
class Server {
public:
void doSomething( CallbackHandler * );
};
#endif
c_backend.cpp:
#include "c_backend.h"
#include <stdio.h>
#include <stdlib.h>
void Server::doSomething( CallbackHandler *ch ) {
double *params = (double *)malloc(3*sizeof(double));
params[0] = 1.1;
params[1] = 2.2;
params[2] = 3.3;
printf("In doSomthing\n");
ch->statusUpdate(params,3);
printf("exiting doSomthing\n");
}
Java前端.java:
public class JavaFrontend {
static {
System.loadLibrary("CBackend");
}
public static void main( String[] args ) {
JFCallbackHandler jf = new JFCallbackHandler();
new Server().doSomething(jf);
}
public static class JFCallbackHandler extends CallbackHandler {
public void statusUpdate( double params[], long size ) {
System.out.println("Java got params: "+params);
}
}
}
还有一个要编译的 Makefile:
JAVA_INCLUDE=-I/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include/darwin
all:
c++ -c c_backend.cpp
swig -java -c++ $(JAVA_INCLUDE) c_backend.i
c++ $(JAVA_INCLUDE) -c c_backend_wrap.cxx
c++ -dynamiclib -o libCBackend.jnilib *.o -framework JavaVM
javac *.java
clean:
rm -rf *.class *.o *_wrap.cxx *_wrap.h Server.java SWIGTYPE*.java c_backend*.java CallbackHandler.java
Swig 版本:
SWIG Version 3.0.8
Compiled with clang++ [x86_64-apple-darwin15.2.0]
Configured options: +pcre
Please see http://www.swig.org for reporting bugs and further information
最佳答案
我付出了一些努力才完成这项工作。您找到的作为起点的类型图是合理的,但我认为不完整。我认为如果没有匹配的 in/jtype/jstype/jni/javadirectorin/javain 一个,你就不能现实地编写 directorin/directorargout 类型映射,因为你很快就会在生成的代码的各种片段之间产生不匹配的期望。 (director 代码也可以调用常规Java 类,也可以被它调用)。
此外,我认为在 Java 中使用多参数类型映射将指针和大小参数压缩为单个参数要好得多,因为长度隐含地是 Java 中数组的一个属性。
所以这里是我必须做的事情的快速总结:
- 首先,您现有的类型映射不会应用于您的代码,因为它们在参数类型和参数名称上都匹配。
%apply
是执行此操作的巧妙方法,尤其是对于像这样的多参数类型映射。 - 其次,为 in 而不仅仅是 directorin 添加了相应的类型映射。
- 第三,添加了类型映射以通过代理将
double[]
从 C++ 一直传递到 Java。 - 最后,因为类型现在只是
double[]
和一个隐式大小,测试用例需要更新以确保它仍然是覆盖而不是重载。为了更好地衡量,我添加了@Override
。
我做了一些风格上的改变,因为它们是更好的代码:
- 从 jdouble 的类型映射输入中删除了强制转换。充其量他们应该是一个空操作(
double*
->jdouble*
),但最坏的情况是他们会隐藏一些不愉快的事情。 - 将类型图设置在它们自己的一个 block 中。这意味着您可以在同一个函数上使用同一个类型映射两次,而不会导致局部变量名称冲突。 (虽然我最后还是去掉了局部变量)
- 使用 SWIG 的 JCALLx 宏 - 这显然是 C++ 代码,但这是我在编写 SWIG 代码时尽量保持的习惯。
所以最后你的 SWIG 界面看起来像这样:
%module(directors="1") c_backend
%{
#include "c_backend.h"
%}
%typemap(jstype) (double *DOUBLE, size_t LENGTH) "double[]"
%typemap(jtype) (double *DOUBLE, size_t LENGTH) "double[]"
%typemap(jni) (double *DOUBLE, size_t LENGTH) "jdoubleArray"
%typemap(javadirectorin) (double *DOUBLE, size_t LENGTH) "$jniinput"
%typemap(javain) (double *DOUBLE, size_t LENGTH) "$javainput"
%typemap(in,numinputs=1) (double *DOUBLE, size_t LENGTH) {
// Note the NULL here if you don't want to be making changes visible
$1 = JCALL2(GetDoubleArrayElements, jenv, $input, NULL);
$2 = JCALL1(GetArrayLength, jenv, $input);
}
%typemap(freearg) (double *DOUBLE, size_t LENGTH) {
// Swap 0 for JNI_ABORT if you don't want to make changes visible
JCALL3(ReleaseDoubleArrayElements, jenv, $input, $1, 0);
}
%typemap(directorin,descriptor="[D") (double *DOUBLE, size_t LENGTH) {
$input = JCALL1(NewDoubleArray, jenv, $2);
JCALL4(SetDoubleArrayRegion, jenv, $input, 0, $2, $1);
}
%typemap(directorargout) (double *DOUBLE, size_t LENGTH) {
(jenv)->GetDoubleArrayRegion($input, 0, $2, $1);
}
%feature("director") CallbackHandler;
%apply (double *DOUBLE, size_t LENGTH) { (double *params, size_t size) };
%include "c_backend.h"
这足以使您的测试用例通过上述更改正常工作,使其覆盖而不是现在过载。
关于java - 使用 Swig Controller 将数组从 C++ 传递到 Java,up 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42393651/