Typemap matching rules enhancement for non-default typemaps. Previously all qualifiers were stripped in one step, now they are stripped one at a time starting with the left most qualifier.

git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk@12007 626c5289-ae23-0410-ae9c-e8d60b6d4f22
This commit is contained in:
William S Fulton 2010-05-02 21:35:02 +00:00
commit efd200ffe2
10 changed files with 398 additions and 38 deletions

View file

@ -1,6 +1,14 @@
Version 2.0.0 (in progress)
============================
2010-05-01: wsfulton
Typemap matching enhancement for non-default typemaps. Previously all
qualifiers were stripped in one step, now they are stripped one at a time
starting with the left most qualifier. For example, int const*const
is first stripped to int *const then int *.
*** POTENTIAL INCOMPATIBILITY ***
2010-04-25: bhy
[Python] Fix #2985655 - broken constructor renaming.
@ -37,7 +45,7 @@ Version 2.0.0 (in progress)
Numerous subtle typemap matching rule fixes when using the default type. The typemap
matching rules are to take a type and find the best default typemap (SWIGTYPE, SWIGTYPE* etc),
then look for the next best match by reducing the chosen default type. The type deduction
now follows C++ template partial specialization matching rules.
now follows C++ class template partial specialization matching rules.
Below are the set of changes made showing the default type deduction
along with the old reduced type and the new version of the reduced type:

View file

@ -351,6 +351,7 @@
<li><a href="Typemaps.html#Typemaps_nn17">Basic matching rules</a>
<li><a href="Typemaps.html#Typemaps_typedef_reductions">Typedef reductions matching</a>
<li><a href="Typemaps.html#Typemaps_nn19">Default typemap matching rules</a>
<li><a href="Typemaps.html#Typemaps_matching_template_comparison">Matching comparison with C++ templates</a>
<li><a href="Typemaps.html#Typemaps_multi_argument_typemaps_patterns">Multi-arguments typemaps</a>
<li><a href="Typemaps.html#Typemaps_debugging_search">Debugging typemap pattern matching</a>
</ul>

View file

