在Java和C之间转换++;作为包装类的参数传递时的类实例

在Java和C之间转换++;作为包装类的参数传递时的类实例,java,c++,swig,Java,C++,Swig,给定一个任意的Java类,比如 public class Coord { public double x; public double y; public double z; public Coord(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } } 和任意C++类(或结构),比如C++ 11 STL类 STD::数组< /C> >,< /P

给定一个任意的Java类,比如

public class Coord {
    public double x;
    public double y;
    public double z;

    public Coord(double x, double y, double z) {
         this.x = x; this.y = y; this.z = z;
    }
}

和任意C++类(或结构),比如C++ 11 STL类<代码> STD::数组< /C> >,< /P>

和使用SWIG封装生成C++ java类的C++类,例如

class Object {
    move_center(std::array<double, 3> vec) {
        // C++ code
    }
}
并让包装器构造一个
std::array
,其值
vec[0]=1.5

我知道在C++中可以定义一个代码> COORD < /Cuff>类,让它在爪哇中使用它,使用代理<代码> COORD < /C> >,但是有两个原因不做:

    C++库接口必须改变
  • 使用Java
    Coord
    类的部分Java代码现在必须使用代理,这会降低速度(或者Java代码必须处理两个Coord类,一个本地代理,并始终在这两个类之间转换)
我可以想象这样做的方式是使用
I
-文件和
typemap

%typemap(jstype) std::array<double, 3> "com.foo.bar.Coord"
%typemap(jstype)std::数组“com.foo.bar.Coord”

但是从那以后,我真的不知道该去哪里。

对此有很多考虑因素,还有几种可能的解决方法,所以我将按照我认为合理的顺序来研究其中的一些

我首先创建了一个测试头文件,在我的演示中使用,这对您展示的内容进行了一些小的调整:

#包括
结构对象{
无效移动中心(标准::数组向量){
//C++代码
}
};
首先,如果您使用的是SWIG的最新版本(肯定比3.0.2更新,但不确定具体是哪个版本),那么您将拥有一些库支持,可以作为起点使用
std::array

%模块测试
%{
#包括“test.hh”
%}
%包括
%模板(Vec3)std::数组;
%包括“test.hh”
作为一个起点,这就足够了,您可以得到一个可用的接口,该接口的类型为
Vec3
,它是
std::array
的一种可用包装形式

显然,尽管使用了
Coord
,但这实际上并不满足您的要求,因此我们想编写一些类型映射,以便在函数调用时在Java/C++类型之间进行转换。实际上,你可以在几个地方这样做。最简单的方法是将其编写为javain类型映射:

%模块测试
%{
#包括“test.hh”
%}
%包括
%模板(Vec3)std::数组;
%类型映射(jstype)std::数组“坐标”
%类型映射(javain,pre=“Vec3 temp$javainput=new Vec3();\n”
临时$javainput.set(0,$javainput.x);\n
临时$javainput.set(1,$javainput.y);\n
“temp$javainput.set(2$javainput.z);”,
pgcppname=“temp$javainput”)std::array,const std::array&“$javaclassname.getCPtr(temp$javainput)”
%包括“test.hh”
基本上,在您向我们展示的类型映射之上,所有这些都是在生成的Java函数调用中插入代码。该代码只读取x、y、z,并将它们放入一个临时的
Vec3
,该临时的
Vec3是专门为通话期间创建的

(如果您想添加一个JavaOUT类型映射,用于从C++函数返回这些变量,另一个变体支持支持非const引用的POST属性,请求细节)

您会注意到,虽然它满足您要求的功能要求,但另一个目标是避免过多的跨语言函数调用,但这里我们有4个额外的函数,包括一个内存分配

所以为了解决这个问题,我们可以开始让我们的界面变得更智能一些。SWIG的库arrays\u java.i中有一些数组的助手代码(很久以前)。我们可以使用它来在一个调用中构造临时的C++对象,使用<代码> %Exp>/Cuff>添加一个新的构造函数,它以<代码>双[3 ] < /代码>作为输入:

%module test

%{
#include "test.hh"
#include <algorithm>
%}

%include <std_array.i>
%include <arrays_java.i>

%template(Vec3) std::array<double, 3>;

%extend std::array<double, 3> {
  std::array<double, 3>(double in[3]) {
    std::array<double, 3> temp;
    std::copy_n(in, 3, std::begin(temp));
    return new std::array<double, 3>(temp);
  }
}

