使用SWIG将Java Map传递给C方法
我有一个在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映射传递给c方法. 请告诉我如何为swig定义.i文件以使其正常工作. 解决方法
为了做到这一点,你需要告诉SWIG使用java.util.Map作为输入参数,使用%typemap(jstype).您还需要提供一些代码以从Java地图类型转换为C std :: map类型,SWIG将在适当的点注入.我已经整理了一个小的(编译但未经测试的)示例来说明这一点:
%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文档中的this example. 支持从std :: map从C返回到Java需要花费更多的工作,但这是可能的. java.util.Map是一个接口,所以我们需要调整std :: map的默认包装来满足该接口.在实践中,使用java.util.AbstractMap更容易并继承,尽管我最终还是覆盖了大部分功能.整个解决方案类似于my answer for 我的最终版本中有相当多的活动部分.我将在这里完整介绍,附带注释说明: %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> %{ static $javaclassname convertMap(Map<String,String> in) { // 2. if (in instanceof $javaclassname) { return ($javaclassname)in; } $javaclassname out = new $javaclassname(); for (Map.Entry<String,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,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,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); } %} > std_map.i并不意味着实现任何接口/抽象类.我们需要重命名一些暴露的内容才能这样做. 内存管理在这里免费发生,因为它仍归C代码所有. (所以你仍然需要决定如何管理C容器的内存,但这并不是什么新鲜事).由于返回到Java的对象只是C映射的包装器,因此容器的元素不必比它更长.在这里,它们也是特殊的字符串,它们作为新对象返回,如果它们是使用SWIG的std :: shared_ptr支持的智能指针,那么一切都会按预期工作.唯一棘手的情况是指向对象的指针.在这种情况下,Java程序员有责任使映射及其内容保持活动,至少与返回的任何Java代理一样长. 最后我编写了以下Java来测试它: 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} (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |