Add new feature "python:cdefaultargs"

Controls default argument code generation to obtain the default
arguments from the C++ layer instead of the Python layer.
This commit is contained in:
William S Fulton 2015-05-28 20:11:57 +01:00
commit b8e1a66a38
5 changed files with 153 additions and 3 deletions

View file

@ -5,7 +5,41 @@ See the RELEASENOTES file for a summary of changes in each release.
Version 3.0.6 (in progress)
===========================
2015-05-07: wsfulton
2015-05-28: wsfulton
[Python] Add new feature "python:cdefaultargs" to control default argument
code generation. By default, SWIG attempts to convert C/C++ default argument values
into Python values and generates code into the Python layer with these values.
Recent versions of SWIG are able to convert more of these values, however, the
new behaviour can be circumvented if desired via this new feature, such that
the default argument values are obtained from the C layer and not the Python layer.
For example:
struct CDA {
int fff(int a = 1, bool b = false);
};
The default code generation in the Python layer is:
class CDA(_object):
...
def fff(self, a=1, b=False):
return _default_args.CDA_fff(self, a, b)
Adding the feature:
%feature("python:cdefaultargs") CDA::fff;
Results in:
class CDA(_object):
...
def fff(self, *args):
return _default_args.CDA_fff(self, *args)
Some code generation modes, eg -builtin and -fastproxy, are unaffected by this as
the default values are always obtained from the C layer.
2015-05-27: wsfulton
[Python] Deal with an integer as the default value of a typedef to bool
parameter in the C++ prototype. See #327. Regression from 3.0.0 onwards.

View file

@ -4147,6 +4147,105 @@ If you need to return binary data, you might use the
also be used to extra binary data from arbitrary pointers.
</p>
<H3><a name="Python_default_args"></a>Default arguments</H3>
<p>
C++ default argument code generation is documented in the main
<a href="SWIG.html#SWIGPlus_default_args">Default arguments</a> section.
There is also an optional Python specific feature that can be used called the <tt>python:cdefaultargs</tt>
<a href="Customization.html#Customization_feature_flags">feature flag</a>.
By default, SWIG attempts to convert C++ default argument values
into Python values and generates code into the Python layer containing these values.
For example:
</p>
<div class="code">
<pre>
struct CDA {
int fff(int a = 1, bool b = false);
};
</pre>
</div>
<p>
From Python this can be called as follows:
</p>
<div class="targetlang">
<pre>
&gt;&gt;&gt; CDA().fff() # C++ layer receives a=1 and b=false
&gt;&gt;&gt; CDA().fff(2) # C++ layer receives a=2 and b=false
&gt;&gt;&gt; CDA().fff(3, True) # C++ layer receives a=3 and b=true
</pre>
</div>
<p>
The default code generation in the Python layer is:
</p>
<div class="targetlang">
<pre>
class CDA(object):
...
def fff(self, a=1, b=False):
return _default_args.CDA_fff(self, a, b)
</pre>
</div>
<p>
Adding the feature:
</p>
<div class="code">
<pre>
%feature("python:cdefaultargs") CDA::fff;
struct CDA {
int fff(int a = 1, bool b = false);
</pre>
</div>
<p>
results in identical behaviour when called from Python, however, it results in different code generation:
</p>
<div class="targetlang">
<pre>
class CDA(object):
...
def fff(self, *args):
return _default_args.CDA_fff(self, *args)
</pre>
</div>
<p>
The default arguments are obtained in the C++ wrapper layer instead of the Python layer.
Some code generation modes are quite different, eg <tt>-builtin</tt> and <tt>-fastproxy</tt>,
and are unaffected by <tt>python:cdefaultargs</tt> as the default values are always obtained from the C++ layer.
</p>
<p>
Note that not all default arguments can be converted into a Python equivalent.
When SWIG does not convert them, it will generate code to obtain them from the C++ layer as if
<tt>python:cdefaultargs</tt> was specified.
This will happen if just one argument cannot be converted into a Python equivalent.
This occurs typically when the argument is not fully numeric, such as <tt>int(1)</tt>:
</p>
<div class="code">
<pre>
struct CDA {
int fff(int a = int(1), bool b = false);
};
</pre>
</div>
<p>
<b>Compatibility Note:</b> SWIG-3.0.6 introduced the <tt>python:cdefaultargs</tt> feature.
Versions of SWIG prior to this varied in their ability to convert C++ default values into
equivalent Python default argument values.
</p>
<H2><a name="Python_nn53"></a>36.8 Typemaps</H2>

View file

@ -292,3 +292,12 @@ struct ConstMethods {
inline int slightly_off_square(int square_error, int def17) { return def17*def17 + square_error; }
%}
// Python C default args
%feature("python:cdefaultargs") CDA::cdefaultargs_test1;
%inline %{
struct CDA {
int cdefaultargs_test1(int a = 1) { return a; }
int cdefaultargs_test2(int a = 1) { return a; }
};
%}

View file

@ -128,6 +128,13 @@ def run(module_name):
if default_args.slightly_off_square() != 291:
raise RuntimeError
# It is difficult to test the python:cdefaultargs feature properly as -builtin
# and -fastproxy do not use the Python layer for default args
if default_args.CDA().cdefaultargs_test1() != 1:
raise RuntimeError
if default_args.CDA().cdefaultargs_test2() != 1:
raise RuntimeError
if __name__ == "__main__":
run('default_args')

View file

@ -2082,9 +2082,10 @@ public:
1. The function is overloaded as Python doesn't support this.
2. We were explicitly asked to use the "compact" arguments form.
3. One of the default argument values can't be represented in Python.
3. We were explicitly asked to use default args from C via the "python:cdefaultargs" feature.
4. One of the default argument values can't be represented in Python.
*/
if (is_real_overloaded(n) || GetFlag(n, "feature:compactdefaultargs") || !is_representable_as_pyargs(n)) {
if (is_real_overloaded(n) || GetFlag(n, "feature:compactdefaultargs") || GetFlag(n, "feature:python:cdefaultargs") || !is_representable_as_pyargs(n)) {
String *parms = NewString("");
if (in_class)
Printf(parms, "self, ");