传递Java Map< String,String>到使用SWIG的C ++方法 [英] Passing Java Map<String, String> to C++ method using SWIG

查看:211
本文介绍了传递Java Map< String,String>到使用SWIG的C ++方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个在C ++中定义的方法:

I have a method defined in C++:

std::map<std::string, std::string> validate(
                                   std::map<std::string, std::string> key, 
                                   std::map<std::string, std::string> value
                                   );

我想在Java中使用这个方法。所以,我必须使用Swig写一个包装,通过它我可以传递 Java Map 作为STL map 到c ++方法。

I want to consume this method in Java. So, I have to write a wrapper using Swig through which I will be able to pass Java Map as STL map to the c++ method.

请让我知道我应该如何定义swig的.i档案来使这项工作。

Please let me know how should I define the .i file for swig to make this work.

推荐答案

为了这样做,你需要告诉SWIG使用 java.util.Map 作为输入参数,使用%typemap(jstype)。你还需要提供一些代码来从Java映射类型转换为C ++ std :: map 类型,SWIG将在适当的点注入。我编写了一个小的(编译但未经测试的)示例来说明这一点:

In order to do this you'll need to tell SWIG to use java.util.Map for the input argument, using %typemap(jstype). You'll also need to supply some code to convert from the Java map type to the C++ std::map type, which SWIG will inject at appropriate points. I've put together a small (compiled, but untested) example to illustrate this:

%module test

%include <std_map.i>
%include <std_string.i>

%typemap(jstype) std::map<std::string, std::string> "java.util.Map<String,String>"
%typemap(javain,pre="    MapType temp$javainput = $javaclassname.convertMap($javainput);",pgcppname="temp$javainput") std::map<std::string, std::string> "$javaclassname.getCPtr(temp$javainput)"
%typemap(javacode) std::map<std::string, std::string> %{
  static $javaclassname convertMap(java.util.Map<String,String> in) {
    $javaclassname out = new $javaclassname();
    for (java.util.Map.Entry<String, String> entry : in.entrySet()) {
      out.set(entry.getKey(), entry.getValue());      
    }
    return out;
  }    
%}

%template(MapType) std::map<std::string, std::string>;

void foo(std::map<std::string, std::string>);

pgcppname 部分是确保 std :: map 我们传入不会过早收集垃圾收集。有关如何工作的详细信息,请参阅SWIG文档中的此示例

The pgcppname part is making sure that the std::map we pass in doesn't get garbage collected too early. See this example in the SWIG documentation for more details on how that works.

要支持从 std :: map 返回从C ++到Java需要相当多的工作, 。 java.util.Map 是一个接口,因此我们需要修改 std :: map 的默认包装接口。在实践中,更容易使用 java.util.AbstractMap 并继承,虽然我最终重写了大部分的功能,反正。这整个解决方案类似于我的回答为 std :: vector

To support returning from std::map from C++ to Java takes quite a bit more work, but is possible. java.util.Map is an interface so we need to adapt the default wrapping of std::map to meet that interface. In practice it's easier to use java.util.AbstractMap and inherit from that although I ended up overriding most of the functions in that anyway. This whole solution is analogous to my answer for std::vector.

零件在我的最终版本。我将在这里完整地显示它,并注释注释:

There are quite a few moving parts in my final version. I'll present it complete here, with annotated notes:

%module test
%{
#include <cassert>
#include <iostream>
%}

%include <std_map.i>

// 1.
%rename (size_impl) std::map<std::string,std::string>::size;
%rename (isEmpty) std::map<std::string,std::string>::empty;
%include <std_string.i>

%typemap(jstype) std::map<std::string, std::string> "java.util.Map<String,String>"
%typemap(javain,pre="    MapType temp$javainput = $javaclassname.convertMap($javainput);",pgcppname="temp$javainput") std::map<std::string, std::string> "$javaclassname.getCPtr(temp$javainput)"
%typemap(javacode) std::map<std::string, std::string> %{
  static $javaclassname convertMap(Map<String,String> in) {
    // 2.
    if (in instanceof $javaclassname) {
      return ($javaclassname)in;
    }

    $javaclassname out = new $javaclassname();
    for (Map.Entry<String, String> entry : in.entrySet()) {
      out.set(entry.getKey(), entry.getValue());
    }
    return out;
  }

  // 3.
  public Set<Map.Entry<String,String>> entrySet() {
    HashSet<Map.Entry<String,String>> ret = new HashSet<Map.Entry<String,String>>(size());
    String array[] = new String[size()];
    all_keys(array);
    for (String key: array) {
      ret.add(new MapTypeEntry(key,this));
    }
    return ret;
  }

  public Collection<String> values() {
    String array[] = new String[size()];
    all_values(array);
    return new ArrayList<String>(Arrays.asList(array));
  }

  public Set<String> keySet() {
    String array[] = new String[size()];
    all_keys(array);
    return new HashSet<String>(Arrays.asList(array));
  }

  // 4.
  public String remove(Object key) {
    final String ret = get(key);
    remove((String)key);
    return ret;
  }

  public String put(String key, String value) {
    final String ret = has_key(key) ? get(key) : null;
    set(key, value);
    return ret;
  }

  // 5.
  public int size() {
    return (int)size_impl();
  }
%}

