diff options
author | William S Fulton <wsf@fultondesigns.co.uk> | 2022-09-16 08:36:25 +0100 |
---|---|---|
committer | William S Fulton <wsf@fultondesigns.co.uk> | 2022-09-16 08:36:25 +0100 |
commit | dad7c93ca0a923e7d2671347cee711d180dd0338 (patch) | |
tree | e4f527a56b5a1e044213ebe1f07200cedcde8153 | |
parent | de65875955b236ee2644014e41bdeb1d574acc93 (diff) | |
download | swig-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
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 & operator=(MoveOnly &&) = 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 &) = 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); +}; +</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 >=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 <swigmove.i> +%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< 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 +} +</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 <swigmove.i> +%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); +} |