我为以下c ++代码创建了JNI包装器。
add.h
class MyClass
{
public:
int add(int x, int y, int &z);
int sub(int x, int y);
};
上面提到的代码是.h文件
add.cpp
int MyClass::add(int x, int y, int &sum)
{
sum=x+y;
return 0;
}
int MyClass::sub(int x, int y)
{
return x - y;
}
swig.i
%module algo
%{
#define SWIG_FILE_WITH_INIT
#include "add.h"
%}
%include "arrays_java.i"
%include "typemaps.i"
%include "add.h"
对于上面提到的.cpp文件,我需要生成JNI包装器并在Java代码上使用它。当我尝试执行swig命令时,我将获得SWIGTYPE_p_int.java文件以及JNI文件。任何人都可以帮我解决这个问题吗?
答案 0 :(得分:5)
要包装C ++“通过非const引用返回”函数,有很多选项可以打开。简而言之,您可以执行以下操作之一:
%extend
提供返回的重载。 (可选择隐藏原文)SWIGTYPE_p_int
。 (可选择在Java内部构建一个重载)OUTPUT
类型映射来获取带数组的引用语义我将在此处展示每个选项的示例。
%extend
提供重载这里的基本想法是,我们将在这里编写一个全新的重载,SWIG包含一些简单的内容。使用下面的%extend
语法,我们可以创建一个新的,仅包装器的重载,它使用临时值存储值,然后如果一切顺利则返回它。
%module algo
%{
#define SWIG_FILE_WITH_INIT
#include "add.h"
#include <exception>
%}
%extend MyClass {
int add(int x, int y) {
int z;
const int result = $self->add(x,y,z);
if (0 == result) return z;
throw std::exception();
// TODO: use SWIG's exception support
}
}
%ignore MyClass::add(int,int,int&); // Optional: hide the original overload
%include "add.h"
由于原始返回的int似乎表明函数本身的成功/失败,我们可以更自然地将它映射到Java中的异常。 (这里省略了详细信息,请参阅我在XXX的回答了解更多信息)
此解决方案的效果与前一个类似,但以不同的方式实现。在这里,我们使用in
typemap与numinputs=0
来设置一个临时变量,我们可以在调用发生时使用它(tmpz
)。 out typemap然后只检查来自真实函数调用的返回代码,argout typemap将tempoary复制到结果中。我包含了比这里真正需要的更多的类型图,因为偶然的z
和现有的函数返回是相同的类型,但如果不是这样的话我们就需要它们。
%module algo
%{
#define SWIG_FILE_WITH_INIT
#include "add.h"
#include <exception>
%}
// These aren't actually needed here because the fuction already returns in
%typemap(jni) int MyClass::add "jint";
%typemap(jtype) int MyClass::add "int";
%typemap(jstype) int MyClass::add "int";
%typemap(javaout) int MyClass::add {
return $jnicall;
}
// These create a temporary and map it to the return value for us
%typemap(argout) int& z {
$result = (jint)tmpz$argnum;
}
%typemap(out) int MyClass::add {
if ($1 != 0) {
throw std::exception();
// TODO: exceptions as in other examples
}
}
%typemap(in,numinputs=0) int& z (int tmpz) {
$1 = &tmpz;
}
%include "add.h"
在这里,我们将使用SWIG的cpointer.i
库来帮助我们使用SWIGTYPE_p_int
对象,并为Java用户透明地执行此操作。为此,我们使用javacode类型映射为z
创建一个临时变量,然后将其传递给原始的SWIG生成函数(可以将其设置为私有以隐藏它)。与其他示例一样,我们可以通过抛出异常来处理返回值指示错误的情况,这次虽然已经是Java代码正在执行抛出,但稍微简单一点。
%module algo
%{
#define SWIG_FILE_WITH_INIT
#include "add.h"
%}
%include "cpointer.i"
%pointer_class(int,IntPointer);
%typemap(javacode) MyClass %{
public int add(int x, int y) {
IntPointer z = new IntPointer();
int ret = this.add(x,y,z.cast());
if (ret != 0) throw new Exception();
return z.value();
}
%}
%javamethodmodifiers MyClass::add(int,int,int&) "private" // Optional
%include "add.h"
OUTPUT
类型地图这在功能上与之前的解决方案非常相似,改变了不使用SWIG提供的辅助类型来处理int指针,而是使用数组并利用Java的“数组传递引用”语义来实现同样的结果。
%module algo
%{
#define SWIG_FILE_WITH_INIT
#include "add.h"
%}
%include <typemaps.i>
%apply int *OUTPUT { int & z };
%typemap(javacode) MyClass %{
public int add(int x, int y) {
int[] z = new int[1];
int ret = this.add(x,y,z);
if (ret != 0) throw new Exception();
return z[0];
}
%}
%javamethodmodifiers MyClass::add(int,int,int&) "private"
%include "add.h"
您可以使用%inline
添加其他类型。然后欺骗SWIG使用它而不是int&
引用。如果我们允许隐式转换,这很好。
%module algo
%{
#define SWIG_FILE_WITH_INIT
#include "add.h"
%}
%inline %{
struct IntWrapper {
int value;
// Enable transparent conversion for the SWIG argument
operator int&() {
return value;
}
};
%}
class MyClass
{
public:
int add(int x, int y, IntWrapper& z); // lie to SWIG, but we'll make it up later with implict conversion
int sub(int x, int y);
};
//%include "add.h" // Don't do this now because we need the lie above
与前面的示例一样,我们可以选择使用重载和方法修饰符用法从Java用户隐藏此实现细节。