Change in default behaviour wrapping C++ bool for Python.

Only a Python True or False will now work for C++ bool parameters.
This fixes overloading bool with other types.
This commit is contained in:
William S Fulton 2014-03-08 12:04:19 +00:00
commit 504c2030bb
11 changed files with 251 additions and 11 deletions

View file

@ -5,6 +5,62 @@ See the RELEASENOTES file for a summary of changes in each release.
Version 3.0.0 (in progress) Version 3.0.0 (in progress)
============================ ============================
2014-03-06: wsfulton
[Python] Change in default behaviour wrapping C++ bool. Only a Python True or False
will now work for C++ bool parameters. This fixes overloading bool with other types.
Python 2.3 minimum is now required for wrapping bool.
When wrapping:
const char* overloaded(bool value) { return "bool"; }
const char* overloaded(int value) { return "int"; }
Previous behaviour:
>>> overloaded(False)
'int'
>>> overloaded(True)
'int'
>>> overloaded(0)
'int'
Now we get the expected behaviour:
>>> overloaded(False)
'bool'
>>> overloaded(0)
'int'
The consequence is when wrapping bool in non-overloaded functions:
const char* boolfunction(bool value) { return value ? "true" : "false"; }
The previous behaviour was very Pythonic:
>>> boolfunction("")
'false'
>>> boolfunction("hi")
'true'
>>> boolfunction(12.34)
'true'
>>> boolfunction(0)
'false'
>>> boolfunction(1)
'true'
Now the new behaviour more along the lines of C++ due to stricter type checking. The
above calls result in an exception and need to be explicitly converted into a bool as
follows:
>>> boolfunction(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: in method 'boolfunction', argument 1 of type 'bool'
>>> boolfunction(bool(0))
'false'
The old behaviour can be resurrected by passing the -DSWIG_PYTHON_LEGACY_BOOL command line
parameter when executing SWIG. Typemaps can of course be written to customise the behaviour
for specific parameters.
*** POTENTIAL INCOMPATIBILITY ***
2014-03-06: wsfulton 2014-03-06: wsfulton
Fix SF Bug #1363 - Problem with method overloading when some methods are added by %extend Fix SF Bug #1363 - Problem with method overloading when some methods are added by %extend
and others are real methods and using template default parameters with smart pointers. and others are real methods and using template default parameters with smart pointers.

View file

@ -294,6 +294,7 @@ CPP_TEST_CASES += \
operator_pointer_ref \ operator_pointer_ref \
operbool \ operbool \
ordering \ ordering \
overload_bool \
overload_copy \ overload_copy \
overload_extend \ overload_extend \
overload_method \ overload_method \

View file

@ -0,0 +1,19 @@
%module overload_bool
%inline %{
const char* overloaded(bool value) { return "bool"; }
const char* overloaded(int value) { return "int"; }
const char* overloaded(const char *value) { return "string"; }
const char* boolfunction(bool value) { return value ? "true" : "false"; }
const char* intfunction(int value) { return "int"; }
// Const references
const char* overloaded_ref(bool const& value) { return "bool"; }
const char* overloaded_ref(int const& value) { return "int"; }
const char* overloaded_ref(const char *value) { return "string"; }
const char* boolfunction_ref(bool const& value) { return value ? "true" : "false"; }
const char* intfunction_ref(int const& value) { return "int"; }
%}

View file

@ -17,10 +17,10 @@ halve_in_place(dv)
bv = BoolVector(4) bv = BoolVector(4)
bv[0]= 1 bv[0]= bool(1)
bv[1]= 0 bv[1]= bool(0)
bv[2]= 4 bv[2]= bool(4)
bv[3]= 0 bv[3]= bool(0)
if bv[0] != bv[2]: if bv[0] != bv[2]:
raise RuntimeError,"bad std::vector<bool> mapping" raise RuntimeError,"bad std::vector<bool> mapping"

View file

@ -0,0 +1,55 @@
import overload_bool
# Overloading bool, int, string
if overload_bool.overloaded(True) != "bool":
raise RuntimeError("wrong!")
if overload_bool.overloaded(False) != "bool":
raise RuntimeError("wrong!")
if overload_bool.overloaded(0) != "int":
raise RuntimeError("wrong!")
if overload_bool.overloaded(1) != "int":
raise RuntimeError("wrong!")
if overload_bool.overloaded(2) != "int":
raise RuntimeError("wrong!")
if overload_bool.overloaded("1234") != "string":
raise RuntimeError("wrong!")
# Test bool masquerading as int
if overload_bool.intfunction(True) != "int":
raise RuntimeError("wrong!")
if overload_bool.intfunction(False) != "int":
raise RuntimeError("wrong!")
# Test int masquerading as bool
# Not possible
#############################################
# Overloading bool, int, string
if overload_bool.overloaded_ref(True) != "bool":
raise RuntimeError("wrong!")
if overload_bool.overloaded_ref(False) != "bool":
raise RuntimeError("wrong!")
if overload_bool.overloaded_ref(0) != "int":
raise RuntimeError("wrong!")
if overload_bool.overloaded_ref(1) != "int":
raise RuntimeError("wrong!")
if overload_bool.overloaded_ref(2) != "int":
raise RuntimeError("wrong!")
if overload_bool.overloaded_ref("1234") != "string":
raise RuntimeError("wrong!")
# Test bool masquerading as int
if overload_bool.intfunction_ref(True) != "int":
raise RuntimeError("wrong!")
if overload_bool.intfunction_ref(False) != "int":
raise RuntimeError("wrong!")
# Test int masquerading as bool
# Not possible

View file

@ -30,7 +30,7 @@ if ref_float(3.5) != 3.5:
if ref_double(3.5) != 3.5: if ref_double(3.5) != 3.5:
raise RuntimeError raise RuntimeError
if ref_bool(1) != 1: if ref_bool(True) != True:
raise RuntimeError raise RuntimeError
if ref_char('x') != 'x': if ref_char('x') != 'x':

View file

@ -5,12 +5,12 @@ if getconstTC().num != 33:
raise RuntimeError raise RuntimeError
# primitive reference variables # primitive reference variables
cvar.var_bool = createref_bool(0) cvar.var_bool = createref_bool(False)
if value_bool(cvar.var_bool) != 0: if value_bool(cvar.var_bool) != False:
raise RuntimeError raise RuntimeError
cvar.var_bool = createref_bool(1) cvar.var_bool = createref_bool(True)
if value_bool(cvar.var_bool) != 1: if value_bool(cvar.var_bool) != True:
raise RuntimeError raise RuntimeError
cvar.var_char = createref_char('w') cvar.var_char = createref_char('w')

View file

@ -46,7 +46,7 @@ for i in range(0,len(m)):
if m[i][j] != im[i][j]: if m[i][j] != im[i][j]:
raise RuntimeError, "bad getslice" raise RuntimeError, "bad getslice"
m = ((1,0,1),(1,1),(1,1)) m = ((True,False,True),(True,True),(True,True))
im = std_containers.midentb(m) im = std_containers.midentb(m)
for i in range(0,len(m)): for i in range(0,len(m)):
for j in range(0,len(m[i])): for j in range(0,len(m[i])):

View file

@ -0,0 +1,88 @@
#!/usr/bin/env ruby
#
# Put description here
#
#
#
#
#
require 'swig_assert'
require 'overload_bool'
include Overload_bool
# Overloading bool, int, string
if overloaded(true) != "bool"
raise RuntimeError, "wrong!"
end
if overloaded(false) != "bool"
raise RuntimeError, "wrong!"
end
if overloaded(0) != "int"
raise RuntimeError, "wrong!"
end
if overloaded(1) != "int"
raise RuntimeError, "wrong!"
end
if overloaded(2) != "int"
raise RuntimeError, "wrong!"
end
if overloaded("1234") != "string"
raise RuntimeError, "wrong!"
end
# Test bool masquerading as integer
# Not possible
# Test int masquerading as bool
if boolfunction(0) != "false"
raise RuntimeError, "wrong!"
end
if boolfunction(1) != "true"
raise RuntimeError, "wrong!"
end
if boolfunction(2) != "true"
raise RuntimeError, "wrong!"
end
#############################################
# Overloading bool, int, string
if overloaded_ref(true) != "bool"
raise RuntimeError, "wrong!"
end
if overloaded_ref(false) != "bool"
raise RuntimeError, "wrong!"
end
if overloaded_ref(0) != "int"
raise RuntimeError, "wrong!"
end
if overloaded_ref(1) != "int"
raise RuntimeError, "wrong!"
end
if overloaded_ref(2) != "int"
raise RuntimeError, "wrong!"
end
if overloaded_ref("1234") != "string"
raise RuntimeError, "wrong!"
end
# Test bool masquerading as integer
# Not possible
# Test int masquerading as bool
if boolfunction_ref(0) != "false"
raise RuntimeError, "wrong!"
end
if boolfunction_ref(1) != "true"
raise RuntimeError, "wrong!"
end
if boolfunction_ref(2) != "true"
raise RuntimeError, "wrong!"
end

View file

@ -12,6 +12,8 @@ SWIGINTERNINLINE PyObject*
} }
} }
#ifdef SWIG_PYTHON_LEGACY_BOOL
// Default prior to SWIG 3.0.0
%fragment(SWIG_AsVal_frag(bool),"header", %fragment(SWIG_AsVal_frag(bool),"header",
fragment=SWIG_AsVal_frag(long)) { fragment=SWIG_AsVal_frag(long)) {
SWIGINTERN int SWIGINTERN int
@ -24,6 +26,23 @@ SWIG_AsVal_dec(bool)(PyObject *obj, bool *val)
return SWIG_OK; return SWIG_OK;
} }
} }
#else
%fragment(SWIG_AsVal_frag(bool),"header",
fragment=SWIG_AsVal_frag(long)) {
SWIGINTERN int
SWIG_AsVal_dec(bool)(PyObject *obj, bool *val)
{
int r;
if (!PyBool_Check(obj))
return SWIG_ERROR;
r = PyObject_IsTrue(obj);
if (r == -1)
return SWIG_ERROR;
if (val) *val = r ? true : false;
return SWIG_OK;
}
}
#endif
/* int */ /* int */

View file

@ -5,9 +5,11 @@
/* ------------------------------------------------------------ /* ------------------------------------------------------------
* Fragment section * Fragment section
* ------------------------------------------------------------ */ * ------------------------------------------------------------ */
/* bool is dangerous in Python, change precedence */ #ifdef SWIG_PYTHON_LEGACY_BOOL
// Default prior to SWIG 3.0.0
#undef SWIG_TYPECHECK_BOOL #undef SWIG_TYPECHECK_BOOL
%define SWIG_TYPECHECK_BOOL 10000 %enddef %define SWIG_TYPECHECK_BOOL 10000 %enddef
#endif
/* Include fundamental fragment definitions */ /* Include fundamental fragment definitions */
%include <typemaps/fragments.swg> %include <typemaps/fragments.swg>