From 6b361bf05004181a118235df37c6c9bac099022a Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Fri, 8 Jul 2022 18:04:20 +0100 Subject: [PATCH 01/11] Add Java support for std::unique for input parameters. This works by transferring ownership of the underlying C++ memory from the target language proxy class to an instance of the unique_ptr which is passed to the wrapped function via std::move. The proxy class has a new swigRelease() method which sets the underlying C++ pointer for the proxy class to null, so working in much the same way as std::unique_ptr::release(). Any attempt at using the proxy class will be the same as if the delete() function has been called on the proxy class. That is, using a C++ null pointer, when a non-null pointer is usually expected. This commit relies on the previous commit that uses std::move on the temporary variable used for the wrapped function's input parameter as std::unique_ptr is not copyable, it has move-only semantics. --- Examples/test-suite/cpp11_std_unique_ptr.i | 21 +++++++++++- .../java/cpp11_std_unique_ptr_runme.java | 34 +++++++++++++++++++ Examples/test-suite/java_throws.i | 15 ++++++++ Lib/java/std_unique_ptr.i | 23 +++++++++++++ Source/Modules/java.cxx | 2 ++ 5 files changed, 94 insertions(+), 1 deletion(-) diff --git a/Examples/test-suite/cpp11_std_unique_ptr.i b/Examples/test-suite/cpp11_std_unique_ptr.i index 9bd65b97f..fd0e8e942 100644 --- a/Examples/test-suite/cpp11_std_unique_ptr.i +++ b/Examples/test-suite/cpp11_std_unique_ptr.i @@ -2,6 +2,7 @@ #if defined(SWIGCSHARP) || defined(SWIGJAVA) || defined(SWIGPYTHON) || defined(SWIGRUBY) || defined(SWIGPERL) +%include "std_string.i" %include "std_unique_ptr.i" %unique_ptr(Klass) @@ -22,7 +23,7 @@ public: const char* getLabel() const { return m_label.c_str(); } - ~Klass() + virtual ~Klass() { SwigExamples::Lock lock(critical_section); total_count--; @@ -44,6 +45,24 @@ int Klass::total_count = 0; %inline %{ +// Virtual inheritance used as this usually results in different values for Klass* and KlassInheritance* +// for testing class inheritance and unique_ptr +struct KlassInheritance : virtual Klass { + KlassInheritance(const char* label) : Klass(label) { + // std::cout << "ptrs.... " << std::hex << (Klass*)this << " " << (KlassInheritance*)this << std::endl; + } +}; + +#if defined(SWIGJAVA) +std::string takeKlassUniquePtr(std::unique_ptr k) { + return std::string(k->getLabel()); +} +#endif + +bool is_nullptr(Klass *p) { + return p == nullptr; +} + std::unique_ptr makeKlassUniquePtr(const char* label) { return std::unique_ptr(new Klass(label)); } diff --git a/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java b/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java index a734b0e5e..3f2b15f44 100644 --- a/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java +++ b/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java @@ -20,8 +20,42 @@ public class cpp11_std_unique_ptr_runme { } } + private static void checkCount(int expected_count) { + int actual_count = Klass.getTotal_count(); + if (actual_count != expected_count) + throw new RuntimeException("Counts incorrect, expected:" + expected_count + " actual:" + actual_count); + } + public static void main(String argv[]) throws Throwable { + // unique_ptr as input + { + Klass kin = new Klass("KlassInput"); + checkCount(1); + String s = cpp11_std_unique_ptr.takeKlassUniquePtr(kin); + checkCount(0); + if (!s.equals("KlassInput")) + throw new RuntimeException("Incorrect string: " + s); + if (!cpp11_std_unique_ptr.is_nullptr(kin)) + throw new RuntimeException("is_nullptr failed"); + kin.delete(); // Should not fail, even though already deleted + checkCount(0); + } + + { + KlassInheritance kini = new KlassInheritance("KlassInheritanceInput"); + checkCount(1); + String s = cpp11_std_unique_ptr.takeKlassUniquePtr(kini); + checkCount(0); + if (!s.equals("KlassInheritanceInput")) + throw new RuntimeException("Incorrect string: " + s); + if (!cpp11_std_unique_ptr.is_nullptr(kini)) + throw new RuntimeException("is_nullptr failed"); + kini.delete(); // Should not fail, even though already deleted + checkCount(0); + } + + // unique_ptr as output Klass k1 = cpp11_std_unique_ptr.makeKlassUniquePtr("first"); if (!k1.getLabel().equals("first")) throw new RuntimeException("wrong object label"); diff --git a/Examples/test-suite/java_throws.i b/Examples/test-suite/java_throws.i index 6cd47b448..940f2c192 100644 --- a/Examples/test-suite/java_throws.i +++ b/Examples/test-suite/java_throws.i @@ -192,6 +192,21 @@ try { } } %} +%typemap(javarelease) SWIGTYPE %{ + protected static long swigRelease($javaclassname obj) { + long ptr = 0; + if (obj != null) { + ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + try { + obj.delete(); + } catch (MyException e) { + throw new RuntimeException(e); + } + } + return ptr; + } +%} %inline %{ struct NoExceptTest { diff --git a/Lib/java/std_unique_ptr.i b/Lib/java/std_unique_ptr.i index 665d913ae..7a647f616 100644 --- a/Lib/java/std_unique_ptr.i +++ b/Lib/java/std_unique_ptr.i @@ -8,19 +8,42 @@ * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) + %typemap (jni) std::unique_ptr< TYPE > "jlong" %typemap (jtype) std::unique_ptr< TYPE > "long" %typemap (jstype) std::unique_ptr< TYPE > "$typemap(jstype, TYPE)" +%typemap(in) std::unique_ptr< TYPE > (TYPE *unique_temp) +%{ unique_temp = *(TYPE **)&$input; + $1.reset(unique_temp); %} + +%typemap(javain) std::unique_ptr< TYPE > "$typemap(jstype, TYPE).swigRelease($javainput)" + %typemap (out) std::unique_ptr< TYPE > %{ jlong lpp = 0; *(TYPE **) &lpp = $1.release(); $result = lpp; %} + %typemap(javaout) std::unique_ptr< TYPE > { long cPtr = $jnicall; return (cPtr == 0) ? null : new $typemap(jstype, TYPE)(cPtr, true); } + +%typemap(javarelease) TYPE %{ + protected static long swigRelease($javaclassname obj) { + long ptr = 0; + if (obj != null) { + if (!obj.swigCMemOwn) + throw new RuntimeException("Cannot release ownership as memory is not owned"); + ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + obj.delete(); + } + return ptr; + } +%} + %template() std::unique_ptr< TYPE >; %enddef diff --git a/Source/Modules/java.cxx b/Source/Modules/java.cxx index ea7e4607a..cf39fc80b 100644 --- a/Source/Modules/java.cxx +++ b/Source/Modules/java.cxx @@ -2025,6 +2025,8 @@ public: typemapLookup(n, "javabody", typemap_lookup_type, WARN_JAVA_TYPEMAP_JAVABODY_UNDEF), // main body of class NIL); + Printv(proxy_class_def, typemapLookup(n, "javarelease", typemap_lookup_type, WARN_NONE), NIL); + // C++ destructor is wrapped by the delete method // Note that the method name is specified in a typemap attribute called methodname String *destruct = NewString(""); From bf761998ed481821997e7c65f5d3e6acdf2eaf03 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sun, 10 Jul 2022 19:08:32 +0100 Subject: [PATCH 02/11] SWIGTYPE && input typemaps now assume object has been moved Change these typemaps to assume that after a function call, the parameter has been moved. The parameter's proxy class that owns the C++ object thus has the underlying pointer set to null so the object cannot be used again and the object is deleted. Scrap new javarelease typemap and move contents into javabody typemap. --- Examples/test-suite/common.mk | 1 + .../cpp11_rvalue_reference_move_input.i | 41 +++++++++++++ ...p11_rvalue_reference_move_input_runme.java | 60 +++++++++++++++++++ Examples/test-suite/java_throws.i | 18 +++++- Lib/java/java.swg | 32 +++++++++- Lib/java/std_unique_ptr.i | 14 ----- Source/Modules/java.cxx | 2 - 7 files changed, 150 insertions(+), 18 deletions(-) create mode 100644 Examples/test-suite/cpp11_rvalue_reference_move_input.i create mode 100644 Examples/test-suite/java/cpp11_rvalue_reference_move_input_runme.java diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index c654807d7..b4633e3f6 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -610,6 +610,7 @@ CPP11_TEST_CASES += \ cpp11_rvalue_reference \ cpp11_rvalue_reference2 \ cpp11_rvalue_reference3 \ + cpp11_rvalue_reference_move_input \ cpp11_sizeof_object \ cpp11_static_assert \ cpp11_std_array \ diff --git a/Examples/test-suite/cpp11_rvalue_reference_move_input.i b/Examples/test-suite/cpp11_rvalue_reference_move_input.i new file mode 100644 index 000000000..03abd7322 --- /dev/null +++ b/Examples/test-suite/cpp11_rvalue_reference_move_input.i @@ -0,0 +1,41 @@ +%module cpp11_rvalue_reference_move_input + +// Testcase for testing rvalue reference input typemaps which assume the object is moved during a function call + +#if defined(SWIGD) +%rename(trace) debug; +#endif + +%include "cpp11_move_only_helper.i" + +%rename(MoveAssign) MovableCopyable::operator=(MovableCopyable &&); +%ignore MovableCopyable::operator=(const MovableCopyable &); // ignore copy assignment operator, keep move assignment operator +%ignore MovableCopyable::MovableCopyable(const MovableCopyable &); // ignore copy constructor, keep the move constructor + +%inline %{ +#include +using namespace std; + +bool debug = false; + +class MovableCopyable { +public: + MovableCopyable(int i = 0) { if (debug) cout << "MovableCopyable(" << i << ")" << " " << this << endl; Counter::normal_constructor++; } + + MovableCopyable(const MovableCopyable &other) { if (debug) cout << "MovableCopyable(const MovableCopyable &)" << " " << this << " " << &other << endl; Counter::copy_constructor++;} + MovableCopyable & operator=(const MovableCopyable &other) { if (debug) cout << "operator=(const MovableCopyable &)" << " " << this << " " << &other << endl; Counter::copy_assignment++; return *this; } + + MovableCopyable(MovableCopyable &&other) noexcept { if (debug) cout << "MovableCopyable(MovableCopyable &&)" << " " << this << endl; Counter::move_constructor++; } + MovableCopyable & operator=(MovableCopyable &&other) noexcept { if (debug) cout << "operator=(MovableCopyable &&)" << " " << this << endl; Counter::move_assignment++; return *this; } + ~MovableCopyable() { if (debug) cout << "~MovableCopyable()" << " " << this << endl; Counter::destructor++; } + + static void movein(MovableCopyable &&mcin) { + MovableCopyable mc = std::move(mcin); + } + + static bool is_nullptr(MovableCopyable *p) { + return p == nullptr; + } +}; + +%} diff --git a/Examples/test-suite/java/cpp11_rvalue_reference_move_input_runme.java b/Examples/test-suite/java/cpp11_rvalue_reference_move_input_runme.java new file mode 100644 index 000000000..dfc09f217 --- /dev/null +++ b/Examples/test-suite/java/cpp11_rvalue_reference_move_input_runme.java @@ -0,0 +1,60 @@ + +import cpp11_rvalue_reference_move_input.*; + +public class cpp11_rvalue_reference_move_input_runme { + + static { + try { + System.loadLibrary("cpp11_rvalue_reference_move_input"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e); + System.exit(1); + } + } + + public static void main(String argv[]) { + + { + Counter.reset_counts(); + MovableCopyable mo = new MovableCopyable(222); + Counter.check_counts(1, 0, 0, 0, 0, 0); + MovableCopyable.movein(mo); + Counter.check_counts(1, 0, 0, 1, 0, 2); + if (!MovableCopyable.is_nullptr(mo)) + throw new RuntimeException("is_nullptr failed"); + mo.delete(); + Counter.check_counts(1, 0, 0, 1, 0, 2); + } + + { + // Move constructor test + Counter.reset_counts(); + MovableCopyable mo = new MovableCopyable(222); + Counter.check_counts(1, 0, 0, 0, 0, 0); + MovableCopyable mo_moved = new MovableCopyable(mo); + Counter.check_counts(1, 0, 0, 1, 0, 1); + if (!MovableCopyable.is_nullptr(mo)) + throw new RuntimeException("is_nullptr failed"); + mo.delete(); + Counter.check_counts(1, 0, 0, 1, 0, 1); + mo_moved.delete(); + Counter.check_counts(1, 0, 0, 1, 0, 2); + } + + { + // Move assignment operator test + Counter.reset_counts(); + MovableCopyable mo111 = new MovableCopyable(111); + MovableCopyable mo222 = new MovableCopyable(222); + Counter.check_counts(2, 0, 0, 0, 0, 0); + mo111.MoveAssign(mo222); + Counter.check_counts(2, 0, 0, 0, 1, 1); + if (!MovableCopyable.is_nullptr(mo222)) + throw new RuntimeException("is_nullptr failed"); + mo222.delete(); + Counter.check_counts(2, 0, 0, 0, 1, 1); + mo111.delete(); + Counter.check_counts(2, 0, 0, 0, 1, 2); + } + } +} diff --git a/Examples/test-suite/java_throws.i b/Examples/test-suite/java_throws.i index 940f2c192..7c3b6f328 100644 --- a/Examples/test-suite/java_throws.i +++ b/Examples/test-suite/java_throws.i @@ -192,10 +192,25 @@ try { } } %} -%typemap(javarelease) SWIGTYPE %{ + +%typemap(javabody) SWIGTYPE %{ + private transient long swigCPtr; + protected transient boolean swigCMemOwn; + + protected $javaclassname(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected static long getCPtr($javaclassname obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + protected static long swigRelease($javaclassname obj) { long ptr = 0; if (obj != null) { + if (!obj.swigCMemOwn) + throw new RuntimeException("Cannot release ownership as memory is not owned"); ptr = obj.swigCPtr; obj.swigCMemOwn = false; try { @@ -208,6 +223,7 @@ try { } %} + %inline %{ struct NoExceptTest { unsigned int noExceptionPlease() { return 123; } diff --git a/Lib/java/java.swg b/Lib/java/java.swg index 23744aeb3..19198b7b4 100644 --- a/Lib/java/java.swg +++ b/Lib/java/java.swg @@ -700,6 +700,7 @@ Swig::LocalRefGuard $1_refguard(jenv, $input); } SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "$1_type reference is null"); return $null; } %} +%typemap(freearg) SWIGTYPE && %{ delete $1; %}; %typemap(out) SWIGTYPE * %{ *($&1_ltype)&$result = $1; %} %typemap(out, fragment="SWIG_PackData", noblock=1) SWIGTYPE (CLASS::*) { @@ -1101,7 +1102,8 @@ Swig::LocalRefGuard $1_refguard(jenv, $input); } jobjectArray "$javainput" %typemap(javain) SWIGTYPE "$&javaclassname.getCPtr($javainput)" -%typemap(javain) SWIGTYPE *, SWIGTYPE &, SWIGTYPE &&, SWIGTYPE [] "$javaclassname.getCPtr($javainput)" +%typemap(javain) SWIGTYPE *, SWIGTYPE &, SWIGTYPE [] "$javaclassname.getCPtr($javainput)" +%typemap(javain) SWIGTYPE && "$javaclassname.swigRelease($javainput)" %typemap(javain) SWIGTYPE (CLASS::*) "$javaclassname.getCMemberPtr($javainput)" /* The javaout typemap is used for converting function return types from the return type @@ -1216,6 +1218,18 @@ Swig::LocalRefGuard $1_refguard(jenv, $input); } CPTR_VISIBILITY static long getCPtr($javaclassname obj) { return (obj == null) ? 0 : obj.swigCPtr; } + + CPTR_VISIBILITY static long swigRelease($javaclassname obj) { + long ptr = 0; + if (obj != null) { + if (!obj.swigCMemOwn) + throw new RuntimeException("Cannot release ownership as memory is not owned"); + ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + obj.delete(); + } + return ptr; + } %} // Derived proxy classes @@ -1230,6 +1244,18 @@ Swig::LocalRefGuard $1_refguard(jenv, $input); } CPTR_VISIBILITY static long getCPtr($javaclassname obj) { return (obj == null) ? 0 : obj.swigCPtr; } + + CPTR_VISIBILITY static long swigRelease($javaclassname obj) { + long ptr = 0; + if (obj != null) { + if (!obj.swigCMemOwn) + throw new RuntimeException("Cannot release ownership as memory is not owned"); + ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + obj.delete(); + } + return ptr; + } %} %enddef @@ -1249,6 +1275,10 @@ Swig::LocalRefGuard $1_refguard(jenv, $input); } CPTR_VISIBILITY static long getCPtr($javaclassname obj) { return (obj == null) ? 0 : obj.swigCPtr; } + + CPTR_VISIBILITY static long swigRelease($javaclassname obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } %} %typemap(javabody) TYPE (CLASS::*) %{ diff --git a/Lib/java/std_unique_ptr.i b/Lib/java/std_unique_ptr.i index 7a647f616..5f4a0c5a2 100644 --- a/Lib/java/std_unique_ptr.i +++ b/Lib/java/std_unique_ptr.i @@ -30,20 +30,6 @@ return (cPtr == 0) ? null : new $typemap(jstype, TYPE)(cPtr, true); } -%typemap(javarelease) TYPE %{ - protected static long swigRelease($javaclassname obj) { - long ptr = 0; - if (obj != null) { - if (!obj.swigCMemOwn) - throw new RuntimeException("Cannot release ownership as memory is not owned"); - ptr = obj.swigCPtr; - obj.swigCMemOwn = false; - obj.delete(); - } - return ptr; - } -%} - %template() std::unique_ptr< TYPE >; %enddef diff --git a/Source/Modules/java.cxx b/Source/Modules/java.cxx index cf39fc80b..ea7e4607a 100644 --- a/Source/Modules/java.cxx +++ b/Source/Modules/java.cxx @@ -2025,8 +2025,6 @@ public: typemapLookup(n, "javabody", typemap_lookup_type, WARN_JAVA_TYPEMAP_JAVABODY_UNDEF), // main body of class NIL); - Printv(proxy_class_def, typemapLookup(n, "javarelease", typemap_lookup_type, WARN_NONE), NIL); - // C++ destructor is wrapped by the delete method // Note that the method name is specified in a typemap attribute called methodname String *destruct = NewString(""); From 1bf15210ef5f86e588cc8a08ff5b1206637feb03 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sat, 16 Jul 2022 23:02:21 +0100 Subject: [PATCH 03/11] Java unique_ptr enhance test for double release --- .../java/cpp11_std_unique_ptr_runme.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java b/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java index 3f2b15f44..4ac72d720 100644 --- a/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java +++ b/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java @@ -42,6 +42,27 @@ public class cpp11_std_unique_ptr_runme { checkCount(0); } + { + Klass kin = new Klass("KlassInput"); + checkCount(1); + String s = cpp11_std_unique_ptr.takeKlassUniquePtr(kin); + checkCount(0); + if (!s.equals("KlassInput")) + throw new RuntimeException("Incorrect string: " + s); + if (!cpp11_std_unique_ptr.is_nullptr(kin)) + throw new RuntimeException("is_nullptr failed"); + boolean exception_thrown = false; + try { + cpp11_std_unique_ptr.takeKlassUniquePtr(kin); + } catch (RuntimeException e) { + exception_thrown = true; + } + if (!exception_thrown) + throw new RuntimeException("double usage of takeKlassUniquePtr should have been an error"); + kin.delete(); // Should not fail, even though already deleted + checkCount(0); + } + { KlassInheritance kini = new KlassInheritance("KlassInheritanceInput"); checkCount(1); From 8bd9eb3f16621ae900b974845806b12262884466 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sun, 17 Jul 2022 12:46:16 +0100 Subject: [PATCH 04/11] Java unique_ptr test ownership enhancement to test Make sure the object is owned before releasing ownership and passing ownership to unique_ptr instance. --- Examples/test-suite/cpp11_std_unique_ptr.i | 4 ++++ .../java/cpp11_std_unique_ptr_runme.java | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/Examples/test-suite/cpp11_std_unique_ptr.i b/Examples/test-suite/cpp11_std_unique_ptr.i index fd0e8e942..c96047f64 100644 --- a/Examples/test-suite/cpp11_std_unique_ptr.i +++ b/Examples/test-suite/cpp11_std_unique_ptr.i @@ -63,6 +63,10 @@ bool is_nullptr(Klass *p) { return p == nullptr; } +Klass *get_not_owned_ptr(Klass *p) { + return p; +} + std::unique_ptr makeKlassUniquePtr(const char* label) { return std::unique_ptr(new Klass(label)); } diff --git a/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java b/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java index 4ac72d720..14c117206 100644 --- a/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java +++ b/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java @@ -63,6 +63,21 @@ public class cpp11_std_unique_ptr_runme { checkCount(0); } + { + Klass kin = new Klass("KlassInput"); + boolean exception_thrown = false; + try { + Klass notowned = cpp11_std_unique_ptr.get_not_owned_ptr(kin); + cpp11_std_unique_ptr.takeKlassUniquePtr(notowned); + } catch (RuntimeException e) { + exception_thrown = true; + } + if (!exception_thrown) + throw new RuntimeException("Should have thrown 'Cannot release ownership as memory is not owned' error"); + kin.delete(); + checkCount(0); + } + { KlassInheritance kini = new KlassInheritance("KlassInheritanceInput"); checkCount(1); From c737bd57131c02eed9573d13f30662397890ba0b Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Fri, 15 Jul 2022 17:41:58 +0100 Subject: [PATCH 05/11] Add C# support std::unique_ptr inputs Based on Java implementation. --- Examples/test-suite/cpp11_std_unique_ptr.i | 2 +- .../csharp/cpp11_std_unique_ptr_runme.cs | 63 +++++++++++++++++++ Lib/csharp/csharp.swg | 30 +++++++++ Lib/csharp/std_auto_ptr.i | 2 +- Lib/csharp/std_unique_ptr.i | 10 ++- 5 files changed, 103 insertions(+), 4 deletions(-) diff --git a/Examples/test-suite/cpp11_std_unique_ptr.i b/Examples/test-suite/cpp11_std_unique_ptr.i index c96047f64..5ef49456c 100644 --- a/Examples/test-suite/cpp11_std_unique_ptr.i +++ b/Examples/test-suite/cpp11_std_unique_ptr.i @@ -53,7 +53,7 @@ struct KlassInheritance : virtual Klass { } }; -#if defined(SWIGJAVA) +#if defined(SWIGJAVA) || defined (SWIGCSHARP) std::string takeKlassUniquePtr(std::unique_ptr k) { return std::string(k->getLabel()); } diff --git a/Examples/test-suite/csharp/cpp11_std_unique_ptr_runme.cs b/Examples/test-suite/csharp/cpp11_std_unique_ptr_runme.cs index db5c8cff0..5b730aa05 100644 --- a/Examples/test-suite/csharp/cpp11_std_unique_ptr_runme.cs +++ b/Examples/test-suite/csharp/cpp11_std_unique_ptr_runme.cs @@ -9,8 +9,71 @@ public class cpp11_std_unique_ptr_runme { System.Threading.Thread.Sleep(10); } + private static void checkCount(int expected_count) + { + int actual_count = Klass.getTotal_count(); + if (actual_count != expected_count) + throw new ApplicationException("Counts incorrect, expected:" + expected_count + " actual:" + actual_count); + } + public static void Main() { + // unique_ptr as input + using (Klass kin = new Klass("KlassInput")) { + checkCount(1); + string s = cpp11_std_unique_ptr.takeKlassUniquePtr(kin); + checkCount(0); + if (s != "KlassInput") + throw new ApplicationException("Incorrect string: " + s); + if (!cpp11_std_unique_ptr.is_nullptr(kin)) + throw new ApplicationException("is_nullptr failed"); + } // Dispose should not fail, even though already deleted + checkCount(0); + + using (Klass kin = new Klass("KlassInput")) { + checkCount(1); + string s = cpp11_std_unique_ptr.takeKlassUniquePtr(kin); + checkCount(0); + if (s != "KlassInput") + throw new ApplicationException("Incorrect string: " + s); + if (!cpp11_std_unique_ptr.is_nullptr(kin)) + throw new ApplicationException("is_nullptr failed"); + bool exception_thrown = false; + try { + cpp11_std_unique_ptr.takeKlassUniquePtr(kin); + } catch (ApplicationException) { + exception_thrown = true; + } + if (!exception_thrown) + throw new ApplicationException("double usage of takeKlassUniquePtr should have been an error"); + } // Dispose should not fail, even though already deleted + checkCount(0); + + using (Klass kin = new Klass("KlassInput")) { + bool exception_thrown = false; + try { + Klass notowned = cpp11_std_unique_ptr.get_not_owned_ptr(kin); + cpp11_std_unique_ptr.takeKlassUniquePtr(notowned); + } catch (ApplicationException) { + exception_thrown = true; + } + if (!exception_thrown) + throw new ApplicationException("Should have thrown 'Cannot release ownership as memory is not owned' error"); + } + checkCount(0); + + using (KlassInheritance kini = new KlassInheritance("KlassInheritanceInput")) { + checkCount(1); + string s = cpp11_std_unique_ptr.takeKlassUniquePtr(kini); + checkCount(0); + if (s != "KlassInheritanceInput") + throw new ApplicationException("Incorrect string: " + s); + if (!cpp11_std_unique_ptr.is_nullptr(kini)) + throw new ApplicationException("is_nullptr failed"); + } // Dispose should not fail, even though already deleted + checkCount(0); + + // unique_ptr as output Klass k1 = cpp11_std_unique_ptr.makeKlassUniquePtr("first"); if (k1.getLabel() != "first") throw new Exception("wrong object label"); diff --git a/Lib/csharp/csharp.swg b/Lib/csharp/csharp.swg index 94e0458a6..60ab388f3 100644 --- a/Lib/csharp/csharp.swg +++ b/Lib/csharp/csharp.swg @@ -913,6 +913,19 @@ SWIGINTERN const char * SWIG_UnpackData(const char *c, void *ptr, size_t sz) { CPTR_VISIBILITY static global::System.Runtime.InteropServices.HandleRef getCPtr($csclassname obj) { return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; } + + CPTR_VISIBILITY static global::System.Runtime.InteropServices.HandleRef swigRelease($csclassname obj) { + if (obj != null) { + if (!obj.swigCMemOwn) + throw new global::System.ApplicationException("Cannot release ownership as memory is not owned"); + global::System.Runtime.InteropServices.HandleRef ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + obj.Dispose(); + return ptr; + } else { + return new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); + } + } %} // Derived proxy classes @@ -926,6 +939,19 @@ SWIGINTERN const char * SWIG_UnpackData(const char *c, void *ptr, size_t sz) { CPTR_VISIBILITY static global::System.Runtime.InteropServices.HandleRef getCPtr($csclassname obj) { return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; } + + CPTR_VISIBILITY static global::System.Runtime.InteropServices.HandleRef swigRelease($csclassname obj) { + if (obj != null) { + if (!obj.swigCMemOwn) + throw new global::System.ApplicationException("Cannot release ownership as memory is not owned"); + global::System.Runtime.InteropServices.HandleRef ptr = obj.swigCPtr; + obj.swigCMemOwn = false; + obj.Dispose(); + return ptr; + } else { + return new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero); + } + } %} %enddef @@ -945,6 +971,10 @@ SWIGINTERN const char * SWIG_UnpackData(const char *c, void *ptr, size_t sz) { CPTR_VISIBILITY static global::System.Runtime.InteropServices.HandleRef getCPtr($csclassname obj) { return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; } + + CPTR_VISIBILITY static global::System.Runtime.InteropServices.HandleRef swigRelease($csclassname obj) { + return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; + } %} %typemap(csbody) TYPE (CLASS::*) %{ diff --git a/Lib/csharp/std_auto_ptr.i b/Lib/csharp/std_auto_ptr.i index 068d3a9d1..b1ec3d569 100644 --- a/Lib/csharp/std_auto_ptr.i +++ b/Lib/csharp/std_auto_ptr.i @@ -9,7 +9,7 @@ %define %auto_ptr(TYPE) %typemap (ctype) std::auto_ptr< TYPE > "void *" -%typemap (imtype, out="System.IntPtr") std::auto_ptr< TYPE > "HandleRef" +%typemap (imtype, out="System.IntPtr") std::auto_ptr< TYPE > "global::System.Runtime.InteropServices.HandleRef" %typemap (cstype) std::auto_ptr< TYPE > "$typemap(cstype, TYPE)" %typemap (out) std::auto_ptr< TYPE > %{ $result = (void *)$1.release(); diff --git a/Lib/csharp/std_unique_ptr.i b/Lib/csharp/std_unique_ptr.i index b2716756a..ba13fce4e 100644 --- a/Lib/csharp/std_unique_ptr.i +++ b/Lib/csharp/std_unique_ptr.i @@ -9,8 +9,14 @@ %define %unique_ptr(TYPE) %typemap (ctype) std::unique_ptr< TYPE > "void *" -%typemap (imtype, out="System.IntPtr") std::unique_ptr< TYPE > "HandleRef" +%typemap (imtype, out="System.IntPtr") std::unique_ptr< TYPE > "global::System.Runtime.InteropServices.HandleRef" %typemap (cstype) std::unique_ptr< TYPE > "$typemap(cstype, TYPE)" + +%typemap(in) std::unique_ptr< TYPE > +%{ $1.reset((TYPE *)$input); %} + +%typemap(csin) std::unique_ptr< TYPE > "$typemap(cstype, TYPE).swigRelease($csinput)" + %typemap (out) std::unique_ptr< TYPE > %{ $result = (void *)$1.release(); %} @@ -23,5 +29,5 @@ %enddef namespace std { - template class unique_ptr {}; + template class unique_ptr {}; } From c3c061cac8fbae8c65caefe462a3bd879fc6f547 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sat, 16 Jul 2022 13:14:40 +0100 Subject: [PATCH 06/11] Add Python support for std::unique_ptr inputs Equivalent to Java/C# implementation. --- Examples/test-suite/cpp11_std_unique_ptr.i | 2 +- .../python/cpp11_std_unique_ptr_runme.py | 65 +++++++++++++++++++ Lib/python/pyrun.swg | 17 +++-- Lib/python/std_unique_ptr.i | 13 ++++ Lib/swigerrors.swg | 3 +- Lib/swigrun.swg | 10 ++- Lib/typemaps/exception.swg | 1 + Lib/typemaps/swigtypemaps.swg | 1 + 8 files changed, 103 insertions(+), 9 deletions(-) diff --git a/Examples/test-suite/cpp11_std_unique_ptr.i b/Examples/test-suite/cpp11_std_unique_ptr.i index 5ef49456c..804a4a27d 100644 --- a/Examples/test-suite/cpp11_std_unique_ptr.i +++ b/Examples/test-suite/cpp11_std_unique_ptr.i @@ -53,7 +53,7 @@ struct KlassInheritance : virtual Klass { } }; -#if defined(SWIGJAVA) || defined (SWIGCSHARP) +#if defined(SWIGJAVA) || defined (SWIGCSHARP) || defined(SWIGPYTHON) std::string takeKlassUniquePtr(std::unique_ptr k) { return std::string(k->getLabel()); } diff --git a/Examples/test-suite/python/cpp11_std_unique_ptr_runme.py b/Examples/test-suite/python/cpp11_std_unique_ptr_runme.py index a84efcd5f..d102ebcd8 100644 --- a/Examples/test-suite/python/cpp11_std_unique_ptr_runme.py +++ b/Examples/test-suite/python/cpp11_std_unique_ptr_runme.py @@ -1,5 +1,70 @@ from cpp11_std_unique_ptr import * +def checkCount(expected_count): + actual_count = Klass.getTotal_count() + if (actual_count != expected_count): + raise RuntimeError("Counts incorrect, expected:" + expected_count + " actual:" + actual_count) + +# unique_ptr as input +kin = Klass("KlassInput") +checkCount(1) +s = takeKlassUniquePtr(kin) +checkCount(0) +if kin.thisown: + raise RuntimeError("thisown should be false") +if s != "KlassInput": + raise RuntimeError("Incorrect string: " + s) +if not is_nullptr(kin): + raise RuntimeError("is_nullptr failed") +del kin # Should not fail, even though already deleted +checkCount(0) + +kin = Klass("KlassInput") +checkCount(1) +s = takeKlassUniquePtr(kin) +checkCount(0) +if kin.thisown: + raise RuntimeError("thisown should be false") +if s != "KlassInput": + raise RuntimeError("Incorrect string: " + s) +if not is_nullptr(kin): + raise RuntimeError("is_nullptr failed") +exception_thrown = False +try: + s = takeKlassUniquePtr(kin) +except RuntimeError as e: + exception_thrown = True +if not exception_thrown: + raise RuntimeError("double usage of takeKlassUniquePtr should have been an error") +del kin # Should not fail, even though already deleted +checkCount(0) + +kin = Klass("KlassInput") +exception_thrown = False +try: + notowned = get_not_owned_ptr(kin) + takeKlassUniquePtr(notowned) +except RuntimeError as e: + exception_thrown = True +if not exception_thrown: + raise RuntimeError("Should have thrown 'Cannot release ownership as memory is not owned' error") +del kin +checkCount(0) + +kini = KlassInheritance("KlassInheritanceInput") +checkCount(1) +s = takeKlassUniquePtr(kini) +checkCount(0) +if kini.thisown: + raise RuntimeError("thisown should be false") +if s != "KlassInheritanceInput": + raise RuntimeError("Incorrect string: " + s) +if not is_nullptr(kini): + raise RuntimeError("is_nullptr failed") +del kini # Should not fail, even though already deleted +checkCount(0) + +# unique_ptr as output k1 = makeKlassUniquePtr("first") k2 = makeKlassUniquePtr("second") if Klass.getTotal_count() != 2: diff --git a/Lib/python/pyrun.swg b/Lib/python/pyrun.swg index 935885934..ec6e3d5a8 100644 --- a/Lib/python/pyrun.swg +++ b/Lib/python/pyrun.swg @@ -1357,12 +1357,19 @@ SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int } } if (sobj) { - if (own) - *own = *own | sobj->own; - if (flags & SWIG_POINTER_DISOWN) { - sobj->own = 0; + if (((flags & SWIG_POINTER_RELEASE) == SWIG_POINTER_RELEASE) && !sobj->own) { + res = SWIG_ERROR_RELEASE_NOT_OWNED; + } else { + if (own) + *own = *own | sobj->own; + if (flags & SWIG_POINTER_DISOWN) { + sobj->own = 0; + } + if (flags & SWIG_POINTER_CLEAR) { + sobj->ptr = 0; + } + res = SWIG_OK; } - res = SWIG_OK; } else { if (implicit_conv) { SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; diff --git a/Lib/python/std_unique_ptr.i b/Lib/python/std_unique_ptr.i index 331817f76..bec8bf968 100644 --- a/Lib/python/std_unique_ptr.i +++ b/Lib/python/std_unique_ptr.i @@ -8,9 +8,22 @@ * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) +%typemap(in, noblock=1) std::unique_ptr< TYPE > (void *argp = 0, int res = 0) { + res = SWIG_ConvertPtr($input, &argp, $descriptor(TYPE *), SWIG_POINTER_RELEASE | %convertptr_flags); + if (!SWIG_IsOK(res)) { + if (res == SWIG_ERROR_RELEASE_NOT_OWNED) { + %releasenotowned_fail(res, "TYPE *", $symname, $argnum); + } else { + %argument_fail(res, "TYPE *", $symname, $argnum); + } + } + $1.reset((TYPE *)argp); +} + %typemap (out) std::unique_ptr< TYPE > %{ %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} + %template() std::unique_ptr< TYPE >; %enddef diff --git a/Lib/swigerrors.swg b/Lib/swigerrors.swg index 1a6d20366..4d5a8e473 100644 --- a/Lib/swigerrors.swg +++ b/Lib/swigerrors.swg @@ -1,4 +1,4 @@ -/* Errors in SWIG */ +/* SWIG Errors applicable to all language modules, values are reserved from -1 to -99 */ #define SWIG_UnknownError -1 #define SWIG_IOError -2 #define SWIG_RuntimeError -3 @@ -13,4 +13,3 @@ #define SWIG_MemoryError -12 #define SWIG_NullReferenceError -13 - diff --git a/Lib/swigrun.swg b/Lib/swigrun.swg index de0db2dc3..9ec98ccf2 100644 --- a/Lib/swigrun.swg +++ b/Lib/swigrun.swg @@ -44,6 +44,8 @@ #define SWIG_POINTER_DISOWN 0x1 #define SWIG_CAST_NEW_MEMORY 0x2 #define SWIG_POINTER_NO_NULL 0x4 +#define SWIG_POINTER_CLEAR 0x8 +#define SWIG_POINTER_RELEASE (SWIG_POINTER_CLEAR | SWIG_POINTER_DISOWN) /* Flags for new pointer objects */ #define SWIG_POINTER_OWN 0x1 @@ -129,7 +131,13 @@ */ #define SWIG_OK (0) +/* Runtime errors are < 0 */ #define SWIG_ERROR (-1) +/* Errors in range -1 to -99 are in swigerrors.swg (errors for all languages including those not using the runtime) */ +/* Errors in range -100 to -199 are language specific errors defined in *errors.swg */ +/* Errors < -200 are generic runtime specific errors */ +#define SWIG_ERROR_RELEASE_NOT_OWNED (-200) + #define SWIG_IsOK(r) (r >= 0) #define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) @@ -144,7 +152,7 @@ #define SWIG_OLDOBJ (SWIG_OK) #define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) #define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) -/* Check, add and del mask methods */ +/* Check, add and del object mask methods */ #define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) #define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) #define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) diff --git a/Lib/typemaps/exception.swg b/Lib/typemaps/exception.swg index e48294c3f..aece8326f 100644 --- a/Lib/typemaps/exception.swg +++ b/Lib/typemaps/exception.swg @@ -19,6 +19,7 @@ #endif #define %varnullref_fmt(_type,_name) %nullref_fmt() %varfail_fmt(_type, _name) #define %outnullref_fmt(_type) %nullref_fmt() %outfail_fmt(_type) +#define %releasenotownedfail_fmt(_type,_name,_argn) "in method '" `_name` "', cannot release ownership as memory is not owned for argument " `_argn`" of type '" `_type`"'" /* setting an error */ #define %error(code,msg...) SWIG_Error(code, msg) diff --git a/Lib/typemaps/swigtypemaps.swg b/Lib/typemaps/swigtypemaps.swg index 4e5bb2b04..733e5acd0 100644 --- a/Lib/typemaps/swigtypemaps.swg +++ b/Lib/typemaps/swigtypemaps.swg @@ -140,6 +140,7 @@ #define %argument_nullref(type, name, argn) SWIG_exception_fail(SWIG_ValueError, %argnullref_fmt(type, name, argn)) #define %variable_fail(code, type, name) SWIG_exception_fail(%default_code(code), %varfail_fmt(type, name)) #define %variable_nullref(type, name) SWIG_exception_fail(SWIG_ValueError, %varnullref_fmt(type, name)) +#define %releasenotowned_fail(code, type, name, argn) SWIG_exception_fail(%default_code(code), %releasenotownedfail_fmt(type, name, argn)) #if defined(SWIG_DIRECTOR_TYPEMAPS) #define %dirout_fail(code, type) SWIG_DirOutFail(%default_code(code), %outfail_fmt(type)) From f99a2e6f6432423cc64c3e45756519637df08e27 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sun, 17 Jul 2022 14:27:33 +0100 Subject: [PATCH 07/11] Add Ruby support for std::unique_ptr inputs Equivalent to Java/C#/Python implementations. --- Examples/test-suite/cpp11_std_unique_ptr.i | 2 - .../ruby/cpp11_std_unique_ptr_runme.rb | 91 +++++++++++++++++++ Lib/ruby/rubyrun.swg | 11 ++- Lib/ruby/std_unique_ptr.i | 13 +++ 4 files changed, 114 insertions(+), 3 deletions(-) diff --git a/Examples/test-suite/cpp11_std_unique_ptr.i b/Examples/test-suite/cpp11_std_unique_ptr.i index 804a4a27d..ec7505974 100644 --- a/Examples/test-suite/cpp11_std_unique_ptr.i +++ b/Examples/test-suite/cpp11_std_unique_ptr.i @@ -53,11 +53,9 @@ struct KlassInheritance : virtual Klass { } }; -#if defined(SWIGJAVA) || defined (SWIGCSHARP) || defined(SWIGPYTHON) std::string takeKlassUniquePtr(std::unique_ptr k) { return std::string(k->getLabel()); } -#endif bool is_nullptr(Klass *p) { return p == nullptr; diff --git a/Examples/test-suite/ruby/cpp11_std_unique_ptr_runme.rb b/Examples/test-suite/ruby/cpp11_std_unique_ptr_runme.rb index cfc03fe2a..ed9138bcd 100644 --- a/Examples/test-suite/ruby/cpp11_std_unique_ptr_runme.rb +++ b/Examples/test-suite/ruby/cpp11_std_unique_ptr_runme.rb @@ -11,6 +11,97 @@ def gc_check(expected_count) # swig_assert_equal_simple(expected_count, Cpp11_std_unique_ptr::Klass::getTotal_count()) end +def checkCount(expected_count) + actual_count = Cpp11_std_unique_ptr::Klass.getTotal_count() + if (actual_count != expected_count) + raise RuntimeError, "Counts incorrect, expected:" + expected_count + " actual:" + actual_count + end +end + + +# unique_ptr as input +kin = Cpp11_std_unique_ptr::Klass.new("KlassInput") +checkCount(1) +s = Cpp11_std_unique_ptr.takeKlassUniquePtr(kin) +checkCount(0) +if (s != "KlassInput") + raise RuntimeError, "Incorrect string: " + s +end +exception_thrown = false +begin + Cpp11_std_unique_ptr.is_nullptr(kin) +rescue ObjectPreviouslyDeleted + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "is_nullptr failed to throw" +end +kin = nil +checkCount(0) + +kin = Cpp11_std_unique_ptr::Klass.new("KlassInput") +checkCount(1) +s = Cpp11_std_unique_ptr.takeKlassUniquePtr(kin) +checkCount(0) +if (s != "KlassInput") + raise RuntimeError, "Incorrect string: " + s +end +exception_thrown = false +begin + Cpp11_std_unique_ptr.is_nullptr(kin) +rescue ObjectPreviouslyDeleted + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "is_nullptr failed to throw" +end +exception_thrown = false +begin + Cpp11_std_unique_ptr.takeKlassUniquePtr(kin) +rescue RuntimeError => e + # puts e.message + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "double usage of takeKlassUniquePtr should have been an error" +end +kin = nil +checkCount(0) + +kin = Cpp11_std_unique_ptr::Klass.new("KlassInput") +exception_thrown = false +begin + notowned = Cpp11_std_unique_ptr::get_not_owned_ptr(kin) + Cpp11_std_unique_ptr::takeKlassUniquePtr(notowned) +rescue RuntimeError + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "Should have thrown 'Cannot release ownership as memory is not owned' error" +end +Cpp11_std_unique_ptr.takeKlassUniquePtr(kin) # Ensure object is deleted (can't rely on GC) +checkCount(0) + +kini = Cpp11_std_unique_ptr::KlassInheritance.new("KlassInheritanceInput") +checkCount(1) +s = Cpp11_std_unique_ptr.takeKlassUniquePtr(kini) +checkCount(0) +if (s != "KlassInheritanceInput") + raise RuntimeError, "Incorrect string: " + s +end +exception_thrown = false +begin + Cpp11_std_unique_ptr.is_nullptr(kini) +rescue ObjectPreviouslyDeleted + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "is_nullptr failed to throw" +end +kini = nil +checkCount(0) + +# unique_ptr as output k1 = Cpp11_std_unique_ptr::makeKlassUniquePtr("first") k2 = Cpp11_std_unique_ptr::makeKlassUniquePtr("second") swig_assert_equal_simple(2, Cpp11_std_unique_ptr::Klass::getTotal_count()) diff --git a/Lib/ruby/rubyrun.swg b/Lib/ruby/rubyrun.swg index e4c23bde5..6cac4626a 100644 --- a/Lib/ruby/rubyrun.swg +++ b/Lib/ruby/rubyrun.swg @@ -281,6 +281,11 @@ SWIG_Ruby_ConvertPtrAndOwn(VALUE obj, void **ptr, swig_type_info *ty, int flags, own->own = 0; } + if (((flags & SWIG_POINTER_RELEASE) == SWIG_POINTER_RELEASE)) { + if (!RDATA(obj)->dfree) + return SWIG_ERROR_RELEASE_NOT_OWNED; + } + /* Check to see if the input object is giving up ownership of the underlying C struct or C++ object. If so then we need to reset the destructor since the Ruby object no @@ -292,7 +297,7 @@ SWIG_Ruby_ConvertPtrAndOwn(VALUE obj, void **ptr, swig_type_info *ty, int flags, swig_class *sklass = (swig_class *) ty->clientdata; track = sklass->trackObjects; } - + if (track) { /* We are tracking objects for this class. Thus we change the destructor * to SWIG_RubyRemoveTracking. This allows us to @@ -306,6 +311,10 @@ SWIG_Ruby_ConvertPtrAndOwn(VALUE obj, void **ptr, swig_type_info *ty, int flags, } } + if (flags & SWIG_POINTER_CLEAR) { + DATA_PTR(obj) = 0; + } + /* Do type-checking if type info was provided */ if (ty) { if (ty->clientdata) { diff --git a/Lib/ruby/std_unique_ptr.i b/Lib/ruby/std_unique_ptr.i index 163c7c2d1..a0128ba78 100644 --- a/Lib/ruby/std_unique_ptr.i +++ b/Lib/ruby/std_unique_ptr.i @@ -8,9 +8,22 @@ * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) +%typemap(in, noblock=1) std::unique_ptr< TYPE > (void *argp = 0, int res = 0) { + res = SWIG_ConvertPtr($input, &argp, $descriptor(TYPE *), SWIG_POINTER_RELEASE | %convertptr_flags); + if (!SWIG_IsOK(res)) { + if (res == SWIG_ERROR_RELEASE_NOT_OWNED) { + %releasenotowned_fail(res, "TYPE *", $symname, $argnum); + } else { + %argument_fail(res, "TYPE *", $symname, $argnum); + } + } + $1.reset((TYPE *)argp); +} + %typemap (out) std::unique_ptr< TYPE > %{ %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} + %template() std::unique_ptr< TYPE >; %enddef From 50fc600e8cb128e8e1d4be3d07107b9f8df5edc2 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sun, 17 Jul 2022 20:23:52 +0100 Subject: [PATCH 08/11] Add Perl support for std::unique_ptr inputs Equivalent to Java/C#/Python/Ruby implementations. --- .../perl5/cpp11_std_unique_ptr_runme.pl | 60 ++++++++++++++++++- Lib/perl5/perlrun.swg | 17 +++++- Lib/perl5/std_unique_ptr.i | 13 ++++ 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/Examples/test-suite/perl5/cpp11_std_unique_ptr_runme.pl b/Examples/test-suite/perl5/cpp11_std_unique_ptr_runme.pl index ce64bbf00..a42580aca 100644 --- a/Examples/test-suite/perl5/cpp11_std_unique_ptr_runme.pl +++ b/Examples/test-suite/perl5/cpp11_std_unique_ptr_runme.pl @@ -1,9 +1,67 @@ use strict; use warnings; -use Test::More tests => 6; +use Test::More tests => 24; BEGIN { use_ok('cpp11_std_unique_ptr') } require_ok('cpp11_std_unique_ptr'); +# adapted from ../java/cpp11_std_unique_ptr_runme.java + +sub checkCount { + my($expected_count) = @_; + my $actual_count = cpp11_std_unique_ptr::Klass::getTotal_count(); + is($actual_count, $expected_count, "Counts incorrect, expected: $expected_count actual: $actual_count"); +} + +# unique_ptr as input +{ + my $kin = new cpp11_std_unique_ptr::Klass("KlassInput"); + checkCount(1); + my $s = cpp11_std_unique_ptr::takeKlassUniquePtr($kin); + checkCount(0); + is($s, "KlassInput", "Incorrect string: $s"); + is(cpp11_std_unique_ptr::is_nullptr($kin), 1, "is_nullptr check"); + undef $kin; # Should not fail, even though already deleted + checkCount(0); +} + +{ + my $kin = new cpp11_std_unique_ptr::Klass("KlassInput"); + checkCount(1); + my $s = cpp11_std_unique_ptr::takeKlassUniquePtr($kin); + checkCount(0); + is($s, "KlassInput", "Incorrect string: $s"); + is(cpp11_std_unique_ptr::is_nullptr($kin), 1, "is_nullptr check"); + eval { + cpp11_std_unique_ptr::takeKlassUniquePtr($kin); + }; + like($@, qr/\bcannot release ownership as memory is not owned\b/, "double usage of takeKlassUniquePtr should be an error"); + undef $kin; # Should not fail, even though already deleted + checkCount(0); +} + +{ + my $kin = new cpp11_std_unique_ptr::Klass("KlassInput"); + eval { + my $notowned = cpp11_std_unique_ptr::get_not_owned_ptr($kin); + cpp11_std_unique_ptr::takeKlassUniquePtr($notowned); + }; + like($@, qr/\bcannot release ownership as memory is not owned\b/, "double usage of takeKlassUniquePtr should be an error"); + undef $kin; + checkCount(0); +} + +{ + my $kini = new cpp11_std_unique_ptr::KlassInheritance("KlassInheritanceInput"); + checkCount(1); + my $s = cpp11_std_unique_ptr::takeKlassUniquePtr($kini); + checkCount(0); + is($s, "KlassInheritanceInput", "Incorrect string: $s"); + is(cpp11_std_unique_ptr::is_nullptr($kini), 1, "is_nullptr failed"); + undef $kini; # Should not fail, even though already deleted + checkCount(0); +} + +# unique_ptr as output my $k1 = cpp11_std_unique_ptr::makeKlassUniquePtr("first"); my $k2 = cpp11_std_unique_ptr::makeKlassUniquePtr("second"); is(cpp11_std_unique_ptr::Klass::getTotal_count, 2, "have 2 pointers"); diff --git a/Lib/perl5/perlrun.swg b/Lib/perl5/perlrun.swg index 28703d1a6..71f19cbf8 100644 --- a/Lib/perl5/perlrun.swg +++ b/Lib/perl5/perlrun.swg @@ -210,6 +210,7 @@ SWIG_Perl_ConvertPtrAndOwn(SWIG_MAYBE_PERL_OBJECT SV *sv, void **ptr, swig_type_ swig_cast_info *tc; void *voidptr = (void *)0; SV *tsv = 0; + int check_owned_pointer_release = (flags & SWIG_POINTER_RELEASE) == SWIG_POINTER_RELEASE; if (own) *own = 0; @@ -286,13 +287,14 @@ SWIG_Perl_ConvertPtrAndOwn(SWIG_MAYBE_PERL_OBJECT SV *sv, void **ptr, swig_type_ /* * DISOWN implementation: we need a perl guru to check this one. */ - if (tsv && (flags & SWIG_POINTER_DISOWN)) { + if (tsv && ((flags & SWIG_POINTER_DISOWN) || check_owned_pointer_release)) { /* * almost copy paste code from below SWIG_POINTER_OWN setting */ SV *obj = sv; HV *stash = SvSTASH(SvRV(obj)); GV *gv = *(GV**)hv_fetch(stash, "OWNER", 5, TRUE); + int owned = 0; if (isGV(gv)) { HV *hv = GvHVn(gv); /* @@ -300,10 +302,21 @@ SWIG_Perl_ConvertPtrAndOwn(SWIG_MAYBE_PERL_OBJECT SV *sv, void **ptr, swig_type_ * Hence, to remove ownership, we delete the entry. */ if (hv_exists_ent(hv, obj, 0)) { - hv_delete_ent(hv, obj, 0, 0); + owned = 1; + if (flags & SWIG_POINTER_DISOWN) { + hv_delete_ent(hv, obj, 0, 0); + } } } + if (check_owned_pointer_release && !owned) { + return SWIG_ERROR_RELEASE_NOT_OWNED; + } } + + if (tsv && (flags & SWIG_POINTER_CLEAR)) { + SvIV_set(tsv, 0); + } + return SWIG_OK; } diff --git a/Lib/perl5/std_unique_ptr.i b/Lib/perl5/std_unique_ptr.i index 163c7c2d1..a0128ba78 100644 --- a/Lib/perl5/std_unique_ptr.i +++ b/Lib/perl5/std_unique_ptr.i @@ -8,9 +8,22 @@ * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) +%typemap(in, noblock=1) std::unique_ptr< TYPE > (void *argp = 0, int res = 0) { + res = SWIG_ConvertPtr($input, &argp, $descriptor(TYPE *), SWIG_POINTER_RELEASE | %convertptr_flags); + if (!SWIG_IsOK(res)) { + if (res == SWIG_ERROR_RELEASE_NOT_OWNED) { + %releasenotowned_fail(res, "TYPE *", $symname, $argnum); + } else { + %argument_fail(res, "TYPE *", $symname, $argnum); + } + } + $1.reset((TYPE *)argp); +} + %typemap (out) std::unique_ptr< TYPE > %{ %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} + %template() std::unique_ptr< TYPE >; %enddef From 5dd5e80a84a9c7b0941f55e21c8ac53c7529f5fd Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sun, 17 Jul 2022 20:44:20 +0100 Subject: [PATCH 09/11] Cosmetic formatting and doc updates in std_unique_ptr.i files --- Lib/csharp/std_unique_ptr.i | 23 +++++++++++++---------- Lib/java/std_unique_ptr.i | 25 +++++++++++++------------ Lib/perl5/std_unique_ptr.i | 13 +++++++------ Lib/python/std_unique_ptr.i | 15 ++++++++------- Lib/ruby/std_unique_ptr.i | 13 +++++++------ 5 files changed, 48 insertions(+), 41 deletions(-) diff --git a/Lib/csharp/std_unique_ptr.i b/Lib/csharp/std_unique_ptr.i index ba13fce4e..2233cdc06 100644 --- a/Lib/csharp/std_unique_ptr.i +++ b/Lib/csharp/std_unique_ptr.i @@ -1,10 +1,11 @@ /* ----------------------------------------------------------------------------- * std_unique_ptr.i * - * The typemaps here allow handling functions returning std::unique_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::unique_ptr. + * Memory ownership is passed from the std::unique_ptr C++ layer to the proxy + * class when returning a std::unique_ptr from a function. + * Memory ownership is passed from the proxy class to the std::unique_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) @@ -18,16 +19,18 @@ %typemap(csin) std::unique_ptr< TYPE > "$typemap(cstype, TYPE).swigRelease($csinput)" %typemap (out) std::unique_ptr< TYPE > %{ - $result = (void *)$1.release(); + $result = (void *)$1.release(); %} + %typemap(csout, excode=SWIGEXCODE) std::unique_ptr< TYPE > { - System.IntPtr cPtr = $imcall; - $typemap(cstype, TYPE) ret = (cPtr == System.IntPtr.Zero) ? null : new $typemap(cstype, TYPE)(cPtr, true);$excode - return ret; - } + System.IntPtr cPtr = $imcall; + $typemap(cstype, TYPE) ret = (cPtr == System.IntPtr.Zero) ? null : new $typemap(cstype, TYPE)(cPtr, true);$excode + return ret; + } + %template() std::unique_ptr< TYPE >; %enddef namespace std { template class unique_ptr {}; -} +} diff --git a/Lib/java/std_unique_ptr.i b/Lib/java/std_unique_ptr.i index 5f4a0c5a2..e1e5a2622 100644 --- a/Lib/java/std_unique_ptr.i +++ b/Lib/java/std_unique_ptr.i @@ -1,10 +1,11 @@ /* ----------------------------------------------------------------------------- * std_unique_ptr.i * - * The typemaps here allow handling functions returning std::unique_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::unique_ptr. + * Memory ownership is passed from the std::unique_ptr C++ layer to the proxy + * class when returning a std::unique_ptr from a function. + * Memory ownership is passed from the proxy class to the std::unique_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) @@ -20,19 +21,19 @@ %typemap(javain) std::unique_ptr< TYPE > "$typemap(jstype, TYPE).swigRelease($javainput)" %typemap (out) std::unique_ptr< TYPE > %{ - jlong lpp = 0; - *(TYPE **) &lpp = $1.release(); - $result = lpp; + jlong lpp = 0; + *(TYPE **) &lpp = $1.release(); + $result = lpp; %} %typemap(javaout) std::unique_ptr< TYPE > { - long cPtr = $jnicall; - return (cPtr == 0) ? null : new $typemap(jstype, TYPE)(cPtr, true); - } + long cPtr = $jnicall; + return (cPtr == 0) ? null : new $typemap(jstype, TYPE)(cPtr, true); + } %template() std::unique_ptr< TYPE >; %enddef namespace std { - template class unique_ptr {}; -} + template class unique_ptr {}; +} diff --git a/Lib/perl5/std_unique_ptr.i b/Lib/perl5/std_unique_ptr.i index a0128ba78..1a7ec06fa 100644 --- a/Lib/perl5/std_unique_ptr.i +++ b/Lib/perl5/std_unique_ptr.i @@ -1,10 +1,11 @@ /* ----------------------------------------------------------------------------- * std_unique_ptr.i * - * The typemaps here allow handling functions returning std::unique_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::unique_ptr. + * Memory ownership is passed from the std::unique_ptr C++ layer to the proxy + * class when returning a std::unique_ptr from a function. + * Memory ownership is passed from the proxy class to the std::unique_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) @@ -21,12 +22,12 @@ } %typemap (out) std::unique_ptr< TYPE > %{ - %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); + %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} %template() std::unique_ptr< TYPE >; %enddef namespace std { - template class unique_ptr {}; + template class unique_ptr {}; } diff --git a/Lib/python/std_unique_ptr.i b/Lib/python/std_unique_ptr.i index bec8bf968..1a7ec06fa 100644 --- a/Lib/python/std_unique_ptr.i +++ b/Lib/python/std_unique_ptr.i @@ -1,10 +1,11 @@ /* ----------------------------------------------------------------------------- * std_unique_ptr.i * - * The typemaps here allow handling functions returning std::unique_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::unique_ptr. + * Memory ownership is passed from the std::unique_ptr C++ layer to the proxy + * class when returning a std::unique_ptr from a function. + * Memory ownership is passed from the proxy class to the std::unique_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) @@ -21,12 +22,12 @@ } %typemap (out) std::unique_ptr< TYPE > %{ - %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); + %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} %template() std::unique_ptr< TYPE >; %enddef namespace std { - template class unique_ptr {}; -} + template class unique_ptr {}; +} diff --git a/Lib/ruby/std_unique_ptr.i b/Lib/ruby/std_unique_ptr.i index a0128ba78..1a7ec06fa 100644 --- a/Lib/ruby/std_unique_ptr.i +++ b/Lib/ruby/std_unique_ptr.i @@ -1,10 +1,11 @@ /* ----------------------------------------------------------------------------- * std_unique_ptr.i * - * The typemaps here allow handling functions returning std::unique_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::unique_ptr. + * Memory ownership is passed from the std::unique_ptr C++ layer to the proxy + * class when returning a std::unique_ptr from a function. + * Memory ownership is passed from the proxy class to the std::unique_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %unique_ptr(TYPE) @@ -21,12 +22,12 @@ } %typemap (out) std::unique_ptr< TYPE > %{ - %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); + %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} %template() std::unique_ptr< TYPE >; %enddef namespace std { - template class unique_ptr {}; + template class unique_ptr {}; } From db5e37a1d7b80304a9c9875b01cfd585070a288a Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Mon, 18 Jul 2022 08:32:26 +0100 Subject: [PATCH 10/11] Add support for std::auto_ptr inputs Ported from std::unique, behaviour is identical with regard to memory ownership/handling --- .../csharp/li_std_auto_ptr_runme.cs | 63 +++++++++++++ .../java/li_std_auto_ptr_runme.java | 70 ++++++++++++++ Examples/test-suite/li_std_auto_ptr.i | 30 +++++- .../test-suite/perl5/li_std_auto_ptr_runme.pl | 60 +++++++++++- .../python/li_std_auto_ptr_runme.py | 65 +++++++++++++ .../test-suite/ruby/li_std_auto_ptr_runme.rb | 91 +++++++++++++++++++ Lib/csharp/std_auto_ptr.i | 31 ++++--- Lib/java/std_auto_ptr.i | 34 ++++--- Lib/perl5/std_auto_ptr.i | 26 ++++-- Lib/python/std_auto_ptr.i | 28 ++++-- Lib/ruby/std_auto_ptr.i | 26 ++++-- 11 files changed, 477 insertions(+), 47 deletions(-) diff --git a/Examples/test-suite/csharp/li_std_auto_ptr_runme.cs b/Examples/test-suite/csharp/li_std_auto_ptr_runme.cs index 863b86701..b804e7ead 100644 --- a/Examples/test-suite/csharp/li_std_auto_ptr_runme.cs +++ b/Examples/test-suite/csharp/li_std_auto_ptr_runme.cs @@ -9,8 +9,71 @@ public class li_std_auto_ptr_runme { System.Threading.Thread.Sleep(10); } + private static void checkCount(int expected_count) + { + int actual_count = Klass.getTotal_count(); + if (actual_count != expected_count) + throw new ApplicationException("Counts incorrect, expected:" + expected_count + " actual:" + actual_count); + } + public static void Main() { + // auto_ptr as input + using (Klass kin = new Klass("KlassInput")) { + checkCount(1); + string s = li_std_auto_ptr.takeKlassAutoPtr(kin); + checkCount(0); + if (s != "KlassInput") + throw new ApplicationException("Incorrect string: " + s); + if (!li_std_auto_ptr.is_nullptr(kin)) + throw new ApplicationException("is_nullptr failed"); + } // Dispose should not fail, even though already deleted + checkCount(0); + + using (Klass kin = new Klass("KlassInput")) { + checkCount(1); + string s = li_std_auto_ptr.takeKlassAutoPtr(kin); + checkCount(0); + if (s != "KlassInput") + throw new ApplicationException("Incorrect string: " + s); + if (!li_std_auto_ptr.is_nullptr(kin)) + throw new ApplicationException("is_nullptr failed"); + bool exception_thrown = false; + try { + li_std_auto_ptr.takeKlassAutoPtr(kin); + } catch (ApplicationException) { + exception_thrown = true; + } + if (!exception_thrown) + throw new ApplicationException("double usage of takeKlassAutoPtr should have been an error"); + } // Dispose should not fail, even though already deleted + checkCount(0); + + using (Klass kin = new Klass("KlassInput")) { + bool exception_thrown = false; + try { + Klass notowned = li_std_auto_ptr.get_not_owned_ptr(kin); + li_std_auto_ptr.takeKlassAutoPtr(notowned); + } catch (ApplicationException) { + exception_thrown = true; + } + if (!exception_thrown) + throw new ApplicationException("Should have thrown 'Cannot release ownership as memory is not owned' error"); + } + checkCount(0); + + using (KlassInheritance kini = new KlassInheritance("KlassInheritanceInput")) { + checkCount(1); + string s = li_std_auto_ptr.takeKlassAutoPtr(kini); + checkCount(0); + if (s != "KlassInheritanceInput") + throw new ApplicationException("Incorrect string: " + s); + if (!li_std_auto_ptr.is_nullptr(kini)) + throw new ApplicationException("is_nullptr failed"); + } // Dispose should not fail, even though already deleted + checkCount(0); + + // auto_ptr as output Klass k1 = li_std_auto_ptr.makeKlassAutoPtr("first"); if (k1.getLabel() != "first") throw new Exception("wrong object label"); diff --git a/Examples/test-suite/java/li_std_auto_ptr_runme.java b/Examples/test-suite/java/li_std_auto_ptr_runme.java index 50ed113a4..40957e974 100644 --- a/Examples/test-suite/java/li_std_auto_ptr_runme.java +++ b/Examples/test-suite/java/li_std_auto_ptr_runme.java @@ -20,8 +20,78 @@ public class li_std_auto_ptr_runme { } } + private static void checkCount(int expected_count) { + int actual_count = Klass.getTotal_count(); + if (actual_count != expected_count) + throw new RuntimeException("Counts incorrect, expected:" + expected_count + " actual:" + actual_count); + } + public static void main(String argv[]) throws Throwable { + // auto_ptr as input + { + Klass kin = new Klass("KlassInput"); + checkCount(1); + String s = li_std_auto_ptr.takeKlassAutoPtr(kin); + checkCount(0); + if (!s.equals("KlassInput")) + throw new RuntimeException("Incorrect string: " + s); + if (!li_std_auto_ptr.is_nullptr(kin)) + throw new RuntimeException("is_nullptr failed"); + kin.delete(); // Should not fail, even though already deleted + checkCount(0); + } + + { + Klass kin = new Klass("KlassInput"); + checkCount(1); + String s = li_std_auto_ptr.takeKlassAutoPtr(kin); + checkCount(0); + if (!s.equals("KlassInput")) + throw new RuntimeException("Incorrect string: " + s); + if (!li_std_auto_ptr.is_nullptr(kin)) + throw new RuntimeException("is_nullptr failed"); + boolean exception_thrown = false; + try { + li_std_auto_ptr.takeKlassAutoPtr(kin); + } catch (RuntimeException e) { + exception_thrown = true; + } + if (!exception_thrown) + throw new RuntimeException("double usage of takeKlassAutoPtr should have been an error"); + kin.delete(); // Should not fail, even though already deleted + checkCount(0); + } + + { + Klass kin = new Klass("KlassInput"); + boolean exception_thrown = false; + try { + Klass notowned = li_std_auto_ptr.get_not_owned_ptr(kin); + li_std_auto_ptr.takeKlassAutoPtr(notowned); + } catch (RuntimeException e) { + exception_thrown = true; + } + if (!exception_thrown) + throw new RuntimeException("Should have thrown 'Cannot release ownership as memory is not owned' error"); + kin.delete(); + checkCount(0); + } + + { + KlassInheritance kini = new KlassInheritance("KlassInheritanceInput"); + checkCount(1); + String s = li_std_auto_ptr.takeKlassAutoPtr(kini); + checkCount(0); + if (!s.equals("KlassInheritanceInput")) + throw new RuntimeException("Incorrect string: " + s); + if (!li_std_auto_ptr.is_nullptr(kini)) + throw new RuntimeException("is_nullptr failed"); + kini.delete(); // Should not fail, even though already deleted + checkCount(0); + } + + // auto_ptr as output Klass k1 = li_std_auto_ptr.makeKlassAutoPtr("first"); if (!k1.getLabel().equals("first")) throw new RuntimeException("wrong object label"); diff --git a/Examples/test-suite/li_std_auto_ptr.i b/Examples/test-suite/li_std_auto_ptr.i index edfe2ccf5..02c8235f2 100644 --- a/Examples/test-suite/li_std_auto_ptr.i +++ b/Examples/test-suite/li_std_auto_ptr.i @@ -14,6 +14,7 @@ #if defined(SWIGCSHARP) || defined(SWIGJAVA) || defined(SWIGPYTHON) || defined(SWIGRUBY) || defined(SWIGPERL) +%include "std_string.i" %include "std_auto_ptr.i" %auto_ptr(Klass) @@ -27,10 +28,13 @@ namespace std { template class auto_ptr { T *ptr; public: - auto_ptr(T *ptr = 0) : ptr(ptr) {} + explicit auto_ptr(T *p = 0) : ptr(p) {} auto_ptr(auto_ptr&& a) : ptr(a.ptr) { a.ptr = 0;} ~auto_ptr() { delete ptr; } T *release() { T *p = ptr; ptr = 0; return p; } + void reset(T *p = 0) { delete ptr; ptr = p; } + T &operator*() const { return *ptr; } + T *operator->() const { return ptr; } auto_ptr& operator=(auto_ptr&& a) { if (&a != this) { delete ptr; ptr = a.ptr; a.ptr = 0; } return *this; } }; } @@ -53,7 +57,7 @@ public: const char* getLabel() const { return m_label.c_str(); } - ~Klass() + virtual ~Klass() { SwigExamples::Lock lock(critical_section); total_count--; @@ -73,10 +77,28 @@ int Klass::total_count = 0; %} -%template(KlassAutoPtr) std::auto_ptr; - %inline %{ +// Virtual inheritance used as this usually results in different values for Klass* and KlassInheritance* +// for testing class inheritance and auto_ptr +struct KlassInheritance : virtual Klass { + KlassInheritance(const char* label) : Klass(label) { + // std::cout << "ptrs.... " << std::hex << (Klass*)this << " " << (KlassInheritance*)this << std::endl; + } +}; + +std::string takeKlassAutoPtr(std::auto_ptr k) { + return std::string(k->getLabel()); +} + +bool is_nullptr(Klass *p) { + return p == 0; +} + +Klass *get_not_owned_ptr(Klass *p) { + return p; +} + std::auto_ptr makeKlassAutoPtr(const char* label) { return std::auto_ptr(new Klass(label)); } diff --git a/Examples/test-suite/perl5/li_std_auto_ptr_runme.pl b/Examples/test-suite/perl5/li_std_auto_ptr_runme.pl index 52cb23ac5..8a33f4562 100644 --- a/Examples/test-suite/perl5/li_std_auto_ptr_runme.pl +++ b/Examples/test-suite/perl5/li_std_auto_ptr_runme.pl @@ -1,9 +1,67 @@ use strict; use warnings; -use Test::More tests => 6; +use Test::More tests => 24; BEGIN { use_ok('li_std_auto_ptr') } require_ok('li_std_auto_ptr'); +# adapted from ../java/li_std_auto_ptr_runme.java + +sub checkCount { + my($expected_count) = @_; + my $actual_count = li_std_auto_ptr::Klass::getTotal_count(); + is($actual_count, $expected_count, "Counts incorrect, expected: $expected_count actual: $actual_count"); +} + +# auto_ptr as input +{ + my $kin = new li_std_auto_ptr::Klass("KlassInput"); + checkCount(1); + my $s = li_std_auto_ptr::takeKlassAutoPtr($kin); + checkCount(0); + is($s, "KlassInput", "Incorrect string: $s"); + is(li_std_auto_ptr::is_nullptr($kin), 1, "is_nullptr check"); + undef $kin; # Should not fail, even though already deleted + checkCount(0); +} + +{ + my $kin = new li_std_auto_ptr::Klass("KlassInput"); + checkCount(1); + my $s = li_std_auto_ptr::takeKlassAutoPtr($kin); + checkCount(0); + is($s, "KlassInput", "Incorrect string: $s"); + is(li_std_auto_ptr::is_nullptr($kin), 1, "is_nullptr check"); + eval { + li_std_auto_ptr::takeKlassAutoPtr($kin); + }; + like($@, qr/\bcannot release ownership as memory is not owned\b/, "double usage of takeKlassAutoPtr should be an error"); + undef $kin; # Should not fail, even though already deleted + checkCount(0); +} + +{ + my $kin = new li_std_auto_ptr::Klass("KlassInput"); + eval { + my $notowned = li_std_auto_ptr::get_not_owned_ptr($kin); + li_std_auto_ptr::takeKlassAutoPtr($notowned); + }; + like($@, qr/\bcannot release ownership as memory is not owned\b/, "double usage of takeKlassAutoPtr should be an error"); + undef $kin; + checkCount(0); +} + +{ + my $kini = new li_std_auto_ptr::KlassInheritance("KlassInheritanceInput"); + checkCount(1); + my $s = li_std_auto_ptr::takeKlassAutoPtr($kini); + checkCount(0); + is($s, "KlassInheritanceInput", "Incorrect string: $s"); + is(li_std_auto_ptr::is_nullptr($kini), 1, "is_nullptr failed"); + undef $kini; # Should not fail, even though already deleted + checkCount(0); +} + +# auto_ptr as output my $k1 = li_std_auto_ptr::makeKlassAutoPtr("first"); my $k2 = li_std_auto_ptr::makeKlassAutoPtr("second"); is(li_std_auto_ptr::Klass::getTotal_count, 2, "have 2 pointers"); diff --git a/Examples/test-suite/python/li_std_auto_ptr_runme.py b/Examples/test-suite/python/li_std_auto_ptr_runme.py index d62224ff6..3f1d392ea 100644 --- a/Examples/test-suite/python/li_std_auto_ptr_runme.py +++ b/Examples/test-suite/python/li_std_auto_ptr_runme.py @@ -1,5 +1,70 @@ from li_std_auto_ptr import * +def checkCount(expected_count): + actual_count = Klass.getTotal_count() + if (actual_count != expected_count): + raise RuntimeError("Counts incorrect, expected:" + expected_count + " actual:" + actual_count) + +# auto_ptr as input +kin = Klass("KlassInput") +checkCount(1) +s = takeKlassAutoPtr(kin) +checkCount(0) +if kin.thisown: + raise RuntimeError("thisown should be false") +if s != "KlassInput": + raise RuntimeError("Incorrect string: " + s) +if not is_nullptr(kin): + raise RuntimeError("is_nullptr failed") +del kin # Should not fail, even though already deleted +checkCount(0) + +kin = Klass("KlassInput") +checkCount(1) +s = takeKlassAutoPtr(kin) +checkCount(0) +if kin.thisown: + raise RuntimeError("thisown should be false") +if s != "KlassInput": + raise RuntimeError("Incorrect string: " + s) +if not is_nullptr(kin): + raise RuntimeError("is_nullptr failed") +exception_thrown = False +try: + s = takeKlassAutoPtr(kin) +except RuntimeError as e: + exception_thrown = True +if not exception_thrown: + raise RuntimeError("double usage of takeKlassAutoPtr should have been an error") +del kin # Should not fail, even though already deleted +checkCount(0) + +kin = Klass("KlassInput") +exception_thrown = False +try: + notowned = get_not_owned_ptr(kin) + takeKlassAutoPtr(notowned) +except RuntimeError as e: + exception_thrown = True +if not exception_thrown: + raise RuntimeError("Should have thrown 'Cannot release ownership as memory is not owned' error") +del kin +checkCount(0) + +kini = KlassInheritance("KlassInheritanceInput") +checkCount(1) +s = takeKlassAutoPtr(kini) +checkCount(0) +if kini.thisown: + raise RuntimeError("thisown should be false") +if s != "KlassInheritanceInput": + raise RuntimeError("Incorrect string: " + s) +if not is_nullptr(kini): + raise RuntimeError("is_nullptr failed") +del kini # Should not fail, even though already deleted +checkCount(0) + +# auto_ptr as output k1 = makeKlassAutoPtr("first") k2 = makeKlassAutoPtr("second") if Klass.getTotal_count() != 2: diff --git a/Examples/test-suite/ruby/li_std_auto_ptr_runme.rb b/Examples/test-suite/ruby/li_std_auto_ptr_runme.rb index cec48a58c..14e0bd9d4 100644 --- a/Examples/test-suite/ruby/li_std_auto_ptr_runme.rb +++ b/Examples/test-suite/ruby/li_std_auto_ptr_runme.rb @@ -11,6 +11,97 @@ def gc_check(expected_count) # swig_assert_equal_simple(expected_count, Li_std_auto_ptr::Klass::getTotal_count()) end +def checkCount(expected_count) + actual_count = Li_std_auto_ptr::Klass.getTotal_count() + if (actual_count != expected_count) + raise RuntimeError, "Counts incorrect, expected:" + expected_count + " actual:" + actual_count + end +end + + +# auto_ptr as input +kin = Li_std_auto_ptr::Klass.new("KlassInput") +checkCount(1) +s = Li_std_auto_ptr.takeKlassAutoPtr(kin) +checkCount(0) +if (s != "KlassInput") + raise RuntimeError, "Incorrect string: " + s +end +exception_thrown = false +begin + Li_std_auto_ptr.is_nullptr(kin) +rescue ObjectPreviouslyDeleted + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "is_nullptr failed to throw" +end +kin = nil +checkCount(0) + +kin = Li_std_auto_ptr::Klass.new("KlassInput") +checkCount(1) +s = Li_std_auto_ptr.takeKlassAutoPtr(kin) +checkCount(0) +if (s != "KlassInput") + raise RuntimeError, "Incorrect string: " + s +end +exception_thrown = false +begin + Li_std_auto_ptr.is_nullptr(kin) +rescue ObjectPreviouslyDeleted + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "is_nullptr failed to throw" +end +exception_thrown = false +begin + Li_std_auto_ptr.takeKlassAutoPtr(kin) +rescue RuntimeError => e + # puts e.message + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "double usage of takeKlassAutoPtr should have been an error" +end +kin = nil +checkCount(0) + +kin = Li_std_auto_ptr::Klass.new("KlassInput") +exception_thrown = false +begin + notowned = Li_std_auto_ptr::get_not_owned_ptr(kin) + Li_std_auto_ptr::takeKlassAutoPtr(notowned) +rescue RuntimeError + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "Should have thrown 'Cannot release ownership as memory is not owned' error" +end +Li_std_auto_ptr.takeKlassAutoPtr(kin) # Ensure object is deleted (can't rely on GC) +checkCount(0) + +kini = Li_std_auto_ptr::KlassInheritance.new("KlassInheritanceInput") +checkCount(1) +s = Li_std_auto_ptr.takeKlassAutoPtr(kini) +checkCount(0) +if (s != "KlassInheritanceInput") + raise RuntimeError, "Incorrect string: " + s +end +exception_thrown = false +begin + Li_std_auto_ptr.is_nullptr(kini) +rescue ObjectPreviouslyDeleted + exception_thrown = true +end +if (!exception_thrown) + raise RuntimeError, "is_nullptr failed to throw" +end +kini = nil +checkCount(0) + +# auto_ptr as output k1 = Li_std_auto_ptr::makeKlassAutoPtr("first") k2 = Li_std_auto_ptr::makeKlassAutoPtr("second") swig_assert_equal_simple(2, Li_std_auto_ptr::Klass::getTotal_count()) diff --git a/Lib/csharp/std_auto_ptr.i b/Lib/csharp/std_auto_ptr.i index b1ec3d569..78d7eaf4c 100644 --- a/Lib/csharp/std_auto_ptr.i +++ b/Lib/csharp/std_auto_ptr.i @@ -1,27 +1,36 @@ /* ----------------------------------------------------------------------------- * std_auto_ptr.i * - * The typemaps here allow handling functions returning std::auto_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::auto_ptr. + * Memory ownership is passed from the std::auto_ptr C++ layer to the proxy + * class when returning a std::auto_ptr from a function. + * Memory ownership is passed from the proxy class to the std::auto_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %auto_ptr(TYPE) %typemap (ctype) std::auto_ptr< TYPE > "void *" %typemap (imtype, out="System.IntPtr") std::auto_ptr< TYPE > "global::System.Runtime.InteropServices.HandleRef" %typemap (cstype) std::auto_ptr< TYPE > "$typemap(cstype, TYPE)" + +%typemap(in) std::auto_ptr< TYPE > +%{ $1.reset((TYPE *)$input); %} + +%typemap(csin) std::auto_ptr< TYPE > "$typemap(cstype, TYPE).swigRelease($csinput)" + %typemap (out) std::auto_ptr< TYPE > %{ - $result = (void *)$1.release(); + $result = (void *)$1.release(); %} + %typemap(csout, excode=SWIGEXCODE) std::auto_ptr< TYPE > { - System.IntPtr cPtr = $imcall; - $typemap(cstype, TYPE) ret = (cPtr == System.IntPtr.Zero) ? null : new $typemap(cstype, TYPE)(cPtr, true);$excode - return ret; - } + System.IntPtr cPtr = $imcall; + $typemap(cstype, TYPE) ret = (cPtr == System.IntPtr.Zero) ? null : new $typemap(cstype, TYPE)(cPtr, true);$excode + return ret; + } + %template() std::auto_ptr< TYPE >; %enddef namespace std { - template class auto_ptr {}; -} + template class auto_ptr {}; +} diff --git a/Lib/java/std_auto_ptr.i b/Lib/java/std_auto_ptr.i index 0eb5fe155..6d65f0451 100644 --- a/Lib/java/std_auto_ptr.i +++ b/Lib/java/std_auto_ptr.i @@ -1,29 +1,39 @@ /* ----------------------------------------------------------------------------- * std_auto_ptr.i * - * The typemaps here allow handling functions returning std::auto_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::auto_ptr. + * Memory ownership is passed from the std::auto_ptr C++ layer to the proxy + * class when returning a std::auto_ptr from a function. + * Memory ownership is passed from the proxy class to the std::auto_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %auto_ptr(TYPE) + %typemap (jni) std::auto_ptr< TYPE > "jlong" %typemap (jtype) std::auto_ptr< TYPE > "long" %typemap (jstype) std::auto_ptr< TYPE > "$typemap(jstype, TYPE)" +%typemap(in) std::auto_ptr< TYPE > (TYPE *auto_temp) +%{ auto_temp = *(TYPE **)&$input; + $1.reset(auto_temp); %} + +%typemap(javain) std::auto_ptr< TYPE > "$typemap(jstype, TYPE).swigRelease($javainput)" + %typemap (out) std::auto_ptr< TYPE > %{ - jlong lpp = 0; - *(TYPE **) &lpp = $1.release(); - $result = lpp; + jlong lpp = 0; + *(TYPE **) &lpp = $1.release(); + $result = lpp; %} + %typemap(javaout) std::auto_ptr< TYPE > { - long cPtr = $jnicall; - return (cPtr == 0) ? null : new $typemap(jstype, TYPE)(cPtr, true); - } + long cPtr = $jnicall; + return (cPtr == 0) ? null : new $typemap(jstype, TYPE)(cPtr, true); + } + %template() std::auto_ptr< TYPE >; %enddef namespace std { - template class auto_ptr {}; -} + template class auto_ptr {}; +} diff --git a/Lib/perl5/std_auto_ptr.i b/Lib/perl5/std_auto_ptr.i index ecaea2b0f..d062886e4 100644 --- a/Lib/perl5/std_auto_ptr.i +++ b/Lib/perl5/std_auto_ptr.i @@ -1,19 +1,33 @@ /* ----------------------------------------------------------------------------- * std_auto_ptr.i * - * The typemaps here allow handling functions returning std::auto_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::auto_ptr. + * Memory ownership is passed from the std::auto_ptr C++ layer to the proxy + * class when returning a std::auto_ptr from a function. + * Memory ownership is passed from the proxy class to the std::auto_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %auto_ptr(TYPE) +%typemap(in, noblock=1) std::auto_ptr< TYPE > (void *argp = 0, int res = 0) { + res = SWIG_ConvertPtr($input, &argp, $descriptor(TYPE *), SWIG_POINTER_RELEASE | %convertptr_flags); + if (!SWIG_IsOK(res)) { + if (res == SWIG_ERROR_RELEASE_NOT_OWNED) { + %releasenotowned_fail(res, "TYPE *", $symname, $argnum); + } else { + %argument_fail(res, "TYPE *", $symname, $argnum); + } + } + $1.reset((TYPE *)argp); +} + %typemap (out) std::auto_ptr< TYPE > %{ - %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); + %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} + %template() std::auto_ptr< TYPE >; %enddef namespace std { - template class auto_ptr {}; + template class auto_ptr {}; } diff --git a/Lib/python/std_auto_ptr.i b/Lib/python/std_auto_ptr.i index fb044f851..d062886e4 100644 --- a/Lib/python/std_auto_ptr.i +++ b/Lib/python/std_auto_ptr.i @@ -1,19 +1,33 @@ /* ----------------------------------------------------------------------------- * std_auto_ptr.i * - * The typemaps here allow handling functions returning std::auto_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::auto_ptr. + * Memory ownership is passed from the std::auto_ptr C++ layer to the proxy + * class when returning a std::auto_ptr from a function. + * Memory ownership is passed from the proxy class to the std::auto_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %auto_ptr(TYPE) +%typemap(in, noblock=1) std::auto_ptr< TYPE > (void *argp = 0, int res = 0) { + res = SWIG_ConvertPtr($input, &argp, $descriptor(TYPE *), SWIG_POINTER_RELEASE | %convertptr_flags); + if (!SWIG_IsOK(res)) { + if (res == SWIG_ERROR_RELEASE_NOT_OWNED) { + %releasenotowned_fail(res, "TYPE *", $symname, $argnum); + } else { + %argument_fail(res, "TYPE *", $symname, $argnum); + } + } + $1.reset((TYPE *)argp); +} + %typemap (out) std::auto_ptr< TYPE > %{ - %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); + %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} + %template() std::auto_ptr< TYPE >; %enddef namespace std { - template class auto_ptr {}; -} + template class auto_ptr {}; +} diff --git a/Lib/ruby/std_auto_ptr.i b/Lib/ruby/std_auto_ptr.i index ecaea2b0f..d062886e4 100644 --- a/Lib/ruby/std_auto_ptr.i +++ b/Lib/ruby/std_auto_ptr.i @@ -1,19 +1,33 @@ /* ----------------------------------------------------------------------------- * std_auto_ptr.i * - * The typemaps here allow handling functions returning std::auto_ptr<>, - * which is the most common use of this type. If you have functions taking it - * as parameter, these typemaps can't be used for them and you need to do - * something else (e.g. use shared_ptr<> which SWIG supports fully). + * SWIG library file for handling std::auto_ptr. + * Memory ownership is passed from the std::auto_ptr C++ layer to the proxy + * class when returning a std::auto_ptr from a function. + * Memory ownership is passed from the proxy class to the std::auto_ptr in the + * C++ layer when passed as a parameter to a wrapped function. * ----------------------------------------------------------------------------- */ %define %auto_ptr(TYPE) +%typemap(in, noblock=1) std::auto_ptr< TYPE > (void *argp = 0, int res = 0) { + res = SWIG_ConvertPtr($input, &argp, $descriptor(TYPE *), SWIG_POINTER_RELEASE | %convertptr_flags); + if (!SWIG_IsOK(res)) { + if (res == SWIG_ERROR_RELEASE_NOT_OWNED) { + %releasenotowned_fail(res, "TYPE *", $symname, $argnum); + } else { + %argument_fail(res, "TYPE *", $symname, $argnum); + } + } + $1.reset((TYPE *)argp); +} + %typemap (out) std::auto_ptr< TYPE > %{ - %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); + %set_output(SWIG_NewPointerObj($1.release(), $descriptor(TYPE *), SWIG_POINTER_OWN | %newpointer_flags)); %} + %template() std::auto_ptr< TYPE >; %enddef namespace std { - template class auto_ptr {}; + template class auto_ptr {}; } From 1b63af0f2c3d28bbaaa8856d7046b0b22c898609 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Tue, 19 Jul 2022 08:57:47 +0100 Subject: [PATCH 11/11] std::unique_ptr std::auto_ptr tidyup Add docs on additional support Additional testing for invalid usage for parameter inputs --- CHANGES.current | 8 ++ Doc/Manual/Library.html | 73 ++++++++++++++++--- .../csharp/cpp11_std_unique_ptr_runme.cs | 14 ++-- .../csharp/li_std_auto_ptr_runme.cs | 14 ++-- .../java/cpp11_std_unique_ptr_runme.java | 4 + .../java/li_std_auto_ptr_runme.java | 4 + .../python/cpp11_std_unique_ptr_runme.py | 2 + .../python/li_std_auto_ptr_runme.py | 2 + .../ruby/cpp11_std_unique_ptr_runme.rb | 5 +- .../test-suite/ruby/li_std_auto_ptr_runme.rb | 3 + 10 files changed, 107 insertions(+), 22 deletions(-) diff --git a/CHANGES.current b/CHANGES.current index e312abaaa..3ed3a94a5 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,14 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.1.0 (in progress) =========================== +2022-07-19: wsfulton + #692 [C#, Java, Perl, Python, Ruby] std::unique_ptr and std::auto_ptr typemaps + provided for inputs types in std_unique_ptr.i and std_auto_ptr.i. + + Now these smart pointers can be used as input parameters to functions. A proxy + class instance transfers memory ownership of the underlying C++ object from the + proxy class to a smart pointer instance passed to the wrapped function. + 2022-07-12: wsfulton Performance optimisation for parameters passed by value that are C++11 movable. The C++ wrappers create a temporary variable for a parameter to be passed to a diff --git a/Doc/Manual/Library.html b/Doc/Manual/Library.html index b18ecc957..7dc649c16 100644 --- a/Doc/Manual/Library.html +++ b/Doc/Manual/Library.html @@ -2055,13 +2055,9 @@ equivalent %shared_ptr(T) macro covered in the previous section.

