summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam S Fulton <wsf@fultondesigns.co.uk>2022-09-16 08:36:25 +0100
committerWilliam S Fulton <wsf@fultondesigns.co.uk>2022-09-16 08:36:25 +0100
commitdad7c93ca0a923e7d2671347cee711d180dd0338 (patch)
treee4f527a56b5a1e044213ebe1f07200cedcde8153
parentde65875955b236ee2644014e41bdeb1d574acc93 (diff)
downloadswig-dad7c93ca0a923e7d2671347cee711d180dd0338.tar.gz
Provide SWIGTYPE MOVE typemaps in swigmove.i
For implementing full move semantics when passing parameters by value. Based on SWIGTYPE && and std::unique_ptr typemaps which implement move semantics. Added for all languages, but untested for: Go, Ocaml, R, Scilab (and unlikely to be fully functional for same reasons as for std::unique_ptr support). Issue #999
-rw-r--r--CHANGES.current4
-rw-r--r--Doc/Manual/CPlusPlus11.html194
-rw-r--r--Doc/Manual/Contents.html2
-rw-r--r--Examples/test-suite/common.mk5
-rw-r--r--Examples/test-suite/cpp11_move_only.i23
-rw-r--r--Examples/test-suite/cpp11_move_typemaps.i12
-rw-r--r--Examples/test-suite/csharp/cpp11_move_typemaps_runme.cs37
-rw-r--r--Examples/test-suite/d/cpp11_move_typemaps_runme.1.d42
-rw-r--r--Examples/test-suite/d/cpp11_move_typemaps_runme.2.d42
-rw-r--r--Examples/test-suite/guile/cpp11_move_typemaps_runme.scm3
-rw-r--r--Examples/test-suite/java/cpp11_move_typemaps_runme.java51
-rw-r--r--Examples/test-suite/javascript/cpp11_move_typemaps_runme.js30
-rw-r--r--Examples/test-suite/lua/cpp11_move_typemaps_runme.lua28
-rw-r--r--Examples/test-suite/mzscheme/cpp11_move_typemaps_runme.scm35
-rw-r--r--Examples/test-suite/octave/cpp11_move_typemaps_runme.m37
-rw-r--r--Examples/test-suite/perl5/cpp11_move_typemaps_runme.pl34
-rw-r--r--Examples/test-suite/php/cpp11_move_typemaps_runme.php32
-rw-r--r--Examples/test-suite/python/cpp11_move_typemaps_runme.py29
-rw-r--r--Examples/test-suite/ruby/cpp11_move_typemaps_runme.rb36
-rw-r--r--Examples/test-suite/schemerunme/cpp11_move_typemaps.scm23
-rw-r--r--Examples/test-suite/tcl/cpp11_move_typemaps_runme.tcl35
-rw-r--r--Lib/csharp/swigmove.i16
-rw-r--r--Lib/d/swigmove.i16
-rw-r--r--Lib/go/swigmove.i15
-rw-r--r--Lib/guile/swigmove.i19
-rw-r--r--Lib/java/swigmove.i16
-rw-r--r--Lib/javascript/jsc/swigmove.i1
-rw-r--r--Lib/javascript/v8/swigmove.i1
-rw-r--r--Lib/lua/swigmove.i18
-rw-r--r--Lib/mzscheme/swigmove.i19
-rw-r--r--Lib/ocaml/swigmove.i11
-rw-r--r--Lib/octave/swigmove.i1
-rw-r--r--Lib/perl5/swigmove.i1
-rw-r--r--Lib/php/swigmove.i24
-rw-r--r--Lib/python/swigmove.i1
-rw-r--r--Lib/r/swigmove.i1
-rw-r--r--Lib/ruby/swigmove.i1
-rw-r--r--Lib/scilab/swigmove.i1
-rw-r--r--Lib/swig.swg12
-rw-r--r--Lib/tcl/swigmove.i1
-rw-r--r--Lib/typemaps/swigmove.swg19
41 files changed, 909 insertions, 19 deletions
diff --git a/CHANGES.current b/CHANGES.current
index cd985e863..d18afadd1 100644
--- a/CHANGES.current
+++ b/CHANGES.current
@@ -7,6 +7,10 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.1.0 (in progress)
===========================
+2022-09-16: wsfulton
+ #999 Provide SWIGTYPE MOVE typemaps in swigmove.i for implementing full
+ move semantics when passing parameters by value.
+
2022-08-31: wsfulton
#999 Improve move semantics when using rvalue references.
The SWIGTYPE && input typemaps now assume the object has been moved.
diff --git a/Doc/Manual/CPlusPlus11.html b/Doc/Manual/CPlusPlus11.html
index ba60fd3f8..861b80048 100644
--- a/Doc/Manual/CPlusPlus11.html
+++ b/Doc/Manual/CPlusPlus11.html
@@ -18,7 +18,7 @@
<ul>
<li><a href="#CPlusPlus11_rvalue_reference_inputs">Rvalue reference inputs</a>
<li><a href="#CPlusPlus11_rvalue_reference_outputs">Rvalue reference outputs</a>
-<li><a href="#CPlusPlus11_move_only">Movable and move-only types</a>
+<li><a href="#CPlusPlus11_move_only">Movable and move-only types by value</a>
</ul>
<li><a href="#CPlusPlus11_generalized_constant_expressions">Generalized constant expressions</a>
<li><a href="#CPlusPlus11_extern_template">Extern template</a>
@@ -240,7 +240,7 @@ Another alternative would be to modify the output rvalue reference typemap to al
Fortunately you're highly unlikely to have to solve any of these issues!
</p>
-<H4><a name="CPlusPlus11_move_only">7.2.1.3 Movable and move-only types</a></H4>
+<H4><a name="CPlusPlus11_move_only">7.2.1.3 Movable and move-only types by value</a></H4>
<p>
@@ -252,7 +252,10 @@ Movable types can appear in function signatures for passing 'by value' and in C+
</p>
<p>
-SWIG has been enhanced with support for both copyable and/or movable types but this is currently just for function return values.
+SWIG has support for both copyable and/or movable types.
+Support for move semantics is quite seamless when returning by value from a function.
+Support for move semantics is less so and may require some customisation when passing by value to a function.
+First let's consider returning by value from a function.
</p>
<p>
@@ -283,6 +286,7 @@ struct MoveOnly {
MoveOnly &amp; operator=(MoveOnly &amp;&amp;) = default;
static MoveOnly create() { return MoveOnly(); }
+ static void take(MoveOnly mo);
};
</pre></div>
@@ -303,17 +307,193 @@ SWIGEXPORT void * SWIGSTDCALL CSharp_MoveOnly_create() {
<p>
<tt>SwigValueWrapper</tt> is covered in <a href="SWIGPlus.html#SWIGPlus_nn19">Pass and return by value</a>.
-Note that the generated code could be optimised further using the <a href="Typemaps.html#Typemaps_optimal">"optimal" attribute</a> in the "out" typemap.
+Note that the generated code could be optimised further using the <a href="Typemaps.html#Typemaps_optimal">"optimal" attribute</a>
+in the "out" typemap, so if the above typemap is customised as follows (note that this is C# specific):
</p>
+<div class="code"><pre>
+%typemap(out, optimal="1") MoveOnly %{
+ $result = new $1_ltype($1);
+%}
+</pre></div>
+
+<p>
+then the generated code will result in the object being optimally moved:
+</p>
+
+<div class="code"><pre>
+SWIGEXPORT void * SWIGSTDCALL CSharp_MoveOnly_create() {
+ void * jresult ;
+ jresult = new MoveOnly(MoveOnly::create());
+ return jresult;
+}
+</pre></div>
+
+<p>
+Now let's consider passing by value.
+We'll consider three cases; namely types that are:
+</p>
+
+<ol>
+ <li> Copyable and not movable - <tt>CopyOnly</tt>.</li>
+ <li> Copyable and movable - <tt>MovableCopyable</tt>.</li>
+ <li> Movable and not copyable - <tt>MoveOnly</tt>.</li>
+</ol>
+
+<p>
+and for clarification, define these two additional types as follows:
+</p>
+
+<div class="code"><pre>
+struct CopyOnly {
+ int val;
+ CopyOnly(): val(0) {}
+
+ CopyOnly(const CopyOnly &amp;) = default;
+ CopyOnly &amp; operator=(const CopyOnly &amp;) = default;
+
+ static CopyOnly create() { return CopyOnly(); }
+ static void take(CopyOnly co);
+};
+
+struct MovableCopyable {
+ int val;
+ MovableCopyable(): val(0) {}
+
+ MovableCopyable(const MovableCopyable &amp;) = default;
+ MovableCopyable(MovableCopyable &amp;&amp;) = default;
+ MovableCopyable &amp; operator=(const MovableCopyable &amp;) = default;
+ MovableCopyable &amp; operator=(MovableCopyable &amp;&amp;) = default;
+
+ static MovableCopyable create() { return MovableCopyable(); }
+ static void take(MovableCopyable mc);
+};
+</pre></div>
+
+<p>
+The generated code is shown below for <tt>CopyOnly::take</tt> (with additional comments for when constructors and assignment operators are called).
+While the code shown is C# specific, the generated constructor and/or assignment operator calls are ultimately the same for all target languages.
+</p>
+
+<div class="code"><pre>
+SWIGEXPORT void SWIGSTDCALL CSharp_CopyOnly_take(void * jarg1) {
+ CopyOnly arg1 ; // (a) Default constructor
+ CopyOnly *argp1 ;
+
+ argp1 = (CopyOnly *)jarg1;
+ if (!argp1) {
+ SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null CopyOnly", 0);
+ return ;
+ }
+ arg1 = *argp1; // (b) Copy assignment
+ CopyOnly::take(SWIG_STD_MOVE(arg1)); // (c) Copy constructor
+}
+</pre></div>
+
+<p>
+Note that <tt>SWIG_STD_MOVE</tt> is a macro defined as shown below to use <tt>std::move</tt> which is only available from C++11 onwards:
+</p>
+
+<div class="code"><pre>
+#if __cplusplus &gt;=201103L
+# define SWIG_STD_MOVE(OBJ) std::move(OBJ)
+#else
+# define SWIG_STD_MOVE(OBJ) OBJ
+#endif
+</pre></div>
+
+<p>
+Also note: <i>(c) Copy constructor</i>.
+Yes, when passing by value the copy constructor is called for all versions of C++, even C++11 and later even though std::move is specified.
+It's a C++ language feature for types that don't have move semantics!
+</p>
+
+<p>
+The generated code for <tt>MovableCopyable::take</tt> is the same as for <tt>CopyOnly::take</tt>, however, the C++ compiler will choose the move constructor this time where commented <i>(c) Move constructor</i>:
+</p>
+
+<div class="code"><pre>
+SWIGEXPORT void SWIGSTDCALL CSharp_MovableCopyable_take(void * jarg1) {
+ MovableCopyable arg1 ; // (a) Default constructor
+ MovableCopyable *argp1 ;
+
+ argp1 = (MovableCopyable *)jarg1;
+ if (!argp1) {
+ SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null MovableCopyable", 0);
+ return ;
+ }
+ arg1 = *argp1; // (b) Copy assignment
+ MovableCopyable::take(SWIG_STD_MOVE(arg1)); // (c) Move constructor
+}
+</pre></div>
+
+<p>
+There are two optimisation opportunities available.
+</p>
+<ol>
+ <li> Remove the default constructor call with the <tt>%feature("valuewrapper")</tt> covered in <a href="SWIGPlus.html#SWIGPlus_nn19">Pass and return by value</a> and replace it with <tt>SwigValueWrapper</tt>.
+ </li>
+ <li> Apply the SWIGTYPE MOVE typemaps which are designed specifically to implement full move semantics when passing parameters by value.
+ They replace the copy assignment with a call to <tt>SwigValueWrapper::reset</tt>, which works much like <tt>std::unique_ptr::reset</tt>.
+ These typemaps could alternatively have replaced the copy assignment with a move assignment, but this is not maximally optimal.
+ </li>
+</ol>
<p>
-There is currently only partial support for move-only types as
-support for move-only types used as a parameter in a function, that are passed 'by value', is not yet available.
+Simply add the following before the <tt>MovableCopyable::take</tt> method is parsed:
</p>
+<div class="code"><pre>
+%valuewrapper MovableCopyable;
+%include &lt;swigmove.i&gt;
+%apply SWIGTYPE MOVE { MovableCopyable }
+</pre></div>
+
+<p>
+will result in this optimal code where just one move constructor is invoked:
+</p>
+
+<div class="code"><pre>
+SWIGEXPORT void SWIGSTDCALL CSharp_MovableCopyable_take(void * jarg1) {
+ SwigValueWrapper&lt; MovableCopyable &gt; arg1 ; // (a) No constructors invoked
+ MovableCopyable *argp1 ;
+
+ argp1 = (MovableCopyable *)jarg1;
+ if (!argp1) {
+ SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null MovableCopyable", 0);
+ return ;
+ }
+ SwigValueWrapper&lt; MovableCopyable &gt;::reset(arg1, argp1); // (b) No constructor or assignment operator invoked
+ MovableCopyable::take(SWIG_STD_MOVE(arg1)); // (c) Move constructor
+}
+</pre></div>
+
+<p>
+Note that <tt>SwigValueWrapper</tt> will call the destructor for the pointer passed to it in the <tt>reset</tt> function.
+This pointer is the underlying C++ object that the proxy class owns.
+The details aren't shown, but the 'csin' typemap also generates C# code to ensure that the proxy class releases ownership of the object.
+Please see the 'SWIGTYPE MOVE' typemaps in the swigmove.i file provided for each target language.
+Therefore full move semantics are implemented; ownership is moved from the proxy class into the C++ layer and the net effect
+is the same as using an <a href="#CPlusPlus11_rvalue_reference_inputs">rvalue reference parameter</a> discussed earlier.
+</p>
+
+<p>
+Lastly, let's consider the <tt>MoveOnly::take</tt> function defined earlier.
+By default the generated code fails to compile as <tt>MoveOnly</tt> does not have a copy assignment operator.
+SWIG is not designed to select a different typemap automatically for move-only types and the user
+must apply the SWIGTYPE MOVE typemaps to ensure that only move-only semantics are used.
+However, SWIG is able to automatically use <tt>%feature("valuewrapper")</tt> for move-only
+types so it is not necessary to explicitly use this feature.
+So in this move-only case, simply add the following before <tt>MoveOnly::take</tt> is parsed, which results in the same optimal code shown above for <tt>MovableCopyable</tt>:
+</p>
+
+<div class="code"><pre>
+%include &lt;swigmove.i&gt;
+%apply SWIGTYPE MOVE { MoveOnly }
+</pre></div>
+
<p>
<b>Compatibility note:</b>
-SWIG-4.1.0 introduced support for taking advantage of types with move semantics and wrapping functions that return movable or move-only types 'by value'.
+SWIG-4.1.0 introduced support for taking advantage of types with move semantics and making it possible to easily use move only types.
</p>
diff --git a/Doc/Manual/Contents.html b/Doc/Manual/Contents.html
index 2fdabafa6..d3106bf09 100644
--- a/Doc/Manual/Contents.html
+++ b/Doc/Manual/Contents.html
@@ -300,7 +300,7 @@
<ul>
<li><a href="CPlusPlus11.html#CPlusPlus11_rvalue_reference_inputs">Rvalue reference inputs</a>
<li><a href="CPlusPlus11.html#CPlusPlus11_rvalue_reference_outputs">Rvalue reference outputs</a>
-<li><a href="CPlusPlus11.html#CPlusPlus11_move_only">Movable and move-only types</a>
+<li><a href="CPlusPlus11.html#CPlusPlus11_move_only">Movable and move-only types by value</a>
</ul>
<li><a href="CPlusPlus11.html#CPlusPlus11_generalized_constant_expressions">Generalized constant expressions</a>
<li><a href="CPlusPlus11.html#CPlusPlus11_extern_template">Extern template</a>
diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk
index 950f62d98..4df120943 100644
--- a/Examples/test-suite/common.mk
+++ b/Examples/test-suite/common.mk
@@ -597,8 +597,9 @@ CPP11_TEST_CASES += \
cpp11_initializer_list \
cpp11_initializer_list_extend \
cpp11_lambda_functions \
- cpp11_move_only \
- cpp11_move_only_valuewrapper \
+ cpp11_move_only \
+ cpp11_move_typemaps \
+ cpp11_move_only_valuewrapper \
cpp11_noexcept \
cpp11_null_pointer_constant \
cpp11_raw_string_literals \
diff --git a/Examples/test-suite/cpp11_move_only.i b/Examples/test-suite/cpp11_move_only.i
index 732bc49db..28aaf05e5 100644
--- a/Examples/test-suite/cpp11_move_only.i
+++ b/Examples/test-suite/cpp11_move_only.i
@@ -12,19 +12,23 @@ using namespace std;
bool trace = false;
struct MoveOnly {
- MoveOnly(int i = 0) { if (trace) cout << "MoveOnly(" << i << ")" << " " << this << endl; Counter::normal_constructor++; }
+ int val;
+ MoveOnly(int i = 0) : val(i) { if (trace) cout << "MoveOnly(" << i << ")" << " " << this << endl; Counter::normal_constructor++; }
MoveOnly(const MoveOnly &other) = delete;
MoveOnly & operator=(const MoveOnly &other) = delete;
- MoveOnly(MoveOnly &&other) noexcept { if (trace) cout << "MoveOnly(MoveOnly &&)" << " " << this << endl; Counter::move_constructor++; }
- MoveOnly & operator=(MoveOnly &&other) noexcept { if (trace) cout << "operator=(MoveOnly &&)" << " " << this << endl; Counter::move_assignment++; return *this; }
+ MoveOnly(MoveOnly &&other) noexcept : val(std::move(other.val)) { if (trace) cout << "MoveOnly(MoveOnly &&)" << " " << this << endl; Counter::move_constructor++; }
+ MoveOnly & operator=(MoveOnly &&other) noexcept { if (trace) cout << "operator=(MoveOnly &&)" << " " << this << endl; Counter::move_assignment++; if (this != &other) { val = std::move(other.val); } return *this; }
~MoveOnly() { if (trace) cout << "~MoveOnly()" << " " << this << endl; Counter::destructor++; }
static MoveOnly create() { return MoveOnly(111); }
// static const MoveOnly createConst() { return MoveOnly(111); } // not supported by default
- // static void take(MoveOnly mo) { if (trace) cout << "take(MoveOnly)" << " " << &mo << endl; }
+ // compile error by default, see cpp11_move_typemaps.i
+ #if defined(WRAP_TAKE_METHOD)
+ static void take(MoveOnly mo) { if (trace) cout << "take(MoveOnly)" << " " << &mo << endl; }
+ #endif
};
%}
@@ -35,13 +39,14 @@ struct MoveOnly {
%inline %{
// Movable and Copyable
struct MovableCopyable {
- MovableCopyable(int i = 0) { if (trace) cout << "MovableCopyable(" << i << ")" << " " << this << endl; Counter::normal_constructor++; }
+ int val;
+ MovableCopyable(int i = 0) : val(i) { if (trace) cout << "MovableCopyable(" << i << ")" << " " << this << endl; Counter::normal_constructor++; }
- MovableCopyable(const MovableCopyable &other) { if (trace) cout << "MovableCopyable(const MovableCopyable &)" << " " << this << " " << &other << endl; Counter::copy_constructor++;}
- MovableCopyable & operator=(const MovableCopyable &other) { if (trace) cout << "operator=(const MovableCopyable &)" << " " << this << " " << &other << endl; Counter::copy_assignment++; return *this; }
+ MovableCopyable(const MovableCopyable &other) : val(other.val) { if (trace) cout << "MovableCopyable(const MovableCopyable &)" << " " << this << " " << &other << endl; Counter::copy_constructor++;}
+ MovableCopyable & operator=(const MovableCopyable &other) { if (trace) cout << "operator=(const MovableCopyable &)" << " " << this << " " << &other << endl; Counter::copy_assignment++; if (this != &other) { val = other.val; } return *this; }
- MovableCopyable(MovableCopyable &&other) noexcept { if (trace) cout << "MovableCopyable(MovableCopyable &&)" << " " << this << endl; Counter::move_constructor++; }
- MovableCopyable & operator=(MovableCopyable &&other) noexcept { if (trace) cout << "operator=(MovableCopyable &&)" << " " << this << endl; Counter::move_assignment++; return *this; }
+ MovableCopyable(MovableCopyable &&other) noexcept : val(std::move(other.val)) { if (trace) cout << "MovableCopyable(MovableCopyable &&)" << " " << this << endl; Counter::move_constructor++; }
+ MovableCopyable & operator=(MovableCopyable &&other) noexcept { if (trace) cout << "operator=(MovableCopyable &&)" << " " << this << endl; Counter::move_assignment++; if (this != &other) { val = std::move(other.val); } return *this; }
~MovableCopyable() { if (trace) cout << "~MovableCopyable()" << " " << this << endl; Counter::destructor++; }
static MovableCopyable create() { return MovableCopyable(111); }
diff --git a/Examples/test-suite/cpp11_move_typemaps.i b/Examples/test-suite/cpp11_move_typemaps.i
new file mode 100644
index 000000000..706780ad4
--- /dev/null
+++ b/Examples/test-suite/cpp11_move_typemaps.i
@@ -0,0 +1,12 @@
+%module cpp11_move_typemaps
+
+%include <swigmove.i>
+%apply SWIGTYPE MOVE { MoveOnly mo }
+%valuewrapper MovableCopyable;
+%apply SWIGTYPE MOVE { MovableCopyable mc }
+
+%inline %{
+#define WRAP_TAKE_METHOD
+%}
+
+%include "cpp11_move_only.i"
diff --git a/Examples/test-suite/csharp/cpp11_move_typemaps_runme.cs b/Examples/test-suite/csharp/cpp11_move_typemaps_runme.cs
new file mode 100644
index 000000000..96849c4d3
--- /dev/null
+++ b/Examples/test-suite/csharp/cpp11_move_typemaps_runme.cs
@@ -0,0 +1,37 @@
+using System;
+using cpp11_move_typemapsNamespace;
+
+public class cpp11_move_typemaps_runme {
+
+ public static void Main() {
+ Counter.reset_counts();
+ using (MoveOnly mo = new MoveOnly(111)) {
+ Counter.check_counts(1, 0, 0, 0, 0, 0);
+ MoveOnly.take(mo);
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+ }
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+ Counter.reset_counts();
+ using (MovableCopyable mo = new MovableCopyable(111)) {
+ Counter.check_counts(1, 0, 0, 0, 0, 0);
+ MovableCopyable.take(mo);
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+ }
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+ using (MoveOnly mo = new MoveOnly(222)) {
+ MoveOnly.take(mo);
+ bool exception_thrown = false;
+ try {
+ MoveOnly.take(mo);
+ } 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("double usage of take should have been an error");
+ }
+ }
+}
diff --git a/Examples/test-suite/d/cpp11_move_typemaps_runme.1.d b/Examples/test-suite/d/cpp11_move_typemaps_runme.1.d
new file mode 100644
index 000000000..29561cde3
--- /dev/null
+++ b/Examples/test-suite/d/cpp11_move_typemaps_runme.1.d
@@ -0,0 +1,42 @@
+module cpp11_move_typemaps_runme;
+
+import cpp11_move_typemaps.Counter;
+import cpp11_move_typemaps.MoveOnly;
+import cpp11_move_typemaps.MovableCopyable;
+import std.conv;
+import std.algorithm;
+
+void main() {
+ {
+ Counter.reset_counts();
+ scope MoveOnly mo = new MoveOnly(111);
+ Counter.check_counts(1, 0, 0, 0, 0, 0);
+ MoveOnly.take(mo);
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+ }
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+ {
+ Counter.reset_counts();
+ scope MovableCopyable mo = new MovableCopyable(111);
+ Counter.check_counts(1, 0, 0, 0, 0, 0);
+ MovableCopyable.take(mo);
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+ }
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+ {
+ scope MoveOnly mo = new MoveOnly(222);
+ MoveOnly.take(mo);
+ bool exception_thrown = false;
+ try {
+ MoveOnly.take(mo);
+ } catch (Exception e) {
+ if (!canFind(e.msg, "Cannot release ownership as memory is not owned"))
+ throw new Exception("incorrect exception message: " ~ e.msg);
+ exception_thrown = true;
+ }
+ if (!exception_thrown)
+ throw new Exception("double usage of take should have been an error");
+ }
+}
diff --git a/Examples/test-suite/d/cpp11_move_typemaps_runme.2.d b/Examples/test-suite/d/cpp11_move_typemaps_runme.2.d
new file mode 100644
index 000000000..29561cde3
--- /dev/null
+++ b/Examples/test-suite/d/cpp11_move_typemaps_runme.2.d
@@ -0,0 +1,42 @@
+module cpp11_move_typemaps_runme;
+
+import cpp11_move_typemaps.Counter;
+import cpp11_move_typemaps.MoveOnly;
+import cpp11_move_typemaps.MovableCopyable;
+import std.conv;
+import std.algorithm;
+
+void main() {
+ {
+ Counter.reset_counts();
+ scope MoveOnly mo = new MoveOnly(111);
+ Counter.check_counts(1, 0, 0, 0, 0, 0);
+ MoveOnly.take(mo);
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+ }
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+ {
+ Counter.reset_counts();
+ scope MovableCopyable mo = new MovableCopyable(111);
+ Counter.check_counts(1, 0, 0, 0, 0, 0);
+ MovableCopyable.take(mo);
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+ }
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+ {
+ scope MoveOnly mo = new MoveOnly(222);
+ MoveOnly.take(mo);
+ bool exception_thrown = false;
+ try {
+ MoveOnly.take(mo);
+ } catch (Exception e) {
+ if (!canFind(e.msg, "Cannot release ownership as memory is not owned"))
+ throw new Exception("incorrect exception message: " ~ e.msg);
+ exception_thrown = true;
+ }
+ if (!exception_thrown)
+ throw new Exception("double usage of take should have been an error");
+ }
+}
diff --git a/Examples/test-suite/guile/cpp11_move_typemaps_runme.scm b/Examples/test-suite/guile/cpp11_move_typemaps_runme.scm
new file mode 100644
index 000000000..43f78e6e4
--- /dev/null
+++ b/Examples/test-suite/guile/cpp11_move_typemaps_runme.scm
@@ -0,0 +1,3 @@
+(dynamic-call "scm_init_cpp11_move_typemaps_module" (dynamic-link "./libcpp11_move_typemaps"))
+(load "testsuite.scm")
+(load "../schemerunme/cpp11_move_typemaps.scm")
diff --git a/Examples/test-suite/java/cpp11_move_typemaps_runme.java b/Examples/test-suite/java/cpp11_move_typemaps_runme.java
new file mode 100644
index 000000000..1b5fd2e6c
--- /dev/null
+++ b/Examples/test-suite/java/cpp11_move_typemaps_runme.java
@@ -0,0 +1,51 @@
+
+import cpp11_move_typemaps.*;
+
+public class cpp11_move_typemaps_runme {
+
+ static {
+ try {
+ System.loadLibrary("cpp11_move_typemaps");
+ } 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();
+ MoveOnly mo = new MoveOnly(111);
+ Counter.check_counts(1, 0, 0, 0, 0, 0);
+ MoveOnly.take(mo);
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+ mo.delete();
+ }
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+ {
+ Counter.reset_counts();
+ MovableCopyable mo = new MovableCopyable(111);
+ Counter.check_counts(1, 0, 0, 0, 0, 0);
+ MovableCopyable.take(mo);
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+ mo.delete();
+ }
+ Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+ {
+ MoveOnly mo = new MoveOnly(222);
+ MoveOnly.take(mo);
+ boolean exception_thrown = false;
+ try {
+ MoveOnly.take(mo);
+ } 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)
+ throw new RuntimeException("double usage of take should have been an error");
+ }
+ }
+}
diff --git a/Examples/test-suite/javascript/cpp11_move_typemaps_runme.js b/Examples/test-suite/javascript/cpp11_move_typemaps_runme.js
new file mode 100644
index 000000000..1e888cb9c
--- /dev/null
+++ b/Examples/test-suite/javascript/cpp11_move_typemaps_runme.js
@@ -0,0 +1,30 @@
+var cpp11_move_typemaps = require("cpp11_move_typemaps");
+
+cpp11_move_typemaps.Counter.reset_counts();
+mo = new cpp11_move_typemaps.MoveOnly(111);
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 0, 0, 0);
+cpp11_move_typemaps.MoveOnly.take(mo);
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 1, 0, 2);
+delete mo;
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+cpp11_move_typemaps.Counter.reset_counts();
+mo = new cpp11_move_typemaps.MovableCopyable(111);
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 0, 0, 0);
+cpp11_move_typemaps.MovableCopyable.take(mo);
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 1, 0, 2);
+delete mo;
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 1, 0, 2);
+
+mo = new cpp11_move_typemaps.MoveOnly(222);
+cpp11_move_typemaps.MoveOnly.take(mo);
+exception_thrown = false;
+try {
+ cpp11_move_typemaps.MoveOnly.take(mo);
+} catch (e) {
+ if (!e.message.includes("cannot release ownership as memory is not owned"))
+ throw new Error("incorrect exception message:" + e.message);
+ exception_thrown = true;
+}
+if (!exception_thrown)
+ throw new Error("double usage of take should have been an error");
diff --git a/Examples/test-suite/lua/cpp11_move_typemaps_runme.lua b/Examples/test-suite/lua/cpp11_move_typemaps_runme.lua
new file mode 100644
index 000000000..d8947b757
--- /dev/null
+++ b/Examples/test-suite/lua/cpp11_move_typemaps_runme.lua
@@ -0,0 +1,28 @@
+require("import") -- the import fn
+import("cpp11_move_typemaps") -- import code
+
+-- catch "undefined" global variables
+local env = _ENV -- Lua 5.2
+if not env then env = getfenv () end -- Lua 5.1
+setmetatable(env, {__index=function (t,i) error("undefined global variable `"..i.."'",2) end})
+
+cpp11_move_typemaps.Counter.reset_counts()
+mo = cpp11_move_typemaps.MoveOnly(111)
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 0, 0, 0)
+cpp11_move_typemaps.MoveOnly.take(mo)
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 1, 0, 2)
+mo = nil
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 1, 0, 2)
+
+cpp11_move_typemaps.Counter.reset_counts()
+mo = cpp11_move_typemaps.MovableCopyable(111)
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 0, 0, 0)
+cpp11_move_typemaps.MovableCopyable.take(mo)
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 1, 0, 2)
+mo = nil
+cpp11_move_typemaps.Counter.check_counts(1, 0, 0, 1, 0, 2)
+
+mo = cpp11_move_typemaps.MoveOnly(222)
+cpp11_move_typemaps.MoveOnly.take(mo)
+s, msg = pcall(function() cpp11_move_typemaps.MoveOnly.take(mo) end)
+assert(s == false and msg:find("Cannot release ownership as memory is not owned", 1, true))
diff --git a/Examples/test-suite/mzscheme/cpp11_move_typemaps_runme.scm b/Examples/test-suite/mzscheme/cpp11_move_typemaps_runme.scm
new file mode 100644
index 000000000..910cb3938
--- /dev/null
+++ b/Examples/test-suite/mzscheme/cpp11_move_typemaps_runme.scm
@@ -0,0 +1,35 @@
+(load-extension "cpp11_move_typemaps.so")
+(require (lib "defmacro.ss"))
+
+; Copied from ../schemerunme/cpp11_move_typemaps.scm and modified for exceptions
+
+; Define an equivalent to Guile's gc procedure
+(define-macro (gc)
+ `(collect-garbage 'major))
+
+(Counter-reset-counts)
+(define mo (new-MoveOnly 111))
+(Counter-check-counts 1 0 0 0 0 0)
+(MoveOnly-take mo)
+(Counter-check-counts 1 0 0 1 0 2)
+(delete-MoveOnly mo)
+(Counter-check-counts 1 0 0 1 0 2)
+
+(Counter-reset-counts)
+(define mo (new-MovableCopyable 111))
+(Counter-check-counts 1 0 0 0 0 0)
+(MovableCopyable-take mo)
+(Counter-check-counts 1 0 0 1 0 2)
+(delete-MovableCopyable mo)
+(Counter-check-counts 1 0 0 1 0 2)
+
+(define mo (new-MoveOnly 222))
+(MoveOnly-take mo)
+(define exception_thrown "no exception thrown for mo")
+(with-handlers ([exn:fail? (lambda (exn)
+ (set! exception_thrown (exn-message exn)))])
+ (MoveOnly-take mo))
+(unless (string-contains? exception_thrown "cannot release ownership as memory is not owned")
+ (error "Wrong or no exception thrown: " exception_thrown))
+
+(exit 0)
diff --git a/Examples/test-suite/octave/cpp11_move_typemaps_runme.m b/Examples/test-suite/octave/cpp11_move_typemaps_runme.m
new file mode 100644
index 000000000..c0532565b
--- /dev/null
+++ b/Examples/test-suite/octave/cpp11_move_typemaps_runme.m
@@ -0,0 +1,37 @@
+# do not dump Octave core
+if exist("crash_dumps_octave_core", "builtin")
+ crash_dumps_octave_core(0);
+endif
+
+cpp11_move_typemaps
+
+Counter.reset_counts();
+mo = MoveOnly(111);
+Counter_check_counts(1, 0, 0, 0, 0, 0);
+MoveOnly.take(mo);
+Counter_check_counts(1, 0, 0, 1, 0, 2);
+clear mo;
+Counter_check_counts(1, 0, 0, 1, 0, 2);
+
+Counter.reset_counts();
+mo = MovableCopyable(111);
+Counter_check_counts(1, 0, 0, 0, 0, 0);
+MovableCopyable.take(mo);
+Counter_check_counts(1, 0, 0, 1, 0, 2);
+clear mo;
+Counter_check_counts(1, 0, 0, 1, 0, 2);
+
+mo = MoveOnly(222);
+MoveOnly.take(mo);
+exception_thrown = false;
+try
+ MoveOnly.take(mo);
+catch e
+ if (isempty(strfind(e.message, "cannot release ownership as memory is not owned")))
+ error("incorrect exception message %s", e.message);
+ endif
+ exception_thrown = true;
+end_try_catch
+if (!exception_thrown)
+ error("double usage of take should have been an error");
+endif
diff --git a/Examples/test-suite/perl5/cpp11_move_typemaps_runme.pl b/Examples/test-suite/perl5/cpp11_move_typemaps_runme.pl
new file mode 100644
index 000000000..aae3e4dcb
--- /dev/null
+++ b/Examples/test-suite/perl5/cpp11_move_typemaps_runme.pl
@@ -0,0 +1,34 @@
+use strict;
+use warnings;
+use Test::More tests => 3;
+BEGIN { use_ok('cpp11_move_typemaps') }
+require_ok('cpp11_move_typemaps');
+
+{
+ cpp11_move_typemaps::Counter::reset_counts();
+ my $mo = new cpp11_move_typemaps::MoveOnly(111);
+ cpp11_move_typemaps::Counter::check_counts(1, 0, 0, 0, 0, 0);
+ cpp11_move_typemaps::MoveOnly::take($mo);
+ cpp11_move_typemaps::Counter::check_counts(1, 0, 0, 1, 0, 2);
+ undef $mo;
+}
+cpp11_move_typemaps::Counter::check_counts(1, 0, 0, 1, 0, 2);
+
+{
+ cpp11_move_typemaps::Counter::reset_counts();
+ my $mo = new cpp11_move_typemaps::MovableCopyable(111);
+ cpp11_move_typemaps::Counter::check_counts(1, 0, 0, 0, 0, 0);
+ cpp11_move_typemaps::MovableCopyable::take($mo);
+ cpp11_move_typemaps::Counter::check_counts(1, 0, 0, 1, 0, 2);
+ undef $mo;
+}
+cpp11_move_typemaps::Counter::check_counts(1, 0, 0, 1, 0, 2);
+
+{
+ my $mo = new cpp11_move_typemaps::MoveOnly(222);
+ cpp11_move_typemaps::MoveOnly::take($mo);
+ eval {
+ cpp11_move_typemaps::MoveOnly::take($mo);
+ };
+ like($@, qr/\bcannot release ownership as memory is not owned\b/, "double usage of takeKlassUniquePtr should be an error");
+}
diff --git a/Examples/test-suite/php/cpp11_move_typemaps_runme.php b/Examples/test-suite/php/cpp11_move_typemaps_runme.php
new file mode 100644
index 000000000..2d4b5e090
--- /dev/null
+++ b/Examples/test-suite/php/cpp11_move_typemaps_runme.php
@@ -0,0 +1,32 @@
+<?php
+
+require "tests.php";
+
+Counter::reset_counts();
+$mo = new MoveOnly(111);
+Counter::check_counts(1, 0, 0, 0, 0, 0);
+MoveOnly::take($mo);
+Counter::check_counts(1, 0, 0, 1, 0, 2);
+$mo = NULL;
+Counter::check_counts(1, 0, 0, 1, 0, 2);
+
+Counter::reset_counts();
+$mo = new MovableCopyable(111);
+Counter::check_counts(1, 0, 0, 0, 0, 0);
+MovableCopyable::take($mo);
+Counter::check_counts(1, 0, 0, 1, 0, 2);
+$mo = NULL;
+Counter::check_counts(1, 0, 0, 1, 0, 2);
+
+$mo = new MoveOnly(222);
+MoveOnly::take($mo);
+$exception_thrown = false;
+try {
+ MoveOnly::take($mo);
+} catch (TypeError $e) {
+ check::str_contains($e->getMessage(), "Cannot release ownership as memory is not owned", "incorrect exception message: {$e->getMessage()}");
+ $exception_thrown = true;
+}
+check::equal($exception_thrown, true, "double usage of takeKlassUniquePtr should have been an error");
+
+check::done();
diff --git a/Examples/test-suite/python/cpp11_move_typemaps_runme.py b/Examples/test-suite/python/cpp11_move_typemaps_runme.py
new file mode 100644
index 000000000..e4cf06c53
--- /dev/null
+++ b/Examples/test-suite/python/cpp11_move_typemaps_runme.py
@@ -0,0 +1,29 @@
+from cpp11_move_typemaps import *
+
+Counter.reset_counts()
+mo = MoveOnly(111)
+Counter.check_counts(1, 0, 0, 0, 0, 0)
+MoveOnly.take(mo)
+Counter.check_counts(1, 0, 0, 1, 0, 2)
+del mo
+Counter.check_counts(1, 0, 0, 1, 0, 2)
+
+Counter.reset_counts()
+mo = MovableCopyable(111)
+Counter.check_counts(1, 0, 0, 0, 0, 0)
+MovableCopyable.take(mo)
+Counter.check_counts(1, 0, 0, 1, 0, 2)
+del mo
+Counter.check_counts(1, 0, 0, 1, 0, 2)
+
+mo = MoveOnly(222)
+MoveOnly.take(mo)
+exception_thrown = False
+try:
+ MoveOnly.take(mo)
+except RuntimeError as e:
+ if "cannot release ownership as memory is not owned" not in str(e):
+ raise RuntimeError("incorrect exception message:" + str(e))
+ exception_thrown = True
+if not exception_thrown:
+ raise RuntimeError("Should have thrown 'Cannot release ownership as memory is not owned' error")
diff --git a/Examples/test-suite/ruby/cpp11_move_typemaps_runme.rb b/Examples/test-suite/ruby/cpp11_move_typemaps_runme.rb
new file mode 100644
index 000000000..d2c9fe3df
--- /dev/null
+++ b/Examples/test-suite/ruby/cpp11_move_typemaps_runme.rb
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+
+require 'swig_assert'
+
+require 'cpp11_move_typemaps'
+
+Cpp11_move_typemaps::Counter.reset_counts()
+mo = Cpp11_move_typemaps::MoveOnly.new(111)
+Cpp11_move_typemaps::Counter.check_counts(1, 0, 0, 0, 0, 0)
+Cpp11_move_typemaps::MoveOnly.take(mo)
+Cpp11_move_typemaps::Counter.check_counts(1, 0, 0, 1, 0, 2)
+mo = nil
+Cpp11_move_typemaps::Counter.check_counts(1, 0, 0, 1, 0, 2)
+
+Cpp11_move_typemaps::Counter.reset_counts()
+mo = Cpp11_move_typemaps::MovableCopyable.new(111)
+Cpp11_move_typemaps::Counter.check_counts(1, 0, 0, 0, 0, 0)
+Cpp11_move_typemaps::MovableCopyable.take(mo)
+Cpp11_move_typemaps::Counter.check_counts(1, 0, 0, 1, 0, 2)
+mo = nil
+Cpp11_move_typemaps::Counter.check_counts(1, 0, 0, 1, 0, 2)
+
+mo = Cpp11_move_typemaps::MoveOnly.new(222)
+Cpp11_move_typemaps::MoveOnly.take(mo)
+exception_thrown = false
+begin
+ Cpp11_move_typemaps::MoveOnly.take(mo)
+rescue RuntimeError => e
+ if (!e.to_s.include? "cannot release ownership as memory is not owned")
+ raise RuntimeError, "incorrect exception message: #{e.to_s}"
+ end
+ exception_thrown = true
+end
+if (!exception_thrown)
+ raise RuntimeError, "Should have thrown 'Cannot release ownership as memory is not owned' error"
+end
diff --git a/Examples/test-suite/schemerunme/cpp11_move_typemaps.scm b/Examples/test-suite/schemerunme/cpp11_move_typemaps.scm
new file mode 100644
index 000000000..0a8b85a58
--- /dev/null
+++ b/Examples/test-suite/schemerunme/cpp11_move_typemaps.scm
@@ -0,0 +1,23 @@
+(Counter-reset-counts)
+(define mo (new-MoveOnly 111))
+(Counter-check-counts 1 0 0 0 0 0)
+(MoveOnly-take mo)
+(Counter-check-counts 1 0 0 1 0 2)
+(delete-MoveOnly mo)
+(Counter-check-counts 1 0 0 1 0 2)
+
+(Counter-reset-counts)
+(define mo (new-MovableCopyable 111))
+(Counter-check-counts 1 0 0 0 0 0)
+(MovableCopyable-take mo)
+(Counter-check-counts 1 0 0 1 0 2)
+(delete-MovableCopyable mo)
+(Counter-check-counts 1 0 0 1 0 2)
+
+(define mo (new-MoveOnly 222))
+(MoveOnly-take mo)
+(expect-throw 'misc-error
+ (MoveOnly-take mo))
+; TODO: check the exception message
+
+(exit 0)
diff --git a/Examples/test-suite/tcl/cpp11_move_typemaps_runme.tcl b/Examples/test-suite/tcl/cpp11_move_typemaps_runme.tcl
new file mode 100644
index 000000000..48f860101
--- /dev/null
+++ b/Examples/test-suite/tcl/cpp11_move_typemaps_runme.tcl
@@ -0,0 +1,35 @@
+
+if [ catch { load ./cpp11_move_typemaps[info sharedlibextension] cpp11_move_typemaps} err_msg ] {
+ puts stderr "Could not load shared object:\n$err_msg"
+}
+
+Counter_reset_counts
+MoveOnly mo 111
+Counter_check_counts 1 0 0 0 0 0
+MoveOnly_take mo
+Counter_check_counts 1 0 0 1 0 2
+mo -delete
+Counter_check_counts 1 0 0 1 0 2
+
+Counter_reset_counts
+MovableCopyable mo 111
+Counter_check_counts 1 0 0 0 0 0
+MovableCopyable_take mo
+Counter_check_counts 1 0 0 1 0 2
+mo -delete
+Counter_check_counts 1 0 0 1 0 2
+
+MoveOnly mo 222
+MoveOnly_take mo
+set exception_thrown 0
+if [ catch {
+ MoveOnly_take mo
+} e ] {
+ if {[string first "cannot release ownership as memory is not owned" $e] == -1} {
+ error "incorrect exception message: $e"
+ }
+ set exception_thrown 1
+}
+if {!$exception_thrown} {
+ error "Should have thrown 'Cannot release ownership as memory is not owned' error"
+}
diff --git a/Lib/csharp/swigmove.i b/Lib/csharp/swigmove.i
new file mode 100644
index 000000000..2f21bd6f7
--- /dev/null
+++ b/Lib/csharp/swigmove.i
@@ -0,0 +1,16 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in, canthrow=1, fragment="<memory>") SWIGTYPE MOVE ($&1_type argp)
+%{ argp = ($&1_ltype)$input;
+ if (!argp) {
+ SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null $1_type", 0);
+ return $null;
+ }
+ SwigValueWrapper< $1_ltype >::reset($1, argp); %}
+
+%typemap(csin) SWIGTYPE MOVE "$&csclassname.swigRelease($csinput)"
diff --git a/Lib/d/swigmove.i b/Lib/d/swigmove.i
new file mode 100644
index 000000000..e2eb83406
--- /dev/null
+++ b/Lib/d/swigmove.i
@@ -0,0 +1,16 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in, canthrow=1) SWIGTYPE MOVE ($&1_type argp)
+%{ argp = ($&1_ltype)$input;
+ if (!argp) {
+ SWIG_DSetPendingException(SWIG_DIllegalArgumentException, "Attempt to dereference null $1_type");
+ return $null;
+ }
+ SwigValueWrapper< $1_ltype >::reset($1, argp); %}
+
+%typemap(din) SWIGTYPE MOVE "$dclassname.swigRelease($dinput)"
diff --git a/Lib/go/swigmove.i b/Lib/go/swigmove.i
new file mode 100644
index 000000000..e1984b6ea
--- /dev/null
+++ b/Lib/go/swigmove.i
@@ -0,0 +1,15 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in) SWIGTYPE MOVE ($&1_type argp)
+%{
+ argp = ($&1_ltype)$input;
+ if (argp == NULL) {
+ _swig_gopanic("Attempt to dereference null $1_type");
+ }
+ SwigValueWrapper< $1_ltype >::reset($1, argp);
+%}
diff --git a/Lib/guile/swigmove.i b/Lib/guile/swigmove.i
new file mode 100644
index 000000000..87ab91ead
--- /dev/null
+++ b/Lib/guile/swigmove.i
@@ -0,0 +1,19 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in, noblock=1) SWIGTYPE MOVE (void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp, $&1_descriptor, SWIG_POINTER_RELEASE);
+ if (!SWIG_IsOK(res)) {
+ if (res == SWIG_ERROR_RELEASE_NOT_OWNED) {
+ %releasenotowned_fail(res, "$1_type", $symname, $argnum);
+ } else {
+ %argument_fail(res, "$1_type", $symname, $argnum);
+ }
+ }
+ if (!argp) { %argument_nullref("$1_type", $symname, $argnum); }
+ SwigValueWrapper< $1_ltype >::reset($1, ($&1_type)argp);
+}
diff --git a/Lib/java/swigmove.i b/Lib/java/swigmove.i
new file mode 100644
index 000000000..671b988af
--- /dev/null
+++ b/Lib/java/swigmove.i
@@ -0,0 +1,16 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in) SWIGTYPE MOVE ($&1_type argp)
+%{ argp = *($&1_ltype*)&$input;
+ if (!argp) {
+ SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "Attempt to dereference null $1_type");
+ return $null;
+ }
+ SwigValueWrapper< $1_ltype >::reset($1, argp); %}
+
+%typemap(javain) SWIGTYPE MOVE "$&javaclassname.swigRelease($javainput)"
diff --git a/Lib/javascript/jsc/swigmove.i b/Lib/javascript/jsc/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/javascript/jsc/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/javascript/v8/swigmove.i b/Lib/javascript/v8/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/javascript/v8/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/lua/swigmove.i b/Lib/lua/swigmove.i
new file mode 100644
index 000000000..d130e797a
--- /dev/null
+++ b/Lib/lua/swigmove.i
@@ -0,0 +1,18 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in, checkfn="lua_isuserdata", noblock=1) SWIGTYPE MOVE (void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr(L, $input, &argp, $&1_descriptor, SWIG_POINTER_RELEASE);
+ if (!SWIG_IsOK(res)) {
+ if (res == SWIG_ERROR_RELEASE_NOT_OWNED) {
+ lua_pushfstring(L, "Cannot release ownership as memory is not owned for argument $argnum of type '$1_type' in $symname"); SWIG_fail;
+ } else {
+ SWIG_fail_ptr("$symname", $argnum, $&1_descriptor);
+ }
+ }
+ SwigValueWrapper< $1_ltype >::reset($1, ($&1_type)argp);
+}
diff --git a/Lib/mzscheme/swigmove.i b/Lib/mzscheme/swigmove.i
new file mode 100644
index 000000000..bbfcdcb16
--- /dev/null
+++ b/Lib/mzscheme/swigmove.i
@@ -0,0 +1,19 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in, noblock=1) SWIGTYPE MOVE (void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp, $&1_descriptor, SWIG_POINTER_RELEASE);
+ if (!SWIG_IsOK(res)) {
+ if (res == SWIG_ERROR_RELEASE_NOT_OWNED) {
+ scheme_signal_error(FUNC_NAME ": cannot release ownership as memory is not owned for argument $argnum of type '$1_type'");
+ } else {
+ %argument_fail(res, "$1_type", $symname, $argnum);
+ }
+ }
+ if (argp == NULL) scheme_signal_error(FUNC_NAME ": swig-type-error (null reference)");
+ SwigValueWrapper< $1_ltype >::reset($1, ($&1_type)argp);
+}
diff --git a/Lib/ocaml/swigmove.i b/Lib/ocaml/swigmove.i
new file mode 100644
index 000000000..32f9903bd
--- /dev/null
+++ b/Lib/ocaml/swigmove.i
@@ -0,0 +1,11 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in, noblock=1) SWIGTYPE MOVE (void *argp = 0) {
+ argp1 = ($&1_ltype) caml_ptr_val($input,$&1_descriptor);
+ SwigValueWrapper< $1_ltype >::reset($1, ($&1_type)argp);
+}
diff --git a/Lib/octave/swigmove.i b/Lib/octave/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/octave/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/perl5/swigmove.i b/Lib/perl5/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/perl5/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/php/swigmove.i b/Lib/php/swigmove.i
new file mode 100644
index 000000000..b16a3c544
--- /dev/null
+++ b/Lib/php/swigmove.i
@@ -0,0 +1,24 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.i
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in, noblock=1) SWIGTYPE MOVE (void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr(&$input, &argp, $&1_descriptor, SWIG_POINTER_RELEASE);
+ if (!SWIG_IsOK(res)) {
+ if (res == SWIG_ERROR_RELEASE_NOT_OWNED) {
+ zend_type_error("Cannot release ownership as memory is not owned for argument $argnum of $&1_descriptor of $symname");
+ return;
+ } else {
+ zend_type_error("Expected $&1_descriptor for argument $argnum of $symname");
+ return;
+ }
+ }
+ if (!argp) {
+ zend_type_error("Invalid null reference for argument $argnum of $&1_descriptor of $symname");
+ return;
+ }
+ SwigValueWrapper< $1_ltype >::reset($1, ($&1_type)argp);
+}
diff --git a/Lib/python/swigmove.i b/Lib/python/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/python/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/r/swigmove.i b/Lib/r/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/r/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/ruby/swigmove.i b/Lib/ruby/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/ruby/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/scilab/swigmove.i b/Lib/scilab/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/scilab/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/swig.swg b/Lib/swig.swg
index 6cac0e377..9f9d53349 100644
--- a/Lib/swig.swg
+++ b/Lib/swig.swg
@@ -656,6 +656,16 @@ namespace std {
* arg1 = *inarg1; // Assignment from a pointer
* arg1 = Vector(1,2,3); // Assignment from a value
*
+ * SwigValueWrapper is a drop in replacement to modify normal value semantics by
+ * using the heap instead of the stack to copy/move the underlying object it is
+ * managing. Smart pointers also manage an underlying object on the heap, so
+ * SwigValueWrapper has characteristics of a smart pointer. The reset function
+ * is specific smart pointer functionality, but cannot be a non-static member as
+ * when SWIG modifies typemap code it assumes non-static member function calls
+ * are routed to the underlying object, changing for example $1.f() to (&x)->f().
+ * The reset function was added as an optimisation to avoid some copying/moving
+ * and to take ownership of an object already created on the heap.
+ *
* The class offers a strong guarantee of exception safety.
* With regards to the implementation, the private SwigSmartPointer nested class is
* a simple smart pointer providing exception safety, much like std::auto_ptr.
@@ -677,6 +687,7 @@ template<typename T> class SwigValueWrapper {
SwigSmartPointer(T *p) : ptr(p) { }
~SwigSmartPointer() { delete ptr; }
SwigSmartPointer& operator=(SwigSmartPointer& rhs) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = rhs.ptr; rhs.ptr = 0; return *this; }
+ void reset(T *p) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = p; }
} pointer;
SwigValueWrapper& operator=(const SwigValueWrapper<T>& rhs);
SwigValueWrapper(const SwigValueWrapper<T>& rhs);
@@ -690,6 +701,7 @@ public:
operator T&() const { return *pointer.ptr; }
#endif
T *operator&() const { return pointer.ptr; }
+ static void reset(SwigValueWrapper& t, T *p) { t.pointer.reset(p); }
};
/*
diff --git a/Lib/tcl/swigmove.i b/Lib/tcl/swigmove.i
new file mode 100644
index 000000000..62ecca768
--- /dev/null
+++ b/Lib/tcl/swigmove.i
@@ -0,0 +1 @@
+%include <typemaps/swigmove.swg>
diff --git a/Lib/typemaps/swigmove.swg b/Lib/typemaps/swigmove.swg
new file mode 100644
index 000000000..b0a296850
--- /dev/null
+++ b/Lib/typemaps/swigmove.swg
@@ -0,0 +1,19 @@
+/* -----------------------------------------------------------------------------
+ * swigmove.swg
+ *
+ * Input typemaps library for implementing full move semantics when passing
+ * parameters by value.
+ * ----------------------------------------------------------------------------- */
+
+%typemap(in, noblock=1) SWIGTYPE MOVE (void *argp = 0, int res = 0) {
+ res = SWIG_ConvertPtr($input, &argp, $&1_descriptor, SWIG_POINTER_RELEASE);
+ if (!SWIG_IsOK(res)) {
+ if (res == SWIG_ERROR_RELEASE_NOT_OWNED) {
+ %releasenotowned_fail(res, "$1_type", $symname, $argnum);
+ } else {
+ %argument_fail(res, "$1_type", $symname, $argnum);
+ }
+ }
+ if (!argp) { %argument_nullref("$1_type", $symname, $argnum); }
+ SwigValueWrapper< $1_ltype >::reset($1, ($&1_type)argp);
+}