%apply and typedefs improvement

Perform repeated typedef lookups instead of a single typedef
lookup on the type being applied in %apply when looking for a family
of typemaps to apply.

Closes #3064
This commit is contained in:
William S Fulton 2024-11-09 13:25:41 +00:00
parent 40b6cc684f
commit d9edb22d1c
7 changed files with 157 additions and 7 deletions

View File

@ -7,6 +7,11 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.4.0 (in progress)
===========================
2024-11-09: wsfulton
#3064 Perform repeated typedef lookups instead of a single typedef
lookup on the type being applied in %apply when looking for a family
of typemaps to apply.
2024-11-01: wsfulton
Fix internal error handling parameters that are typedefs to references
when using the compactdefaultargs feature.

View File

@ -617,6 +617,9 @@
<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>
<ul>
<li><a href="Typemaps.html#Typemaps_apply_typedefs">Typedefs and %apply</a>
</ul>
<li><a href="Typemaps.html#Typemaps_nn47">Passing data between typemaps</a>
<li><a href="Typemaps.html#Typemaps_nn52">C++ "this" pointer</a>
<li><a href="Typemaps.html#Typemaps_nn51">Where to go for more information?</a>

View File

@ -98,6 +98,9 @@
<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>
<ul>
<li><a href="#Typemaps_apply_typedefs">Typedefs and %apply</a>
</ul>
<li><a href="#Typemaps_nn47">Passing data between typemaps</a>
<li><a href="#Typemaps_nn52">C++ "this" pointer</a>
<li><a href="#Typemaps_nn51">Where to go for more information?</a>
@ -5104,7 +5107,6 @@ For example:
%typemap(check) int *POSITIVE {
if (*$1 &lt;= 0) {
SWIG_exception(SWIG_ValueError, "Expected a positive number!\n");
return NULL;
}
}
@ -5144,6 +5146,88 @@ will delete the typemaps for all the typemap methods; namely "in", "check" and "
</pre>
</div>
<H3><a name="Typemaps_apply_typedefs">14.14.1 Typedefs and %apply</a></H3>
<p>
Consider a similar example to that shown earlier but this time with a couple of typedefs:
</p>
<div class="code">
<pre>
%typemap(check) int *NEGATIVE {
if (*$1 &gt;= 0) {
SWIG_exception(SWIG_ValueError, "Expected a negative number!\n");
}
}
typedef int integer;
typedef integer negative_integer;
void fff(negative_integer *number);
</pre>
</div>
<p>
Typically <tt>%apply</tt> is specified with the exact type <i>being applied</i>, such as <tt>int *</tt> in the following case:
</p>
<div class="code">
<pre>
%apply int *NEGATIVE { negative_integer *number };
</pre>
</div>
<p>
to find an exact typemap match: <tt>int *NEGATIVE</tt>.
</p>
<p>
However, the following will also work in this case:
</p>
<div class="code">
<pre>
%apply negative_integer *NEGATIVE { negative_integer *number };
</pre>
</div>
<p>
as SWIG will use the typedefs to find a match. Typemaps will be searched for until one matches by following the typedefs in the expected order:
</p>
<ul>
<li> <tt>negative_integer *NEGATIVE</tt>
<li> <tt>integer *NEGATIVE</tt>
<li> <tt>int *NEGATIVE</tt>
</ul>
<p>
Note that the search looks for a <b>family of typemaps</b> and will stop when a typemap with <b>any matching method name</b> is found.
So for example, consider the addition of an "argout" typemap method to the example:
</p>
<div class="code">
<pre>
%typemap(argout) negative_integer *NEGATIVE {
// return value somehow
}
</pre>
</div>
<p>
There is then at least one typemap method creating a family of typemaps: <tt>negative_integer *NEGATIVE</tt>.
As the search halts on the closest match when chasing typedefs,
and this family of typemaps does not have a "check" typemap method, there is no "check" typemap method to apply.
</p>
<p>
In summary, the addition of the better matching "argout" typemap
for <tt>negative_integer *NEGATIVE</tt>
introduces a new family of typemaps resulting in the previously matching "check" typemap
for <tt>int *NEGATIVE</tt>
no longer being available for applying.
</p>
<H2><a name="Typemaps_nn47">14.15 Passing data between typemaps</a></H2>