// 6.
%typemap(javaimports) std::map<std::string, std::string> "import java.util.*;";
// 7.
%typemap(javabase) std::map<std::string, std::string> "AbstractMap<String, String>";

// 8.
%{
template <typename K, typename V>
struct map_entry {
  const K key;
  map_entry(const K& key, std::map<K,V> *owner) : key(key), m(owner) {
  }
  std::map<K,V> * const m;
};
%}

// 9.
template <typename K, typename V>
struct map_entry {
  const K key;
  %extend {
    V getValue() const {
      return (*$self->m)[$self->key];
    }

    V setValue(const V& n) const {
      const V old = (*$self->m)[$self->key];
      (*$self->m)[$self->key] = n;
      return old;
    }
  }
  map_entry(const K& key, std::map<K,V> *owner);
};

// 10.
%typemap(javainterfaces) map_entry<std::string, std::string> "java.util.Map.Entry<String,String>";
// 11.
%typemap(in,numinputs=0) JNIEnv * %{
  $1 = jenv;
%}

// 12.
%extend std::map<std::string, std::string> {
  void all_values(jobjectArray values, JNIEnv *jenv) const {
    assert((jsize)$self->size() == jenv->GetArrayLength(values));
    jsize pos = 0;
    for (std::map<std::string, std::string>::const_iterator it = $self->begin();
         it != $self->end();
         ++it) {
       jenv->SetObjectArrayElement(values, pos++, jenv->NewStringUTF(it->second.c_str()));
    }
  }

  void all_keys(jobjectArray keys, JNIEnv *jenv) const {
    assert((jsize)$self->size() == jenv->GetArrayLength(keys));
    jsize pos = 0;
    for (std::map<std::string, std::string>::const_iterator it = $self->begin();
         it != $self->end();
         ++it) {
       jenv->SetObjectArrayElement(keys, pos++, jenv->NewStringUTF(it->first.c_str()));
    }
  }
}

%template(MapType) std::map<std::string, std::string>;
%template(MapTypeEntry) map_entry<std::string, std::string>;

