Fix overloading of shared_ptr method overloading

Add 'equivalent' attribute to typecheck typemap.
Closes #1098.
This commit is contained in:
William S Fulton 2017-09-23 14:46:44 +01:00
commit ed4b84f4d3
24 changed files with 378 additions and 15 deletions

View file

@ -381,6 +381,7 @@
<ul>
<li><a href="Library.html#Library_shared_ptr_basics">shared_ptr basics</a>
<li><a href="Library.html#Library_shared_ptr_inheritance">shared_ptr and inheritance</a>
<li><a href="Library.html#Library_shared_ptr_overloading">shared_ptr and method overloading</a>
<li><a href="Library.html#Library_shared_ptr_templates">shared_ptr and templates</a>
<li><a href="Library.html#Library_shared_ptr_directors">shared_ptr and directors</a>
</ul>
@ -500,6 +501,9 @@
<li><a href="Typemaps.html#Typemaps_runtime_type_checker_usage">Usage</a>
</ul>
<li><a href="Typemaps.html#Typemaps_overloading">Typemaps and overloading</a>
<ul>
<li><a href="Typemaps.html#Typemaps_typecheck_pointer">SWIG_TYPECHECK_POINTER precedence level and the typecheck typemap</a>
</ul>
<li><a href="Typemaps.html#Typemaps_nn48">More about %apply and %clear</a>
<li><a href="Typemaps.html#Typemaps_nn47">Passing data between typemaps</a>
<li><a href="Typemaps.html#Typemaps_nn52">C++ "this" pointer</a>

View file

@ -35,6 +35,7 @@
<ul>
<li><a href="#Library_shared_ptr_basics">shared_ptr basics</a>
<li><a href="#Library_shared_ptr_inheritance">shared_ptr and inheritance</a>
<li><a href="#Library_shared_ptr_overloading">shared_ptr and method overloading</a>
<li><a href="#Library_shared_ptr_templates">shared_ptr and templates</a>
<li><a href="#Library_shared_ptr_directors">shared_ptr and directors</a>
</ul>
@ -1921,7 +1922,30 @@ Adding the missing <tt>%shared_ptr</tt> macros will fix this:
</pre>
</div>
<H4><a name="Library_shared_ptr_templates">9.4.4.3 shared_ptr and templates</a></H4>
<H4><a name="Library_shared_ptr_overloading">9.4.4.3 shared_ptr and method overloading</a></H4>
<p>
A C++ compiler can disambiguate a method overloaded by a shared_ptr and one using the raw underlying type.
For example, either one of these methods can be called in C++:
</p>
<div class="code">
<pre>
int age(std::shared_ptr&lt;GrandParent&gt; num);
int age(GrandParent&amp; num);
</pre>
</div>
<p>
When wrapped by SWIG, disambiguation is not possible using the overloaded names as there is just one equivalent type (<tt>GrandParent</tt>) in the target language.
SWIG will choose to wrap just the first method by default.
<a href="SWIGPlus.html#SWIGPlus_nn25">Ambiguity in overloading</a> discusses ways to control which method(s) gets wrapped using <tt>%ignore</tt> or <tt>%rename</tt>.
For the interested reader, SWIG detects that they are equivalent types via the <a href=Typemaps.html#Typemaps_typecheck_pointer>typecheck typemaps</a> in the shared_ptr library.
</p>
<H4><a name="Library_shared_ptr_templates">9.4.4.4 shared_ptr and templates</a></H4>
<p>
The <tt>%shared_ptr</tt> macro should be used for all the required instantiations
@ -1962,7 +1986,7 @@ The SWIG code below shows the required ordering:
</pre>
</div>
<H4><a name="Library_shared_ptr_directors">9.4.4.4 shared_ptr and directors</a></H4>
<H4><a name="Library_shared_ptr_directors">9.4.4.5 shared_ptr and directors</a></H4>
<p>

View file