%typemap(jstype) std::array<double, 3> "Coord"
%typemap(javain,pre="    Vec3 temp$javainput = new Vec3(new double[]{$javainput.x, $javainput.y, $javainput.z});",
         pgcppname="temp$javainput") std::array<double, 3>, const std::array<double, 3>& "$javaclassname.getCPtr(temp$javainput)"

%include "test.hh"
但是请注意,我们现在已经放弃了对SWIG库中std_array.i的要求,并且只依赖于arrays_java.i。虽然这实际上不起作用(这里的
%apply
无效)

这其实不是什么大问题,我也没有花太多时间,因为我们可以通过编写arrays\u java自己提供的JNI调用来解决这个问题:

%module test

%{
#include "test.hh"
#include <algorithm>
%}


%typemap(jstype) std::array<double, 3> "Coord"
%typemap(javain) std::array<double, 3>, const std::array<double, 3>& "new double[]{$javainput.x, $javainput.y, $javainput.z}"
%typemap(jtype) std::array<double, 3> "double[]"
%typemap(jni) std::array<double, 3> "jdoubleArray"
%typemap(in) std::array<double, 3> {
  if (!$input || JCALL1(GetArrayLength, jenv, $input) != 3) {
    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "incorrect array size");
    return $null;
  }
  double *arr = JCALL2(GetDoubleArrayElements, jenv, $input, NULL);
  std::copy_n(arr, 3, $1.begin());
  JCALL3(ReleaseDoubleArrayElements, jenv, $input, arr, JNI_ABORT);
}

%include "test.hh"
%模块测试
%{
#包括“test.hh”
#包括
%}
%类型映射(jstype)std::数组“坐标”
%typemap(javain)std::array,const std::array&“新的双精度[]{$javainput.x,$javainput.y,$javainput.z}”
%typemap(jtype)std::数组“double[]”
%类型映射(jni)std::数组“jdoubleArray”
%类型映射(in)std::数组{
如果(!$input | | JCALL1(GetArrayLength,jenv,$input)!=3){
SWIG_JavaThrowException(jenv,SWIG_JavaIndexOutOfBoundsException,“数组大小不正确”);
返回$null;
}
double*arr=JCALL2(GetDoubleArrayElements,jenv,$input,NULL);
复制(arr,3,$1.begin());
JCALL3(ReleaseDoubleArrayElements,jenv,$input,arr,JNI_ABORT);
}
%包括“test.hh”
这已经开始尽可能地接近我们的目标,尽可能减少开销包装。我们编写了足够的JNI,允许我们一次性将整个
double[]
数组复制到
std::array
中。(C++编译器应该很好地优化复制操作)。我们仍然在java中分配一个3倍的临时数组,这在方法中是不可避免的,因为我们没有办法增加C++传递的参数数量,所以只有减少这个数目。 如果您想使用参数化类型映射,可以支持对修改输入的函数的非常量传递引用。您需要使用
GetDoubleArrayElements
调用的最后一个参数来查看它是否是副本,并将取消映射保存到argout typemap,然后在此时创建自己的副本

作为一种完全替代的方法,我们可以选择将
Coord
对象作为jobject一路传递到in-typemap,并在那里进行3次JNI调用,以获取此时的x、y和z成员变量值。每
%module test

%{
#include "test.hh"
#include <algorithm>
%}

%include <arrays_java.i>

%apply double[3] { std::array<double, 3> };
%typemap(jstype) std::array<double, 3> "Coord"
%typemap(javain) std::array<double, 3>, const std::array<double, 3>& "new double[]{$javainput.x, $javainput.y, $javainput.z}"

%typemap(in) std::array<double, 3> //....

%include "test.hh"
%module test

%{
#include "test.hh"
#include <algorithm>
%}


%typemap(jstype) std::array<double, 3> "Coord"
%typemap(javain) std::array<double, 3>, const std::array<double, 3>& "new double[]{$javainput.x, $javainput.y, $javainput.z}"
%typemap(jtype) std::array<double, 3> "double[]"
%typemap(jni) std::array<double, 3> "jdoubleArray"
%typemap(in) std::array<double, 3> {
  if (!$input || JCALL1(GetArrayLength, jenv, $input) != 3) {
    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "incorrect array size");
    return $null;
  }
  double *arr = JCALL2(GetDoubleArrayElements, jenv, $input, NULL);
  std::copy_n(arr, 3, $1.begin());
  JCALL3(ReleaseDoubleArrayElements, jenv, $input, arr, JNI_ABORT);
}

%include "test.hh"