-Note that the support provided is limited to returning this smart pointer from a function. -Any other use of std::auto_ptr is not directly provided yet. +Example usage of a std::unique_ptr being returned from a function is shown below.

-

-Example usage would be -

 %include <std_unique_ptr.i>
@@ -2113,12 +2109,67 @@ Note that the implementation is quite different to the std::shared_ptr
 where the proxy class manages the underlying C++ memory as a pointer to a shared_ptr instead of a plain raw pointer.
 

+

+A possibly less common usage of this smart pointer is as a parameter to a function. +When used like this it indicates that memory usage of the object pointed to by the underlying pointer +is transferred to the function being called. +The code that SWIG generates assumes this happens. +First, it is assumed that a proxy class already owns the underlying C++ object and is used to pass the object to the C++ function being called. +Second, the ownership is transferred from the proxy class to the C++ function being called and +lifetime is then controlled by the function. +Finally, it is assumed the lifetime of the object may not last beyond returning from the C++ function +and hence the proxy class can no longer be used. +

+ +

+Consider expanding the example above with a function that takes a std::unique_ptr as follows: +

+ +
+
+void take(std::unique_ptr);
+
+
+ +

+and use from C#: +

+ +
+
+Klass k = Klass.Create(17); // create an instance of Klass any way you like
+int value = k.getValue();   // ok
+example.take(k);            // memory ownership passes from C# layer to C++ layer
+int v = k.getValue();       // don't do this - invalid use of k
+
+
+ +

