Pass Java map to C method using swig

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
                                   );

I want to use this method in Java Therefore, I have to write a wrapper using swig, through which I can pass Java map as STL map to C method

Please tell me how to define swig I file to make it work properly

Solution

To do this, you need to tell swig to use Java util. Map as input parameter, use% typemap (jstype) You also need to provide some code to convert from Java map type to C STD:: map type, and swig will inject at the appropriate point I've compiled 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>);

The pgcppname section ensures that the STD:: map we pass in does not collect garbage prematurely For more details on how it works, see this example in the swig documentation

It takes more work to support returning from STD:: map from C to Java, but this is possible java. util. Map is an interface, so we need to adjust the default wrapper of STD:: map to meet the interface In practice, use Java util. Abstractmap is easier to and inherit, although I eventually covered most of the functionality The whole solution is similar to my answer for STD:: vector

There are quite a few active parts in my final version I'll give you a complete introduction here with 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> %{
  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 does not mean implementing any interface / abstract class We need to rename some exposed content to do this. > Since we use type to implement Map (through abstractmap), the final conversion from maptype is stupid – > maptype, which is actually just a copy operation The convertmap method now checks this as an optimization. > Entryset is the main requirement of abstractmap We have defined (later) maptypeentry to implement Map for us Entry interface This will use more code later in% extend to effectively enumerate all keys as arrays Please note that this is not thread safe. If we change the map and this enumeration is in progress, a strange error will occur and may not be detected. > Remove is one of the methods we must implement to become variable Both remove and put must return old values, so there are some additional Java implementations, because C map will not do this. > Even size () is not compatible because long / int conversion is required Really, we should detect a very large loss of map accuracy and do something rational for overflow. > I'm bored typing Java anywhere util. Map, which makes the generated swig code need to be imported. > This sets maptype to inherit from abstractmap so that we can proxy and meet the requirements of Java mapping instead of performing additional copies for conversion. > The C definition of the class that will be our entry It's just a key, and then a pointer to the map it owns This value is not stored in the entry object itself and is always returned to the underlying mapping This type is also immutable. We can't change the map or key we own. > This is what swig sees We provide an additional get / setValue function to call back the map it originates from There is no public pointer to the owning map, because we don't need to do so, it's actually just an implementation detail. > java. util. Map. Entry< String,String>. > This is a trick to automatically fill in the jenv parameters of some internal code of% extend. We need to make some JNI calls in this code. >% These two methods in extensions put all keys and values into the output array respectively When passed in, the size of the array should be correct There is an assertion to verify this, but in fact it should be an exception Both are internal implementation details and may be private They are used by all functions that require bulk access to keys / values. > Foo actually performs integrity checks on my code

Memory management is free here because it is still owned by C code So you still need to decide how to manage the memory of C container, but this is not new Since the object returned to Java is only a wrapper for C mapping, the elements of the container do not have to be longer than it Here, they are also special strings, which are returned as new objects if they use swig's STD:: shared_ PTR supports smart pointers, then everything will work as expected The only tricky situation is the pointer to the object In this case, it is the java programmer's responsibility to keep the mapping and its contents alive, at least as long as any Java proxy returned

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);
  }
}

I compiled and ran as:

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}
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>