Passing Java Map to C++ method using SWIG
我有一个用C ++定义的方法:
1 2 3 4 | std::map<std::string, std::string> validate( std::map<std::string, std::string> key, std::map<std::string, std::string> value ); |
我想在Java中使用此方法。 所以,我必须使用Swig编写一个包装器,通过它我可以将
请告诉我如何为swig定义.i文件以使其正常工作。
为了做到这一点,你需要告诉SWIG使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | %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>); |
支持从
我的最终版本中有相当多的活动部分。我将在这里完整介绍,附带注释说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | %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 <<" "; } return std::map<std::string, std::string>(in); } %} |
内存管理在这里免费发生,因为它仍归C ++代码所有。 (所以你仍然需要决定如何管理C ++容器的内存,但这并不是什么新鲜事)。由于返回到Java的对象只是C ++映射的包装器,因此容器的元素不必比它更长。在这里它们也是
最后我编写了以下Java来测试它:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
我编译并运行为:
1 2 3 4 5 6 7 | 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(假设您的函数声明可以在头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import com.googlecode.javacpp.*; import com.googlecode.javacpp.annotation.*; @Platform(include={"<string>","<map>","MapTest.h"}) public class MapTest { static { Loader.load(); } @Name("std::map<std::string, std::string>") public static class StringStringMap extends Pointer { static { Loader.load(); } public StringStringMap() { allocate(); } public StringStringMap(Pointer p) { super(p); } private native void allocate(); @Index @ByRef public native String get(String x); public native StringStringMap put(String x, String y); } public static native @ByVal StringStringMap validate( @ByVal StringStringMap key, @ByVal StringStringMap value); public static void main(String[] args) { StringStringMap m = new StringStringMap(); m.put("foo","bar"); System.out.println(m.get("foo")); } } |
我发现这比SWIG更容易,更清晰......