@ -33,6 +33,7 @@
<li><a href="#Typemaps_nn17">Basic matching rules</a>
<li><a href="#Typemaps_typedef_reductions">Typedef reductions matching</a>
<li><a href="#Typemaps_nn19">Default typemap matching rules</a>
<li><a href="#Typemaps_matching_template_comparison">Matching comparison with C++ templates</a>
<li><a href="#Typemaps_multi_argument_typemaps_patterns">Multi-arguments typemaps</a>
<li><a href="#Typemaps_debugging_search">Debugging typemap pattern matching</a>
</ul>
@ -1026,8 +1027,10 @@ is used.
</ul>
<p>
If <tt>TYPE</tt> includes qualifiers (const, volatile, etc.), they are stripped to form a new stripped type
If <tt>TYPE</tt> includes qualifiers (const, volatile, etc.), each qualifier is stripped one at a time to form a new stripped type
and the matching rules above are repeated on the stripped type.
The left-most qualifier is stripped first, resulting in the right-most (or top-level) qualifier being stripped last.
For example <tt>int const*const</tt> is first stripped to <tt>int *const</tt> then <tt>int *</tt>.
</p>
<p>
@ -1056,8 +1059,8 @@ To find a typemap for the argument <tt>const char *s</tt>, SWIG will search for
<pre>
const char *s Exact type and name match
const char * Exact type match
char *s Type and name match (stripped qualifiers)
char * Type match (stripped qualifiers)
char *s Type and name match (qualifier stripped)
char * Type match (qualifier stripped)
</pre>
</div>
@ -1097,6 +1100,11 @@ void F(int x[1000]); // int [ANY] rule (typemap 5)
</pre>
</div>
<p>
<b>Compatibility note: </b> SWIG-2.0.0 introduced stripping the qualifiers one step at a time. Prior versions
stripped all qualifiers in one step.
</p>
<H3><a name="Typemaps_typedef_reductions"></a>10.3.2 Typedef reductions matching</H3>
@ -1280,7 +1288,7 @@ If the basic pattern matching rules result in no match being made, even after ty
the default typemap matching rules are used to look for a suitable typemap match.
These rules match a generic typemap based on the reserved <tt>SWIGTYPE</tt> base type.
For example pointers will use <tt>SWIGTYPE *</tt> and references will use <tt>SWIGTYPE &amp;</tt>.
More precisely, the rules are based on the C++ template partial specialization matching rules used
More precisely, the rules are based on the C++ class template partial specialization matching rules used
by C++ compilers when looking for an appropriate partial template specialization.
This means that a match is chosen from the most specialized set of generic typemap types available. For example,
when looking for a match to <tt>int const *</tt>, the rules will prefer to match <tt>SWIGTYPE const *</tt>
@ -1407,11 +1415,10 @@ Finally the best way to view the typemap matching rules in action is via the <a
<p>
<b>Compatibility note: </b> The default typemap matching rules were modified in SWIG-2.0.0 from a slightly
simpler scheme to match the current C++ template partial specialization matching rules.
simpler scheme to match the current C++ class template partial specialization matching rules.
</p>
<H3><a name="Typemaps_multi_argument_typemaps_patterns"></a>10.3.4 Multi-arguments typemaps</H3>
<H3><a name="Typemaps_multi_argument_typemaps_patterns"></a>10.3.5 Multi-arguments typemaps</H3>
<p>
@ -1441,7 +1448,165 @@ but all subsequent arguments must match exactly.
</p>
<H3><a name="Typemaps_debugging_search"></a>10.3.5 Debugging typemap pattern matching</H3>
<H3><a name="Typemaps_matching_template_comparison"></a>10.3.4 Matching rules compared to C++ templates</H3>
<p>
For those intimately familiar with C++ templates, a comparison of the typemap matching rules and template type deduction is interesting.
The two areas considered are firstly the default typemaps and their similarities to partial template specialization and secondly, non-default typemaps and their similarities to full template specialization.
</p>
<p>
For default (SWIGTYPE) typemaps the rules are inspired by C++ class template
partial specialization. For example, given partial specialization for <tt>T const&amp;</tt> :
</p>
<div class="code">
<pre>
template &lt;typename T&gt; struct X { void a(); };
template &lt;typename T&gt; struct X&lt; T const&amp; &gt; { void b(); };
</pre>
</div>
<p>
The full (unspecialized) template is matched with most types, such as:
</p>
<div class="code">
<pre>
X&lt; int &amp; &gt; x1; x1.a();
</pre>
</div>
<p>
and the following all match the <tt>T const&amp;</tt> partial specialization:
</p>
<div class="code">
<pre>
X&lt; int *const&amp; &gt; x2; x2.b();
X&lt; int const*const&amp; &gt; x3; x3.b();
X&lt; int const&amp; &gt; x4; x4.b();
</pre>
</div>
<p>
Now, given just these two default typemaps, where T is analogous to SWIGTYPE:
</p>
<div class="code">
<pre>
%typemap(...) SWIGTYPE { ... }
%typemap(...) SWIGTYPE const&amp; { ... }
</pre>
</div>
<p>
The generic default typemap <tt>SWIGTYPE</tt> is used with most types, such as
</p>
<div class="code">
<pre>
int &amp;
</pre>
</div>
<p>
and the following all match the <tt>SWIGTYPE const&amp;</tt> typemap, just like the partial template matching:
</p>
<div class="code">
<pre>
int *const&amp;
int const*const&amp;
int const&amp;
</pre>
</div>
<p>
Note that the template and typemap matching rules are not identical for all default typemaps though, for example, with arrays.
</p>
<p>
For non-default typemaps, one might expect SWIG to follow the fully specialized template rules.
This is nearly the case, but not quite.
Consider a very similar example to the earlier partially specialized template but this time there is a fully specialized template:
</p>
<div class="code">
<pre>
template &lt;typename T&gt; struct Y { void a(); };
template &lt;&gt; struct Y&lt; int const &amp; &gt; { void b(); };
</pre>
</div>
<p>
Only the one type matches the specialized template exactly:
</p>
<div class="code">
<pre>
Y&lt; int &amp; &gt; y1; y1.a();
Y&lt; int *const&amp; &gt; y2; y2.a();
Y&lt; int const *const&amp; &gt; y3; y3.a();
Y&lt; int const&amp; &gt; y4; y4.b(); // fully specialized match
</pre>
</div>
<p>
Given typemaps with the same types used for the template declared above, where T is again analogous to SWIGTYPE:
</p>
<div class="code">
<pre>
%typemap(...) SWIGTYPE { ... }
%typemap(...) int const&amp; { ... }
</pre>
</div>
<p>
The comparison between non-default typemaps and fully specialized single parameter templates turns out to be the same, as just the one type will match the non-default typemap:
</p>
<div class="code">
<pre>
int &amp;
int *const&amp;
int const*const&amp;
int const&amp; // matches non-default typemap int const&amp;
</pre>
</div>
<p>
However, if a non-const type is used instead:
</p>
<div class="code">
<pre>
%typemap(...) SWIGTYPE { ... }
%typemap(...) int &amp; { ... }
</pre>
</div>
<p>
then there is a clear difference to template matching as both the const and non-const types match the typemap:
</p>
<div class="code">
<pre>
int &amp; // matches non-default typemap int &amp;
int *const&amp;
int const*const&amp;
int const&amp; // matches non-default typemap int &amp;
</pre>
</div>
<p>
There are other subtle differences such as typedef handling, but at least it should be clear that the typemap matching rules
are similar to those for specialized template handling.
</p>
<H3><a name="Typemaps_debugging_search"></a>10.3.6 Debugging typemap pattern matching</H3>
<p>

