From dad7c93ca0a923e7d2671347cee711d180dd0338 Mon Sep 17 00:00:00 2001
From: William S Fulton
Date: Fri, 16 Sep 2022 08:36:25 +0100
Subject: [PATCH] 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
---
CHANGES.current | 4 +
Doc/Manual/CPlusPlus11.html | 194 +++++++++++++++++-
Doc/Manual/Contents.html | 2 +-
Examples/test-suite/common.mk | 5 +-
Examples/test-suite/cpp11_move_only.i | 23 ++-
Examples/test-suite/cpp11_move_typemaps.i | 12 ++
.../csharp/cpp11_move_typemaps_runme.cs | 37 ++++
.../d/cpp11_move_typemaps_runme.1.d | 42 ++++
.../d/cpp11_move_typemaps_runme.2.d | 42 ++++
.../guile/cpp11_move_typemaps_runme.scm | 3 +
.../java/cpp11_move_typemaps_runme.java | 51 +++++
.../javascript/cpp11_move_typemaps_runme.js | 30 +++
.../lua/cpp11_move_typemaps_runme.lua | 28 +++
.../mzscheme/cpp11_move_typemaps_runme.scm | 35 ++++
.../octave/cpp11_move_typemaps_runme.m | 37 ++++
.../perl5/cpp11_move_typemaps_runme.pl | 34 +++
.../php/cpp11_move_typemaps_runme.php | 32 +++
.../python/cpp11_move_typemaps_runme.py | 29 +++
.../ruby/cpp11_move_typemaps_runme.rb | 36 ++++
.../schemerunme/cpp11_move_typemaps.scm | 23 +++
.../tcl/cpp11_move_typemaps_runme.tcl | 35 ++++
Lib/csharp/swigmove.i | 16 ++
Lib/d/swigmove.i | 16 ++
Lib/go/swigmove.i | 15 ++
Lib/guile/swigmove.i | 19 ++
Lib/java/swigmove.i | 16 ++
Lib/javascript/jsc/swigmove.i | 1 +
Lib/javascript/v8/swigmove.i | 1 +
Lib/lua/swigmove.i | 18 ++
Lib/mzscheme/swigmove.i | 19 ++
Lib/ocaml/swigmove.i | 11 +
Lib/octave/swigmove.i | 1 +
Lib/perl5/swigmove.i | 1 +
Lib/php/swigmove.i | 24 +++
Lib/python/swigmove.i | 1 +
Lib/r/swigmove.i | 1 +
Lib/ruby/swigmove.i | 1 +
Lib/scilab/swigmove.i | 1 +
Lib/swig.swg | 12 ++
Lib/tcl/swigmove.i | 1 +
Lib/typemaps/swigmove.swg | 19 ++
41 files changed, 909 insertions(+), 19 deletions(-)
create mode 100644 Examples/test-suite/cpp11_move_typemaps.i
create mode 100644 Examples/test-suite/csharp/cpp11_move_typemaps_runme.cs
create mode 100644 Examples/test-suite/d/cpp11_move_typemaps_runme.1.d
create mode 100644 Examples/test-suite/d/cpp11_move_typemaps_runme.2.d
create mode 100644 Examples/test-suite/guile/cpp11_move_typemaps_runme.scm
create mode 100644 Examples/test-suite/java/cpp11_move_typemaps_runme.java
create mode 100644 Examples/test-suite/javascript/cpp11_move_typemaps_runme.js
create mode 100644 Examples/test-suite/lua/cpp11_move_typemaps_runme.lua
create mode 100644 Examples/test-suite/mzscheme/cpp11_move_typemaps_runme.scm
create mode 100644 Examples/test-suite/octave/cpp11_move_typemaps_runme.m
create mode 100644 Examples/test-suite/perl5/cpp11_move_typemaps_runme.pl
create mode 100644 Examples/test-suite/php/cpp11_move_typemaps_runme.php
create mode 100644 Examples/test-suite/python/cpp11_move_typemaps_runme.py
create mode 100644 Examples/test-suite/ruby/cpp11_move_typemaps_runme.rb
create mode 100644 Examples/test-suite/schemerunme/cpp11_move_typemaps.scm
create mode 100644 Examples/test-suite/tcl/cpp11_move_typemaps_runme.tcl
create mode 100644 Lib/csharp/swigmove.i
create mode 100644 Lib/d/swigmove.i
create mode 100644 Lib/go/swigmove.i
create mode 100644 Lib/guile/swigmove.i
create mode 100644 Lib/java/swigmove.i
create mode 100644 Lib/javascript/jsc/swigmove.i
create mode 100644 Lib/javascript/v8/swigmove.i
create mode 100644 Lib/lua/swigmove.i
create mode 100644 Lib/mzscheme/swigmove.i
create mode 100644 Lib/ocaml/swigmove.i
create mode 100644 Lib/octave/swigmove.i
create mode 100644 Lib/perl5/swigmove.i
create mode 100644 Lib/php/swigmove.i
create mode 100644 Lib/python/swigmove.i
create mode 100644 Lib/r/swigmove.i
create mode 100644 Lib/ruby/swigmove.i
create mode 100644 Lib/scilab/swigmove.i
create mode 100644 Lib/tcl/swigmove.i
create mode 100644 Lib/typemaps/swigmove.swg
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 @@
Generalized constant expressions
Extern template
@@ -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!
-
+
@@ -252,7 +252,10 @@ Movable types can appear in function signatures for passing 'by value' and in C+
-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.
@@ -283,6 +286,7 @@ struct MoveOnly {
MoveOnly & operator=(MoveOnly &&) = default;
static MoveOnly create() { return MoveOnly(); }
+ static void take(MoveOnly mo);
};
@@ -303,17 +307,193 @@ SWIGEXPORT void * SWIGSTDCALL CSharp_MoveOnly_create() {
SwigValueWrapper is covered in Pass and return by value.
-Note that the generated code could be optimised further using the "optimal" attribute in the "out" typemap.
+Note that the generated code could be optimised further using the "optimal" attribute
+in the "out" typemap, so if the above typemap is customised as follows (note that this is C# specific):
+
+
+
+%typemap(out, optimal="1") MoveOnly %{
+ $result = new $1_ltype($1);
+%}
+
+
+
+then the generated code will result in the object being optimally moved:
+
+
+
+SWIGEXPORT void * SWIGSTDCALL CSharp_MoveOnly_create() {
+ void * jresult ;
+ jresult = new MoveOnly(MoveOnly::create());
+ return jresult;
+}
+
+
+
+Now let's consider passing by value.
+We'll consider three cases; namely types that are:
+
+
+
+ - Copyable and not movable - CopyOnly.
+ - Copyable and movable - MovableCopyable.
+ - Movable and not copyable - MoveOnly.
+
+
+
+and for clarification, define these two additional types as follows:
+
+
+
+struct CopyOnly {
+ int val;
+ CopyOnly(): val(0) {}
+
+ CopyOnly(const CopyOnly &) = default;
+ CopyOnly & operator=(const CopyOnly &) = default;
+
+ static CopyOnly create() { return CopyOnly(); }
+ static void take(CopyOnly co);
+};
+
+struct MovableCopyable {
+ int val;
+ MovableCopyable(): val(0) {}
+
+ MovableCopyable(const MovableCopyable &) = default;
+ MovableCopyable(MovableCopyable &&) = default;
+ MovableCopyable & operator=(const MovableCopyable &) = default;
+ MovableCopyable & operator=(MovableCopyable &&) = default;
+
+ static MovableCopyable create() { return MovableCopyable(); }
+ static void take(MovableCopyable mc);
+};
+
+
+
+The generated code is shown below for CopyOnly::take (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.
+
+
+
+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
+}
+
+
+
+Note that SWIG_STD_MOVE is a macro defined as shown below to use std::move which is only available from C++11 onwards:
+
+
+
+#if __cplusplus >=201103L
+# define SWIG_STD_MOVE(OBJ) std::move(OBJ)
+#else
+# define SWIG_STD_MOVE(OBJ) OBJ
+#endif
+
+
+
+Also note: (c) Copy constructor.
+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!
-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.
+The generated code for MovableCopyable::take is the same as for CopyOnly::take, however, the C++ compiler will choose the move constructor this time where commented (c) Move constructor:
+
+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
+}
+
+
+
+There are two optimisation opportunities available.
+
+
+ - Remove the default constructor call with the %feature("valuewrapper") covered in Pass and return by value and replace it with SwigValueWrapper.
+
+ - 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 SwigValueWrapper::reset, which works much like std::unique_ptr::reset.
+ These typemaps could alternatively have replaced the copy assignment with a move assignment, but this is not maximally optimal.
+
+
+
+Simply add the following before the MovableCopyable::take method is parsed:
+
+
+
+%valuewrapper MovableCopyable;
+%include <swigmove.i>
+%apply SWIGTYPE MOVE { MovableCopyable }
+
+
+
+will result in this optimal code where just one move constructor is invoked:
+
+
+
+SWIGEXPORT void SWIGSTDCALL CSharp_MovableCopyable_take(void * jarg1) {
+ SwigValueWrapper< MovableCopyable > arg1 ; // (a) No constructors invoked
+ MovableCopyable *argp1 ;
+
+ argp1 = (MovableCopyable *)jarg1;
+ if (!argp1) {
+ SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null MovableCopyable", 0);
+ return ;
+ }
+ SwigValueWrapper< MovableCopyable >::reset(arg1, argp1); // (b) No constructor or assignment operator invoked
+ MovableCopyable::take(SWIG_STD_MOVE(arg1)); // (c) Move constructor
+}
+
+
+
+Note that SwigValueWrapper will call the destructor for the pointer passed to it in the reset 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 rvalue reference parameter discussed earlier.
+
+
+
+Lastly, let's consider the MoveOnly::take function defined earlier.
+By default the generated code fails to compile as MoveOnly 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 %feature("valuewrapper") 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 MoveOnly::take is parsed, which results in the same optimal code shown above for MovableCopyable:
+
+
+
+%include <swigmove.i>
+%apply SWIGTYPE MOVE { MoveOnly }
+
+
Compatibility note:
-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.
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 @@
Generalized constant expressions
Extern template
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
+%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 @@
+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="") 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
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
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
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
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
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
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
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
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 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& rhs);
SwigValueWrapper(const SwigValueWrapper& 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
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);
+}