+Attempts to use k after the ownership has been passed into the take function +should not be attempted. +The implementation sets the proxy class to an invalid state by setting the class's underlying +C++ pointer to null after the return from the take function. +Subsequent use of an invalid proxy class instance is very much dependent on the implementation +in the target language and ranges from a segfault to giving a nice error. +Consider implementing additional checks via the 'check' typemap. +

+ +

+Attempts to pass ownership from a proxy class to a std::unique parameter more than once will result +in a "Cannot release ownership as memory is not owned" exception. For example, if example.take(k) in the example above is called twice. +

+ +

+Compatibility note: Support for std::unique_ptr was added in SWIG-4.1.0. +

+

12.4.6 auto_ptr smart pointer

While std::auto_ptr is deprecated in C++11, some existing code may -still be using it, so SWIG provides limited support for this class by some target languages. +still be using it. SWIG provides support for this class which is nearly identical +to std::unique_ptr.

@@ -2133,13 +2184,9 @@ the previous two sections.

-Note that the support provided is limited to returning this smart pointer from a function. -Any other use of std::auto_ptr is not directly provided. +Example usage of a std::auto_ptr being returned from a function is shown below.

-

-Example usage would be -

 %include <std_auto_ptr.i>
@@ -2181,6 +2228,10 @@ The implementation simply calls std::auto_ptr::release() to obtain the
 That is, it works the same way covered in the previous section for std::unique_ptr.
 