// 13.
%inline %{
  std::map<std::string, std::string> foo(std::map<std::string, std::string> in) {
    for (std::map<std::string, std::string>::const_iterator it = in.begin();
         it != in.end(); ++it) {
      std::cout << it->first << ": " << it->second << "\n";
    }

    return std::map<std::string, std::string>(in);
  }
%}




  1. std_map.i并不意味着实现任何接口/抽象类。

  2. 因为我们使我们的类型实现 Map (通过 AbstractMap ),最终转换 MapType - > MapType 当它只是一个复制操作。 convertMap 方法现在检查此案例为优化。

  3. EntrySet AbstractMap 的主要要求。我们已定义(稍后) MapTypeEntry 为我们实现 Map.Entry 接口。这在稍后使用%extend 中的一些更多的代码有效地枚举所有的键作为数组。注意,这不是线程安全的,如果我们改变地图,而这枚举正在进行奇怪的事情会发生,可能不会被检测到。

  4. remove 是我们必须实现以便可变的方法之一。 remove put 都必须返回旧的值,所以这里有一些额外的Java

  5. 即使 size()不兼容,因为需要long / int转换。

  6. 我很无聊在输入 java.util.Map c $ c>无处不在,因此这使得生成的SWIG代码具有所需的导入。

  7. 这将设置 MapType code> AbstractMap ,以便我们代理并满足Java地图的要求,而不是进行额外的副本转换。

  8. C ++定义的类,将作为我们的条目。这只是一个键,然后指向它拥有的地图的指针。该值不存储在 Entry 对象本身中,并始终返回到底层映射。这种类型是不可变的,我们不能改变拥有地图或密钥。

  9. 这是SWIG看到的。我们提供了一个额外的get / setValue函数,调用它源自的地图。

  10. java.util.Map()不会显示指向拥有地图的指针,因为我们不需要这样做,而只是一个实现细节。 .Entry< String,String>

  11. 这是一个自动填充 jenv %extend 中的一些代码,我们需要在该代码中进行一些JNI调用。

  12. %extend 将所有键和值分别放入输出数组。数组在传入时应该是正确的大小。有一个断言验证这个,但真的应该是一个例外。这两个都是内部实现细节,可能应该是私有的。

  13. foo 的一个实际实现来检查我的代码。

  1. std_map.i isn't meant to implement any interface/abstract class. We need to rename some of what exposes in order to do so.
  2. Since we make our type implement Map (via AbstractMap), it's silly to end up converting from MapType -> MapType when that's literally just a copy operation. The convertMap method now checks for this case as an optimisation.
  3. EntrySet is the main requirement of AbstractMap. We have defined (later on) MapTypeEntry to implement the Map.Entry interface for us. This uses some more code inside %extend later on to efficiently get enumerate all the keys as an array. Note that this is not thread-safe, if we change the map whilst this enumeration is in progress weird bad things will happen and possibly not get detected.
  4. remove is one of the methods we have to implement in order to be mutable. Both remove and put have to return the old value, so there's a bit of extra Java here to make that happen since C++ maps don't do that.
  5. Even size() isn't compatible because of the long/int conversion that's needed. Really we should detect the loss of precision somewhere for very large maps and do something sane for overflows.
  6. I got bored of typing java.util.Map everywhere so this makes the generated SWIG code have the import needed.
  7. This sets up the MapType to inherit from AbstractMap, so that we proxy and meet the requirements of a Java map rather than doing an extra copy to convert back.
  8. The C++ definition of the class that will act as our entry. This just has a key and then a pointer to the map it is owned by. The value isn't stored in the Entry object itself and is always referred back to the underlying map for. This type is immutable too, we can't change the owning map or the key ever.
  9. This is what SWIG sees. We supply an extra get/setValue function that calls back to the map it originates from. The pointer to the owning map isn't exposed since we're not required to do so and it's really just an implementation detail.
  10. The java.util.Map.Entry<String,String>.
  11. This is a trick that auto-populates the jenv argument of some code inside %extend that we need to make some JNI calls inside that code.
  12. These two methods inside %extend place all of the keys and values respectively into an output array. The array is expected to be the right size when passed in. There's an assert to validate this, but really it should be an exception. Both of these are internal implementation details that probably ought to be private anyway. They get used by all of the functions that require bulk access to keys/values.
  13. An actual implementation of foo to sanity check my code.

内存管理在这里是免费的,因为它仍然属于C ++代码。 (所以你仍然必须决定如何管理C ++容器的内存,但这不是什么新鲜事)。由于返回到Java的对象只是一个C ++映射的包装器,容器的元素不必比它长。这里他们也是 Strings 这是特殊的,因为他们作为新对象返回,如果他们是智能指针使用SWIG的 std :: shared_ptr 支持,那么一切都会按预期工作。唯一的情况是棘手的是指针对象的映射。在这种情况下,Java程序员有责任保持地图及其内容至少与返回的任何Java代理一样长久。

Memory management happens for free here since it remains owned by the C++ code. (So you've still got to decide how to manage memory for a C++ container, but that's nothing new). Since the object that gets returned to Java is just a wrapper around a C++ map the elements of the container don't have to outlive it. Here they're also Strings which are special in that they get returned as new objects, if they were smart pointers using SWIG's std::shared_ptr support then everything would work as expected. The only case that would be tricky is maps of pointers to objects. In that case it's the responsibility of the Java programmer to keep the map and its contents alive at least as long as any Java proxies that get returned.

最后,我写了以下Java来测试它:

Finally I wrote the following Java to test it:

import java.util.Map;

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");

    Map<String,String> m = new MapType();
    m.put("key1", "value1");
    System.out.println(m);
    m = test.foo(m);
    System.out.println(m);
  }
}

这是我编译和运行的:

swig2.0 -Wall -java -c++ test.i
gcc -Wall -Wextra -shared -o libtest.so -I/usr/lib/jvm/default-java/include -I/usr/lib/jvm/default-java/include/linux test_wrap.cxx
javac run.java
LD_LIBRARY_PATH=. java run
{key1=value1}
key1: value1
{key1=value1}

这篇关于传递Java Map&lt; String,String&gt;到使用SWIG的C ++方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