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 @@
@@ -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: +
+ ++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. +
++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 @@