草庐IT

java - Swig 类型映射将变量地址作为参数传递?

coder 2024-02-11 原文

我为以下 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++“通过非常量引用返回”函数,有很多选项可供选择。简而言之,您可以执行以下操作之一:

  1. 使用 %extend 提供一个返回的重载。 (可选择隐藏原件)
  2. 使用类型映射将其转换为返回值(并可选择将其映射到
  3. 更多地使用 SWIGTYPE_p_int。 (可选择在 Java 内部构建重载)
  4. 使用现有的 SWIG OUTPUT 类型映射通过数组获得按引用语义传递
  5. 使用结构体来获得按引用传递的语义

我将在此处展示每个选项的示例。


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=0in 类型映射来设置一个我们可以在调用发生时使用的临时变量 (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 用户隐藏此实现细节。

有关以上几个示例中提出的异常点的更多信息,请参阅 here , 或 here .

关于java - Swig 类型映射将变量地址作为参数传递?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44568507/

有关java - Swig 类型映射将变量地址作为参数传递?的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  3. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  4. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  5. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  6. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  7. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  8. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  9. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  10. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

随机推荐