View file

@ -393,6 +393,7 @@ CPP_TEST_CASES += \
typemap_numinputs \
typemap_template \
typemap_out_optimal \
typemap_qualifier_strip \
typemap_variables \
typemap_various \
typename \

View file

@ -0,0 +1,54 @@
import typemap_qualifier_strip
val = typemap_qualifier_strip.create_int(111)
if typemap_qualifier_strip.testA1(val) != 1234:
raise RuntimeError
if typemap_qualifier_strip.testA2(val) != 1234:
raise RuntimeError
if typemap_qualifier_strip.testA3(val) != 1234:
raise RuntimeError
if typemap_qualifier_strip.testA4(val) != 1234:
raise RuntimeError
if typemap_qualifier_strip.testB1(val) != 111:
raise RuntimeError
if typemap_qualifier_strip.testB2(val) != 111:
raise RuntimeError
if typemap_qualifier_strip.testB3(val) != 111:
raise RuntimeError
if typemap_qualifier_strip.testB4(val) != 111:
raise RuntimeError
if typemap_qualifier_strip.testC1(val) != 5678:
raise RuntimeError
if typemap_qualifier_strip.testC2(val) != 111:
raise RuntimeError
if typemap_qualifier_strip.testC3(val) != 5678:
raise RuntimeError
if typemap_qualifier_strip.testC4(val) != 111:
raise RuntimeError
if typemap_qualifier_strip.testD1(val) != 111:
raise RuntimeError
if typemap_qualifier_strip.testD2(val) != 3456:
raise RuntimeError
if typemap_qualifier_strip.testD3(val) != 111:
raise RuntimeError
if typemap_qualifier_strip.testD4(val) != 111:
raise RuntimeError

View file

@ -0,0 +1,76 @@
%module typemap_qualifier_strip
%typemap(in) int *ptr {
int temp = 1234;
$1 = &temp;
}
%typemap(in) int *const ptrConst {
int temp = 5678;
$1 = &temp;
}
%typemap(in) int const* constPtr {
int temp = 3456;
$1 = &temp;
}
%inline %{
int *create_int(int newval) {
static int val = 0;
val = newval;
return &val;
}
int testA1(int const*const ptr) {
return *ptr;
}
int testA2(int const* ptr) {
return *ptr;
}
int testA3(int *const ptr) {
return *ptr;
}
int testA4(int * ptr) {
return *ptr;
}
int testB1(int const*const p) {
return *p;
}
int testB2(int const* p) {
return *p;
}
int testB3(int *const p) {
return *p;
}
int testB4(int * p) {
return *p;
}
int testC1(int const*const ptrConst) {
return *ptrConst;
}
int testC2(int const* ptrConst) {
return *ptrConst;
}
int testC3(int *const ptrConst) {
return *ptrConst;
}
int testC4(int * ptrConst) {
return *ptrConst;
}
int testD1(int const*const constPtr) {
return *constPtr;
}
int testD2(int const* constPtr) {
return *constPtr;
}
int testD3(int *const constPtr) {
return *constPtr;
}
int testD4(int * constPtr) {
return *constPtr;
}
%}

View file

