我为以下 C++ 代码创建了 JNI 包装器。
add.h
class MyClass
{
public:
int add(int x, int y, int &z);
int sub(int x, int y);
};
上面提到的代码就是.h文件
添加.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 文件。谁能帮我解决这个问题?
最佳答案
要包装 C++“通过非常量引用返回”函数,有很多选项可供选择。简而言之,您可以执行以下操作之一:
- 使用
%extend
提供一个返回的重载。 (可选择隐藏原件) - 使用类型映射将其转换为返回值(并可选择将其映射到
- 更多地使用
SWIGTYPE_p_int
。 (可选择在 Java 内部构建重载) - 使用现有的 SWIG
OUTPUT
类型映射通过数组获得按引用语义传递 - 使用结构体来获得按引用传递的语义
我将在此处展示每个选项的示例。
1。使用 %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的回答)
2。使用类型映射返回它
这个解决方案的效果和上一个类似,只是实现方式不同。在这里,我们使用带有 numinputs=0
的 in
类型映射来设置一个我们可以在调用发生时使用的临时变量 (tmpz
) .然后 out 类型映射只检查来自真实函数调用的返回代码,argout 类型映射将 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"
3。使用 SWIG 指针函数
在这里,我们将使用 SWIG 的 cpointer.i
库来帮助我们处理 SWIGTYPE_p_int
对象,并为我们的 Java 用户透明地执行此操作。为此,我们使用 javacode 类型映射为 z
创建了一个临时变量,然后将其传递给原始 SWIG 生成的函数(可以将其设为私有(private)以隐藏它)。与其他示例一样,我们可以通过抛出异常来处理返回值指示错误的情况,尽管这次已经是 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"
4。使用 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"
5.使用我们自己的结构进行引用传递
您可以使用 %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 用户隐藏此实现细节。
关于java - Swig 类型映射将变量地址作为参数传递?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44568507/