SWIG 和 shared_ptr:javaout 类型映射未应用于地图模板

SWIG and shared_ptr: javaout typemap not applied to map template

问题

我正在开发一个 Android 应用程序,其中我必须在 Java 代码和 C++ 代码之间传递一个 OpenCV Mat。为此,我创建了以下工作正常的 SWIG 类型映射:

%include "std_map.i"
%include "std_shared_ptr.i"
...
%shared_ptr(cv::Mat)
...
// normal typemaps for cv::Mat w/o shared_ptr
%typemap(jstype) cv::Mat, cv::Mat& "org.opencv.core.Mat"
%typemap(javain) cv::Mat, cv::Mat& "$javainput.getNativeObjAddr()"
%typemap(jtype) cv::Mat, cv::Mat& "long"
%typemap(jni) cv::Mat, cv::Mat& "jlong" 

%typemap(in) cv::Mat, cv::Mat& {
     = *(cv::Mat **)&$input;
}
%typemap(javaout) cv::Mat, cv::Mat& {
    return new org.opencv.core.Mat($jnicall);
}

// rather hacky javaout typemap override for the shared_ptr
%typemap(javaout) std::shared_ptr< cv::Mat > {
    long cPtr = $jnicall;
    return (cPtr == 0) ? null : new org.opencv.core.Mat(cPtr);
}

无论如何,在某些时候我必须return一个std::map<std::string, std::shared_ptr<cv::Mat>>到Java。我使用带有

的地图模板完成了此操作
%template(Map_String_Shared_ptr_Mat) std::map<std::string, std::shared_ptr<cv::Mat>>;

在 Java 中生成以下 get 方法:

public org.opencv.core.Mat get(String key) {
    long cPtr = xyJNI.Map_String_Shared_ptr_Mat_get(swigCPtr, this, key);
    return (cPtr == 0) ? null : new org.opencv.core.Mat(cPtr, true);

}

它没有使用之前提供的 javaout 类型映射。 (它在函数 returning a std::shared_ptr<cv::Mat> 但不在地图模板中起作用)

到目前为止我尝试了什么

我试图通过

插入我自己的get方法
%typemap(javacode) std::map<std::string, std::shared_ptr<cv::Mat>> %{
   public org.opencv.Mat get{
      ...
   }
%};

但这会导致冲突,因为存在两个 get 方法。

此外,当我首先尝试 %ignore get 方法时,没有创建相应的 xyJNI.Map_String_Shared_ptr_Mat_get(swigCPtr, this, key);,因此我无法提供我自己的 get

问题

现在我需要一种方法来告诉 SWIG 应用该 javaout 类型映射。但是我也可以通过覆盖 get 方法主体的方法来为 Mat 使用正确的构造函数。

希望有人能帮我解决这个问题

注意:我不关心 Map_String_Shared_ptr_Mat 不是 Java 中的真实 Map。这对我来说不是问题

编辑: 添加了 shared_ptr javaout typemap

不幸的是,我无法找到将 javaout 类型映射应用于 Mat_String_Shared_ptr_Mat.get() 或替换 Mat_String_Shared_ptr_Mat.get().[=30= 的方法内容的解决方案]

我最终得到的解决方法是 returns 在 getPlain() 方法中引用 cv::Mat(所以不是 shared_ptr),类型映射是应用。

这当然不是我正在寻找的完美解决方案,但它可能会帮助其他面临类似问题的人。


解决方案

%include "std_map.i"

// 1.
%typemap(javaout) cv::Mat, cv::Mat& {
    return new org.opencv.core.Mat($jnicall);
}

...

// 2.
%ignore std::map<std::string, std::shared_ptr<cv::Mat>>::get;

// 3.
%template(Map_String_Shared_ptr_Mat) std::map<std::string, std::shared_ptr<cv::Mat>>;

// 4.
%extend std::map<std::string, std::shared_ptr<cv::Mat>>{
    const cv::Mat& getPlain(const std::string& key) throw (std::out_of_range) {
        std::map<std::string,std::shared_ptr< cv::Mat > >::iterator i = self->find(key);
        if (i != self->end())
            return *i->second;
        else
            throw std::out_of_range("key not found");
    }
}
  1. cv::Mat 的类型映射(与 shared_ptr 类型映射不同)应用于包装映射
  2. 忽略get函数(第4步会自行实现)
  3. 让 SWIG 通过 std_map.i
  4. 中定义的模板创建包装器
  5. 通过向地图添加 getPlain 函数来扩展包装器。此函数 returns 引用 cv::Mat 对象本身,而不是 shared_ptr。创建 Java 代码时应用步骤 1 的 javaout 类型映射

输出

在 运行 SWIG 之后,Java class Map_String_Shared_ptr_Mat 包含所需的 getPlain 方法:

public org.opencv.core.Mat getPlain(String key) {
     return new org.opencv.core.Mat(xyJNI.Map_String_Shared_ptr_Mat_getPlain(swigCPtr, this, key));
}

更新

注意:此部分特定于cv::Mat

由于 Java 中 cv::Mat 的实现,我不得不更改实现以在 C++ 中执行按引用调用。

这是因为 Java Mat 实现存储了指向 C++ Mat 的指针。在 finalize 方法中,C++ 对象被释放。在之前的实现中,对象可能已经被删除,这将导致 SIGSEGV 崩溃。

因此将实施更改为以下内容:

%extend std::map<std::string, std::shared_ptr<cv::Mat>>{
    void getPlain(const std::string& key, cv::Mat& dest) throw (std::out_of_range) {
        std::map<std::string,std::shared_ptr< cv::Mat > >::iterator i = self->find(key);
        if (i != self->end()){
             (*i->second).copyTo(dest);
        } else{
            throw std::out_of_range("key not found");
        }
    }

将数据复制到第二个参数中的 Mat,它本身是在 Java 中创建的(因此不会产生已释放的段错误)。


解决方案可以调整为 create/wrap 分别实现 set 和其他功能。

注意:我也最终使用了post中介绍的一些方法:Passing Java Map<String, String> to C++ method using SWIG,绝对值得一看。