@ -357,8 +357,8 @@ SwigType *SwigType_default_create(SwigType *ty) {
* SwigType_default_deduce()
*
* This function implements type deduction used in the typemap matching rules
* and is very close to the type deduction used in partial template specialization
* matching in that the most specialized type is always chosen.
* and is very close to the type deduction used in partial template class
* specialization matching in that the most specialized type is always chosen.
* SWIGTYPE is used as the generic type. The basic idea is to repeatedly call
* this function to find a deduced type unless until nothing matches.
*

View file

@ -159,6 +159,7 @@ extern "C" {
extern int SwigType_isenum(SwigType *t);
extern int SwigType_check_decl(SwigType *t, const_String_or_char_ptr decl);
extern SwigType *SwigType_strip_qualifiers(SwigType *t);
extern SwigType *SwigType_strip_single_qualifier(SwigType *t);
extern SwigType *SwigType_functionpointer_decompose(SwigType *t);
extern String *SwigType_base(const SwigType *t);
extern String *SwigType_namestr(const SwigType *t);

View file

@ -698,11 +698,11 @@ static Hash *typemap_search(const_String_or_char_ptr tmap_method, SwigType *type
Hash *backup = 0;
SwigType *primitive = 0;
SwigType *ctype = 0;
SwigType *ctype_unstripped = 0;
int ts;
int isarray;
const String *cname = 0;
const String *cqualifiedname = 0;
SwigType *unstripped = 0;
String *tm_method = typemap_method_name(tmap_method);
int debug_display = (in_typemap_search_multi == 0) && typemap_search_debug;
@ -718,7 +718,8 @@ static Hash *typemap_search(const_String_or_char_ptr tmap_method, SwigType *type
Delete(typestr);
}
while (ts >= 0) {
ctype = type;
ctype = Copy(type);
ctype_unstripped = Copy(ctype);
while (ctype) {
/* Try to get an exact type-match */
tm = get_typemap(ts, ctype);
@ -751,29 +752,25 @@ static Hash *typemap_search(const_String_or_char_ptr tmap_method, SwigType *type
goto ret_result;
}
/* No match so far. If the type is unstripped, we'll strip its
qualifiers and check. Otherwise, we'll try to resolve a typedef */
if (!unstripped) {
unstripped = ctype;
ctype = SwigType_strip_qualifiers(ctype);
if (!Equal(ctype, unstripped))
continue; /* Types are different */
Delete(ctype);
ctype = unstripped;
unstripped = 0;
}
/* No match so far - try with a qualifier stripped (strip one qualifier at a time until none remain)
* The order of stripping in SwigType_strip_single_qualifier is used to provide some sort of consistency
* with the default (SWIGTYPE) typemap matching rules for the first qualifier to be stripped. */
{
String *octype;
if (unstripped) {
Delete(ctype);
ctype = unstripped;
unstripped = 0;
SwigType *oldctype = ctype;
ctype = SwigType_strip_single_qualifier(oldctype);
if (!Equal(ctype, oldctype)) {
Delete(oldctype);
continue;
}
octype = ctype;
ctype = SwigType_typedef_resolve(ctype);
if (octype != type)
Delete(octype);
Delete(oldctype);
}
/* Once all qualifiers are stripped try resolve a typedef */
{
SwigType *oldctype = ctype;
ctype = SwigType_typedef_resolve(ctype_unstripped);
Delete(oldctype);
ctype_unstripped = Copy(ctype);
}
}
@ -802,12 +799,10 @@ static Hash *typemap_search(const_String_or_char_ptr tmap_method, SwigType *type
ret_result:
Delete(primitive);
if ((unstripped) && (unstripped != type))
Delete(unstripped);
if (matchtype)
*matchtype = Copy(ctype);
if (type != ctype)
Delete(ctype);
Delete(ctype);
Delete(ctype_unstripped);
return result;
}

View file

@ -1125,3 +1125,62 @@ SwigType *SwigType_strip_qualifiers(SwigType *t) {
}
return r;
}
/* -----------------------------------------------------------------------------
* SwigType_strip_single_qualifier()
*
* If the type contains a qualifier, strip one qualifier and return a new type.
* The left most qualifier is stripped first (when viewed as C source code) but
* this is the equivalent to the right most qualifier using SwigType notation.
* Example:
* r.q(const).p.q(const).int => r.q(const).p.int
* r.q(const).p.int => r.p.int
* r.p.int => r.p.int
* ----------------------------------------------------------------------------- */
SwigType *SwigType_strip_single_qualifier(SwigType *t) {
static Hash *memoize_stripped = 0;
SwigType *r = 0;
List *l;
int numitems;
if (!memoize_stripped)
memoize_stripped = NewHash();
r = Getattr(memoize_stripped, t);
if (r)
return Copy(r);
l = SwigType_split(t);
numitems = Len(l);
if (numitems >= 2) {
int item;
/* iterate backwards from last but one item */
for (item = numitems - 2; item >= 0; --item) {
String *subtype = Getitem(l, item);
if (SwigType_isqualifier(subtype)) {
Iterator it;
Delitem(l, item);
r = NewStringEmpty();
for (it = First(l); it.item; it = Next(it)) {
Append(r, it.item);
}
break;
}
}
}
if (!r)
r = Copy(t);
Delete(l);
{
String *key, *value;
key = Copy(t);
value = Copy(r);
Setattr(memoize_stripped, key, value);
Delete(key);
Delete(value);
}
return r;
}