diff --git a/CHANGES.current b/CHANGES.current index e722d1346..a9847ac6f 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -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. diff --git a/Doc/Manual/Python.html b/Doc/Manual/Python.html index 1886fcd45..23c2aaae3 100644 --- a/Doc/Manual/Python.html +++ b/Doc/Manual/Python.html @@ -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.

+ +

Default arguments

+ +

+C++ default argument code generation is documented in the main +Default arguments section. +There is also an optional Python specific feature that can be used called the python:cdefaultargs +feature flag. +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: +

+ +
+
+struct CDA {
+  int fff(int a = 1, bool b = false);
+};
+
+
+ +

+From Python this can be called as follows: +

+ +
+
+>>> CDA().fff()        # C++ layer receives a=1 and b=false
+>>> CDA().fff(2)       # C++ layer receives a=2 and b=false
+>>> CDA().fff(3, True) # C++ layer receives a=3 and b=true
+
+
+ +

+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;
+struct CDA {
+  int fff(int a = 1, bool b = false);
+
+
+ +

+results in identical behaviour when called from Python, however, it results in different code generation: +

+ +
+
+class CDA(object):
+    ...
+    def fff(self, *args):
+        return _default_args.CDA_fff(self, *args)
+
+
+ +

+The default arguments are obtained in the C++ wrapper layer instead of the Python layer. +Some code generation modes are quite different, eg -builtin and -fastproxy, +and are unaffected by python:cdefaultargs as the default values are always obtained from the C++ layer. +

+ +

+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 +python:cdefaultargs 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 int(1): +

+ +
+
+struct CDA {
+  int fff(int a = int(1), bool b = false);
+};
+
+
+ +

+Compatibility Note: SWIG-3.0.6 introduced the python:cdefaultargs feature. +Versions of SWIG prior to this varied in their ability to convert C++ default values into +equivalent Python default argument values. +

+

36.8 Typemaps

diff --git a/Examples/test-suite/default_args.i b/Examples/test-suite/default_args.i index 719681f95..d3014d386 100644 --- a/Examples/test-suite/default_args.i +++ b/Examples/test-suite/default_args.i @@ -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; } +}; +%} + diff --git a/Examples/test-suite/python/default_args_runme.py b/Examples/test-suite/python/default_args_runme.py index 11878f7a4..6610a4ec4 100644 --- a/Examples/test-suite/python/default_args_runme.py +++ b/Examples/test-suite/python/default_args_runme.py @@ -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') diff --git a/Source/Modules/python.cxx b/Source/Modules/python.cxx index d5b920bf8..1d4d5b238 100644 --- a/Source/Modules/python.cxx +++ b/Source/Modules/python.cxx @@ -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, ");