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:
parent
de65875955
commit
dad7c93ca0
41 changed files with 909 additions and 19 deletions
|
|
@ -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>
|
||||
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 <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>
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue