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 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.
Please let me know how should I define the .i file for swig to make this work.
In order to do this you’ll need to tell SWIG to use
java.util.Mapfor 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::maptype, which SWIG will inject at appropriate points. I’ve put together a small (compiled, but untested) example to illustrate this:The
pgcppnamepart is making sure that thestd::mapwe pass in doesn’t get garbage collected too early. See this example in the SWIG documentation for more details on how that works.To support returning from
std::mapfrom C++ to Java takes quite a bit more work, but is possible.java.util.Mapis an interface so we need to adapt the default wrapping ofstd::mapto meet that interface. In practice it’s easier to usejava.util.AbstractMapand inherit from that although I ended up overriding most of the functions in that anyway. This whole solution is analogous to my answer forstd::vector.There are quite a few moving parts in my final version. I’ll present it complete here, with annotated notes:
Map(viaAbstractMap), it’s silly to end up converting fromMapType->MapTypewhen that’s literally just a copy operation. TheconvertMapmethod now checks for this case as an optimisation.EntrySetis the main requirement ofAbstractMap. We have defined (later on)MapTypeEntryto implement theMap.Entryinterface for us. This uses some more code inside%extendlater 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.removeis one of the methods we have to implement in order to be mutable. Bothremoveandputhave 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.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.java.util.Mapeverywhere so this makes the generated SWIG code have the import needed.MapTypeto inherit fromAbstractMap, so that we proxy and meet the requirements of a Java map rather than doing an extra copy to convert back.Entryobject 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.java.util.Map.Entry<String,String>.jenvargument of some code inside%extendthat we need to make some JNI calls inside that code.%extendplace 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.footo sanity check my code.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
Stringswhich are special in that they get returned as new objects, if they were smart pointers using SWIG’sstd::shared_ptrsupport 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.Finally I wrote the following Java to test it:
Which I compiled and ran as: