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
This commit is contained in:
William S Fulton 2022-09-16 08:36:25 +01:00
commit dad7c93ca0
41 changed files with 909 additions and 19 deletions

View file

@ -18,7 +18,7 @@
<ul>
<li><a href="#CPlusPlus11_rvalue_reference_inputs">Rvalue reference inputs</a>
<li><a href="#CPlusPlus11_rvalue_reference_outputs">Rvalue reference outputs</a>
<li><a href="#CPlusPlus11_move_only">Movable and move-only types</a>
<li><a href="#CPlusPlus11_move_only">Movable and move-only types by value</a>
</ul>
<li><a href="#CPlusPlus11_generalized_constant_expressions">Generalized constant expressions</a>
<li><a href="#CPlusPlus11_extern_template">Extern template</a>
@ -240,7 +240,7 @@ Another alternative would be to modify the output rvalue reference typemap to al
Fortunately you're highly unlikely to have to solve any of these issues!
</p>
<H4><a name="CPlusPlus11_move_only">7.2.1.3 Movable and move-only types</a></H4>
<H4><a name="CPlusPlus11_move_only">7.2.1.3 Movable and move-only types by value</a></H4>
<p>
@ -252,7 +252,10 @@ Movable types can appear in function signatures for passing 'by value' and in C+
</p>
<p>
SWIG has been enhanced with support for both copyable and/or movable types but this is currently just for function return values.
SWIG has support for both copyable and/or movable types.
Support for move semantics is quite seamless when returning by value from a function.
Support for move semantics is less so and may require some customisation when passing by value to a function.
First let's consider returning by value from a function.
</p>
<p>
@ -283,6 +286,7 @@ struct MoveOnly {
MoveOnly &amp; operator=(MoveOnly &amp;&amp;) = default;
static MoveOnly create() { return MoveOnly(); }
static void take(MoveOnly mo);
};
</pre></div>
@ -303,17 +307,193 @@ SWIGEXPORT void * SWIGSTDCALL CSharp_MoveOnly_create() {
<p>
<tt>SwigValueWrapper</tt> is covered in <a href="SWIGPlus.html#SWIGPlus_nn19">Pass and return by value</a>.
Note that the generated code could be optimised further using the <a href="Typemaps.html#Typemaps_optimal">"optimal" attribute</a> in the "out" typemap.
Note that the generated code could be optimised further using the <a href="Typemaps.html#Typemaps_optimal">"optimal" attribute</a>
in the "out" typemap, so if the above typemap is customised as follows (note that this is C# specific):
</p>
<div class="code"><pre>
%typemap(out, optimal="1") MoveOnly %{
$result = new $1_ltype($1);
%}
</pre></div>
<p>
then the generated code will result in the object being optimally moved:
</p>
<div class="code"><pre>
SWIGEXPORT void * SWIGSTDCALL CSharp_MoveOnly_create() {
void * jresult ;
jresult = new MoveOnly(MoveOnly::create());
return jresult;
}
</pre></div>
<p>
Now let's consider passing by value.
We'll consider three cases; namely types that are:
</p>
<ol>
<li> Copyable and not movable - <tt>CopyOnly</tt>.</li>
<li> Copyable and movable - <tt>MovableCopyable</tt>.</li>
<li> Movable and not copyable - <tt>MoveOnly</tt>.</li>
</ol>
<p>
and for clarification, define these two additional types as follows:
</p>
<div class="code"><pre>
struct CopyOnly {
int val;
CopyOnly(): val(0) {}
CopyOnly(const CopyOnly &amp;) = default;
CopyOnly &amp; operator=(const CopyOnly &amp;) = default;
static CopyOnly create() { return CopyOnly(); }
static void take(CopyOnly co);
};
struct MovableCopyable {
int val;
MovableCopyable(): val(0) {}
MovableCopyable(const MovableCopyable &amp;) = default;
MovableCopyable(MovableCopyable &amp;&amp;) = default;
MovableCopyable &amp; operator=(const MovableCopyable &amp;) = default;
MovableCopyable &amp; operator=(MovableCopyable &amp;&amp;) = default;
static MovableCopyable create() { return MovableCopyable(); }
static void take(MovableCopyable mc);
};
</pre></div>
<p>
The generated code is shown below for <tt>CopyOnly::take</tt> (with additional comments for when constructors and assignment operators are called).
While the code shown is C# specific, the generated constructor and/or assignment operator calls are ultimately the same for all target languages.
</p>
<div class="code"><pre>
SWIGEXPORT void SWIGSTDCALL CSharp_CopyOnly_take(void * jarg1) {
CopyOnly arg1 ; // (a) Default constructor
CopyOnly *argp1 ;
argp1 = (CopyOnly *)jarg1;
if (!argp1) {
SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null CopyOnly", 0);
return ;
}
arg1 = *argp1; // (b) Copy assignment
CopyOnly::take(SWIG_STD_MOVE(arg1)); // (c) Copy constructor
}
</pre></div>
<p>
Note that <tt>SWIG_STD_MOVE</tt> is a macro defined as shown below to use <tt>std::move</tt> which is only available from C++11 onwards:
</p>
<div class="code"><pre>
#if __cplusplus &gt;=201103L
# define SWIG_STD_MOVE(OBJ) std::move(OBJ)
#else
# define SWIG_STD_MOVE(OBJ) OBJ
#endif
</pre></div>
<p>
Also note: <i>(c) Copy constructor</i>.
Yes, when passing by value the copy constructor is called for all versions of C++, even C++11 and later even though std::move is specified.
It's a C++ language feature for types that don't have move semantics!
</p>
<p>
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 <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>
Simply add the following before the <tt>MovableCopyable::take</tt> method is parsed:
</p>
<div class="code"><pre>
%valuewrapper MovableCopyable;
%include &lt;swigmove.i&gt;
%apply SWIGTYPE MOVE { MovableCopyable }
</pre></div>
<p>
will result in this optimal code where just one move constructor is invoked:
</p>
<div class="code"><pre>
SWIGEXPORT void SWIGSTDCALL CSharp_MovableCopyable_take(void * jarg1) {
SwigValueWrapper&lt; MovableCopyable &gt; arg1 ; // (a) No constructors invoked
MovableCopyable *argp1 ;
argp1 = (MovableCopyable *)jarg1;
if (!argp1) {
SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null MovableCopyable", 0);
return ;
}
SwigValueWrapper&lt; MovableCopyable &gt;::reset(arg1, argp1); // (b) No constructor or assignment operator invoked
MovableCopyable::take(SWIG_STD_MOVE(arg1)); // (c) Move constructor
}
</pre></div>
<p>
Note that <tt>SwigValueWrapper</tt> will call the destructor for the pointer passed to it in the <tt>reset</tt> function.
This pointer is the underlying C++ object that the proxy class owns.
The details aren't shown, but the 'csin' typemap also generates C# code to ensure that the proxy class releases ownership of the object.
Please see the 'SWIGTYPE MOVE' typemaps in the swigmove.i file provided for each target language.
Therefore full move semantics are implemented; ownership is moved from the proxy class into the C++ layer and the net effect
is the same as using an <a href="#CPlusPlus11_rvalue_reference_inputs">rvalue reference parameter</a> discussed earlier.
</p>
<p>
Lastly, let's consider the <tt>MoveOnly::take</tt> function defined earlier.
By default the generated code fails to compile as <tt>MoveOnly</tt> does not have a copy assignment operator.
SWIG is not designed to select a different typemap automatically for move-only types and the user
must apply the SWIGTYPE MOVE typemaps to ensure that only move-only semantics are used.
However, SWIG is able to automatically use <tt>%feature("valuewrapper")</tt> for move-only
types so it is not necessary to explicitly use this feature.
So in this move-only case, simply add the following before <tt>MoveOnly::take</tt> is parsed, which results in the same optimal code shown above for <tt>MovableCopyable</tt>:
</p>
<div class="code"><pre>
%include &lt;swigmove.i&gt;
%apply SWIGTYPE MOVE { MoveOnly }
</pre></div>
<p>
<b>Compatibility note:</b>
SWIG-4.1.0 introduced support for taking advantage of types with move semantics and wrapping functions that return movable or move-only types 'by value'.
SWIG-4.1.0 introduced support for taking advantage of types with move semantics and making it possible to easily use move only types.
</p>