+

+Input parameters also work the same way as std::unique_ptr covered in the previous section. +

+

12.5 Utility Libraries

diff --git a/Examples/test-suite/csharp/cpp11_std_unique_ptr_runme.cs b/Examples/test-suite/csharp/cpp11_std_unique_ptr_runme.cs index 5b730aa05..09c6ec0af 100644 --- a/Examples/test-suite/csharp/cpp11_std_unique_ptr_runme.cs +++ b/Examples/test-suite/csharp/cpp11_std_unique_ptr_runme.cs @@ -41,7 +41,9 @@ public class cpp11_std_unique_ptr_runme { bool exception_thrown = false; try { cpp11_std_unique_ptr.takeKlassUniquePtr(kin); - } catch (ApplicationException) { + } catch (ApplicationException e) { + if (!e.Message.Contains("Cannot release ownership as memory is not owned")) + throw new ApplicationException("incorrect exception message"); exception_thrown = true; } if (!exception_thrown) @@ -52,10 +54,12 @@ public class cpp11_std_unique_ptr_runme { using (Klass kin = new Klass("KlassInput")) { bool exception_thrown = false; try { - Klass notowned = cpp11_std_unique_ptr.get_not_owned_ptr(kin); - cpp11_std_unique_ptr.takeKlassUniquePtr(notowned); - } catch (ApplicationException) { - exception_thrown = true; + Klass notowned = cpp11_std_unique_ptr.get_not_owned_ptr(kin); + cpp11_std_unique_ptr.takeKlassUniquePtr(notowned); + } catch (ApplicationException e) { + if (!e.Message.Contains("Cannot release ownership as memory is not owned")) + throw new ApplicationException("incorrect exception message"); + exception_thrown = true; } if (!exception_thrown) throw new ApplicationException("Should have thrown 'Cannot release ownership as memory is not owned' error"); diff --git a/Examples/test-suite/csharp/li_std_auto_ptr_runme.cs b/Examples/test-suite/csharp/li_std_auto_ptr_runme.cs index b804e7ead..b5ff1fd29 100644 --- a/Examples/test-suite/csharp/li_std_auto_ptr_runme.cs +++ b/Examples/test-suite/csharp/li_std_auto_ptr_runme.cs @@ -41,7 +41,9 @@ public class li_std_auto_ptr_runme { bool exception_thrown = false; try { li_std_auto_ptr.takeKlassAutoPtr(kin); - } catch (ApplicationException) { + } catch (ApplicationException e) { + if (!e.Message.Contains("Cannot release ownership as memory is not owned")) + throw new ApplicationException("incorrect exception message"); exception_thrown = true; } if (!exception_thrown) @@ -52,10 +54,12 @@ public class li_std_auto_ptr_runme { using (Klass kin = new Klass("KlassInput")) { bool exception_thrown = false; try { - Klass notowned = li_std_auto_ptr.get_not_owned_ptr(kin); - li_std_auto_ptr.takeKlassAutoPtr(notowned); - } catch (ApplicationException) { - exception_thrown = true; + Klass notowned = li_std_auto_ptr.get_not_owned_ptr(kin); + li_std_auto_ptr.takeKlassAutoPtr(notowned); + } catch (ApplicationException e) { + if (!e.Message.Contains("Cannot release ownership as memory is not owned")) + throw new ApplicationException("incorrect exception message"); + exception_thrown = true; } if (!exception_thrown) throw new ApplicationException("Should have thrown 'Cannot release ownership as memory is not owned' error"); diff --git a/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java b/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java index 14c117206..36a9c03fc 100644 --- a/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java +++ b/Examples/test-suite/java/cpp11_std_unique_ptr_runme.java @@ -55,6 +55,8 @@ public class cpp11_std_unique_ptr_runme { try { cpp11_std_unique_ptr.takeKlassUniquePtr(kin); } catch (RuntimeException e) { + if (!e.getMessage().contains("Cannot release ownership as memory is not owned")) + throw new RuntimeException("incorrect exception message"); exception_thrown = true; } if (!exception_thrown) @@ -70,6 +72,8 @@ public class cpp11_std_unique_ptr_runme { Klass notowned = cpp11_std_unique_ptr.get_not_owned_ptr(kin); cpp11_std_unique_ptr.takeKlassUniquePtr(notowned); } catch (RuntimeException e) { + if (!e.getMessage().contains("Cannot release ownership as memory is not owned")) + throw new RuntimeException("incorrect exception message"); exception_thrown = true; } if (!exception_thrown) diff --git a/Examples/test-suite/java/li_std_auto_ptr_runme.java b/Examples/test-suite/java/li_std_auto_ptr_runme.java index 40957e974..daec84dc0 100644 --- a/Examples/test-suite/java/li_std_auto_ptr_runme.java +++ b/Examples/test-suite/java/li_std_auto_ptr_runme.java @@ -55,6 +55,8 @@ public class li_std_auto_ptr_runme { try { li_std_auto_ptr.takeKlassAutoPtr(kin); } catch (RuntimeException e) { + if (!e.getMessage().contains("Cannot release ownership as memory is not owned")) + throw new RuntimeException("incorrect exception message"); exception_thrown = true; } if (!exception_thrown) @@ -70,6 +72,8 @@ public class li_std_auto_ptr_runme { Klass notowned = li_std_auto_ptr.get_not_owned_ptr(kin); li_std_auto_ptr.takeKlassAutoPtr(notowned); } catch (RuntimeException e) { + if (!e.getMessage().contains("Cannot release ownership as memory is not owned")) + throw new RuntimeException("incorrect exception message"); exception_thrown = true; } if (!exception_thrown) diff --git a/Examples/test-suite/python/cpp11_std_unique_ptr_runme.py b/Examples/test-suite/python/cpp11_std_unique_ptr_runme.py index d102ebcd8..d317754b4 100644 --- a/Examples/test-suite/python/cpp11_std_unique_ptr_runme.py +++ b/Examples/test-suite/python/cpp11_std_unique_ptr_runme.py @@ -33,6 +33,8 @@ exception_thrown = False try: s = takeKlassUniquePtr(kin) except RuntimeError as e: + if "cannot release ownership as memory is not owned" not in str(e): + raise RuntimeError("incorrect exception message"); exception_thrown = True if not exception_thrown: raise RuntimeError("double usage of takeKlassUniquePtr should have been an error") diff --git a/Examples/test-suite/python/li_std_auto_ptr_runme.py b/Examples/test-suite/python/li_std_auto_ptr_runme.py index 3f1d392ea..c1df7eb76 100644 --- a/Examples/test-suite/python/li_std_auto_ptr_runme.py +++ b/Examples/test-suite/python/li_std_auto_ptr_runme.py @@ -33,6 +33,8 @@ exception_thrown = False try: s = takeKlassAutoPtr(kin) except RuntimeError as e: + if "cannot release ownership as memory is not owned" not in str(e): + raise RuntimeError("incorrect exception message"); exception_thrown = True if not exception_thrown: raise RuntimeError("double usage of takeKlassAutoPtr should have been an error") diff --git a/Examples/test-suite/ruby/cpp11_std_unique_ptr_runme.rb b/Examples/test-suite/ruby/cpp11_std_unique_ptr_runme.rb index ed9138bcd..83bac5f1b 100644 --- a/Examples/test-suite/ruby/cpp11_std_unique_ptr_runme.rb +++ b/Examples/test-suite/ruby/cpp11_std_unique_ptr_runme.rb @@ -73,7 +73,10 @@ exception_thrown = false begin notowned = Cpp11_std_unique_ptr::get_not_owned_ptr(kin) Cpp11_std_unique_ptr::takeKlassUniquePtr(notowned) -rescue RuntimeError +rescue RuntimeError => e + if (!e.to_s.include? "cannot release ownership as memory is not owned") + raise RuntimeError, "incorrect exception message" + end exception_thrown = true end if (!exception_thrown) diff --git a/Examples/test-suite/ruby/li_std_auto_ptr_runme.rb b/Examples/test-suite/ruby/li_std_auto_ptr_runme.rb index 14e0bd9d4..6562d8d84 100644 --- a/Examples/test-suite/ruby/li_std_auto_ptr_runme.rb +++ b/Examples/test-suite/ruby/li_std_auto_ptr_runme.rb @@ -74,6 +74,9 @@ begin notowned = Li_std_auto_ptr::get_not_owned_ptr(kin) Li_std_auto_ptr::takeKlassAutoPtr(notowned) rescue RuntimeError + if (!e.to_s.include? "cannot release ownership as memory is not owned") + raise RuntimeError, "incorrect exception message" + end exception_thrown = true end if (!exception_thrown)