c++ - SWIG-Java memory management when returning references to member variables -
i accessing c++ code java using swig.
getters in c++ return const references:
class b { public: const a& geta() const { return a_; } private: a_; };
generate swig wrappers. b::geta
in java returns reference expected. yet jvm not know reference associated class b
. might lead broken reference:
public createa() { b b = new b(); return b.geta(); }
object returned createa
invalid once garbage collector destroys b b
, jvm knows nothing it.
swig provides documentation addressing issue: references , swig. yet means need add these references manually.
i came following solution returns copies instead of const references in swig generated code:
%typemap(out) const swigtype& { *($&1_ltype)&$result = new $1_basetype(($1_type)*$1); } %typemap(javaout) const swigtype& { return new $javaclassname($jnicall, true); }
i have 2 questions:
- is approach safe? did not forget something?
- is there better solution address issue , not write code?
any appreciated.
that looks sensible written wouldn't recommend using it.
as stands lose semantics of return const reference if force every case of become copy. impact swig wrapping of other things (e.g. standard library containers).
it has potential adversely impact performance of wrapped code compared native library. more though, fundamentally changes behaviour in cases too. consider example following swig example:
%module test %typemap(out) const swigtype& { *($&1_ltype)&$result = new $1_basetype(($1_type)*$1); } %typemap(javaout) const swigtype& { return new $javaclassname($jnicall, true); } %inline %{ struct { int v; a() : v(0) {} }; class b { public: b() { } const a& geta() const { return a_; } void counter() { a_.v++; } int test() const { return a_.v; } private: a_; }; %}
if use java like:
public class run { public static void main(string[] argv) { system.loadlibrary("test"); a = test(); system.out.println("a.v = " + a.getv()); } private static test() { b b = new b(); result = b.geta(); system.out.println("a.v = " + b.test() + " (test)"); b.counter(); system.out.println("a.v = " + result.getv()); return result; } }
the result v (unexpectedly) 0:
a.v = 0 (test) a.v = 0 a.v = 0
contrast same (correct) c++ beahviour:
#include <iostream> struct { int v; a() : v(0) {} }; class b { public: b() { } const a& geta() const { return a_; } void counter() { a_.v++; } int test() const { return a_.v; } private: a_; }; static test(); int main() { const a& = test(); std::cout << "a.v = " << a.v << "\n"; } static test() { b b; const a& result = b.geta(); std::cout << "a.v = " << b.test() << " (test)" << "\n"; b.counter(); std::cout << "a.v = " << result.v << "\n"; return result; }
which correctly returns:
a.v = 0 (test) a.v = 1 a.v = 1
that unnatural semantics show-stopper me.
my suggestion skip copy constructor plan , adapt example documentation generic:
%module test %typemap(javabody,noblock=1) swigtype { private long swigcptr; protected boolean swigcmemown; protected $javaclassname(long cptr, boolean cmemoryown) { swigcmemown = cmemoryown; swigcptr = cptr; } protected $javaclassname(long cptr, boolean cmemoryown, object cparent) { this(cptr, cmemoryown); swigcparent = cparent; } protected static long getcptr($javaclassname obj) { return (obj == null) ? 0 : obj.swigcptr; } private object swigcparent; } %typemap(javaout) swigtype const & { return new $javaclassname($jnicall, $owner, this); } %inline %{ struct { int v; a() : v(0) {} }; class b { public: b() { } const a& geta() const { return a_; } void counter() { a_.v++; } int test() const { return a_.v; } private: a_; }; %}
which behaves c++ equivalent 1 object reference gets inserted , managed automatically per class.
if want made work automatically both static , non-static methods need use few tricks because, unfortunately, there no way of specifying typemap should apply non-static methods. trick used explained in detail on question. trick can modify swig interface add:
%pragma(java) moduleimports=%{ import java.lang.reflect.field; %} %pragma(java) modulecode=%{ static object getthisornull(final object o, final class c) { (field f: o.getclass().getdeclaredfields()) { if (f.gettype().equals(c)) { try { return f.get(o); } catch (illegalaccessexception e) { // omm nom nom... } } } return null; } %}
and change javaout typemap be:
%typemap(javaout) swigtype const & { return new $javaclassname($jnicall, $owner, $module.getthisornull(new object(){}, $javaclassname.class)); }
the remainder of interface discussed, modification causes swigcparent
null
if there no parent (i.e. static method).
Comments
Post a Comment