@ -89,6 +89,9 @@
<li><a href="#Typemaps_runtime_type_checker_usage">Usage</a>
</ul>
<li><a href="#Typemaps_overloading">Typemaps and overloading</a>
<ul>
<li><a href="#Typemaps_typecheck_pointer">SWIG_TYPECHECK_POINTER precedence level and the typecheck typemap</a>
</ul>
<li><a href="#Typemaps_nn48">More about %apply and %clear</a>
<li><a href="#Typemaps_nn47">Passing data between typemaps</a>
<li><a href="#Typemaps_nn52">C++ "this" pointer</a>
@ -4754,7 +4757,8 @@ then the type is given a precedence higher than any other known precedence level
<div class="shell">
<pre>
example.i:18: Warning 467: Overloaded method foo(int) not supported (incomplete type checking rule - no precedence level in typecheck typemap for 'int').
example.i:18: Warning 467: Overloaded method foo(int) not supported (incomplete type
checking rule - no precedence level in typecheck typemap for 'int').
</pre>
</div>
@ -4779,10 +4783,112 @@ simply check the type of the first array element and use that to dispatch to the
Subsequent "in" typemaps would then perform more extensive type-checking.
</li>
<li>Make sure you read the section on overloading in the "<a href="SWIGPlus.html#SWIGPlus">SWIG and C++</a>" chapter.
<li>Make sure you read the section on <a href="SWIGPlus.html#SWIGPlus_overloaded_methods">overloading</a> in the SWIG and C++ chapter.
</li>
</ul>
<H3><a name="Typemaps_typecheck_pointer">11.13.1 SWIG_TYPECHECK_POINTER precedence level and the typecheck typemap</a></H3>
<p>
When it comes to overloading of a particular type passed by value, pointer or reference (const and non-const),
a C++ compiler can disambiguate which overloaded function to call.
However, SWIG effectively treats these as pointers in the target language and thus as equivalent types.
For example, consider:
</p>
<div class="code">
<pre>
class X { ... };
void m(X const &amp;c); // equivalent: void m(X *c);
void m(X &amp;r); // equivalent: void m(X *r);
void m(X *p); // equivalent: void m(X *p);
</pre>
</div>
<p>
These cannot be disambiguated in the target languages and so SWIG will choose the first method and ignore the subsequent two methods.
The scripting languages do this by using the overload dispatch mechanism described earlier and warnings indicate this:
</p>
<div class="shell">
<pre>
example.i:6: Warning 509: Overloaded method m(X &amp;) effectively ignored,
example.i:5: Warning 509: as it is shadowed by m(X const &amp;).
example.i:7: Warning 509: Overloaded method m(X *) effectively ignored,
example.i:5: Warning 509: as it is shadowed by m(X const &amp;).
</pre>
</div>
<p>
The statically typed languages like Java and C# automatically ignore all but the first equivalent overloaded methods with warnings:
</p>
<div class="shell">
<pre>
example.i:6: Warning 516: Overloaded method m(X &amp;) ignored,
example.i:5: Warning 516: using m(X const &amp;) instead.
example.i:7: Warning 516: Overloaded method m(X *) ignored,
example.i:5: Warning 516: using m(X const &amp;) instead.
</pre>
</div>
<p>
You can select the overloaded method you would like to wrap by ignoring the other two with <tt>%ignore</tt> or rename two of them with <tt>%rename</tt>
and this will of course remove the warnings too.
The problem of ambiguity is also discussed in the C++ chapter on <a href="SWIGPlus.html#SWIGPlus_overloaded_methods">overloading</a>.
</p>
<p>
So how does this work with respect to typemaps?
The typemaps SWIG provides to handle overloading for these three methods are from the SWIGTYPE family.
As discussed earlier, in <a href="Typemaps.html#Typemaps_nn19">Default typemap matching rules</a>,
the <tt>SWIGTYPE &amp;</tt> typemaps are used for references and <tt>SWIGTYPE *</tt> typemaps are used for pointers.
SWIG uses the special <tt>SWIG_TYPECHECK_POINTER</tt> (0) precedence level to handle these types in the "typecheck" typemap:
</p>
<div class="code">
<pre>
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) SWIGTYPE &amp; "..."
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) SWIGTYPE * "..."
</pre>
</div>
<p>
When the SWIGTYPE "typecheck" typemaps use the <tt>SWIG_TYPECHECK_POINTER</tt> precedence level,
SWIG converts the type to a pointer equivalent type and then uses the equivalent type to detect if it can be disambiguated in an overloaded method in the target language.
In our example above, the equivalent types for <tt>X const &amp;</tt>, <tt>X &amp;</tt> and <tt>X *</tt> are all <tt>X *</tt>.
As they are the same, they cannot be disambiguated and so just the first overloaded method is chosen.
</p>
<p>
The automatic conversion to equivalent types and subsequent type comparison is triggered via the use of the special <tt>SWIG_TYPECHECK_POINTER</tt> precedence level
and works for types passed by value, pointer and reference.
Alas, there are more ways to overload a method that also need handling.
C++ smart pointers are such a type which can be disambiguated by a C++ compiler but not automatically by SWIG.
SWIG does not automatically know that a smart pointer has an equivalent type, but it can be told manually.
Just specify the 'equivalent' attribute in the "typecheck" typemap with a pointer to the underlying type.
</p>
<div class="code">
<pre>
%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER, equivalent="X *") MySmartPtr&lt;X&gt; " ... "
void m(X &amp;r); // equivalent: void m(X *r);
void m(MySmartPtr&lt;X&gt; s); // equivalent: void m(X *s);
</pre>
</div>
<p>
Now SWIG will detect the two types are equivalent and generate valid code by wrapping just the first overloaded method.
You can of course choose which method to wrap by ignoring one of them with <tt>%ignore</tt>.
Otherwise both can be wrapped by removing the overloading name ambiguity by renaming one of them with <tt>%rename</tt>.
</p>
<p>
The 'equivalent' attribute is used in the implementation for the <a href="Library.html#Library_std_shared_ptr">shared_ptr smart pointer</a> library.
</p>
<H2><a name="Typemaps_nn48">11.14 More about %apply and %clear</a></H2>