View File

@ -0,0 +1,44 @@
%module apply_typemap_typedefs
%typemap(in) int "_this_will_not_compile_"
%typemap(in) int OK "$1 = 10;" // (A)
%typemap(in) Int1 OKAY "$1 = 20;" // (B)
%inline %{
typedef int Int1;
typedef Int1 Int2;
typedef Int2 Int3;
%}
%apply Int2 OK {Int2 i2} // applies (A) as 'Int2 OK' resolves to 'int OK'
%inline %{
int test10(Int2 i2) { return i2; }
%}
%apply Int3 OKAY {Int3 i3} // applies (B) as 'Int3 OKAY' resolves to 'Int1 OKAY'
%inline %{
int test20(Int3 i3) { return i3; }
%}
%typemap(in) int EXACT "_this_too_will_not_compile_"
%typemap(in) Int1 EXACT "_this_too_will_not_compile_"
%typemap(in) Int2 EXACT "_this_too_will_not_compile_"
%typemap(in) Int3 EXACT "$1 = 30;"
%apply Int3 EXACT {Int3 i3}
%inline %{
int test30(Int3 i3) { return i3; }
%}
%typemap(in) int CONFUSING "_this_too_will_not_compile_"
%typemap(in) int ii "$1 = 40;" // (D)
%typemap(in) Int1 CONFUSING "_this_too_will_not_compile_"
%typemap(in) Int2 CONFUSING "_this_too_will_not_compile_"
%typemap(check) Int3 CONFUSING "/*check typemap*/"
%apply Int3 CONFUSING {Int3 ii} // Only applies the (check) typemap above, the other CONFUSING typemaps are not applied as the repeated typedef lookups halts as soon as any typemap method matches
%inline %{
int test40(Int3 ii) { return ii; } // Uses (D) using normal typemap lookup rules as the %apply above does not provide an 'in' typemap method in the family of 'Int3 CONFUSING' typemaps
%}

View File

@ -125,6 +125,7 @@ CPP_TEST_CASES += \
anonymous_bitfield \
apply_signed_char \
apply_strings \
apply_typemap_typedefs \
argcargvtest \
argout \
array_member \

View File

@ -0,0 +1,10 @@
from apply_typemap_typedefs import *
if test10(10) != 10:
raise RuntimeError
if test20(20) != 20:
raise RuntimeError
if test30(30) != 30:
raise RuntimeError
if test40(40) != 40:
raise RuntimeError

View File

@ -494,20 +494,23 @@ int Swig_typemap_apply(ParmList *src, ParmList *dest) {
/* See if there is a matching typemap in this scope */
sm = typemap_get(type, name);
/* if there is not matching, look for a typemap in the
original typedef, if any, like in:
/* If there is no exact match on the type, look for a typemap by resolving the type
until/if a suitable match on the type is found, such as
typedef unsigned long size_t;
...
%apply(size_t) {my_size}; ==> %apply(unsigned long) {my_size};
*/
if (!sm) {
SwigType *ntype = SwigType_typedef_resolve(type);
if (ntype && (Cmp(ntype, type) != 0)) {
SwigType *ntype = Copy(type);
while (!sm && ntype) {
SwigType *nt = ntype;
ntype = SwigType_typedef_resolve(ntype);
if (ntype) {
sm = typemap_get(ntype, name);
}
Delete(ntype);
Delete(nt);
}
Delete(ntype);
if (sm) {
/* Got a typemap. Need to only merge attributes for methods that match our signature */