[C++-sig] strange behavior with respect to numeric and Booleanoverloads

troy d. straszheim troy at resophonic.com
Wed Mar 18 18:00:19 CET 2009


So, this isn't just a bool-int issue, you can get the same thing with 
int and double:

std::string takes_bool(bool b) { return "bool"; } 

std::string takes_int(int b) { return "int"; } 

std::string takes_double(double b) { return "double"; } 

 

BOOST_PYTHON_MODULE(overload_resolution) 

{ 

   def("bid", takes_bool); 

   def("bid", takes_int); 

   def("bid", takes_double);
 

   def("bdi", takes_bool); 

   def("bdi", takes_double); 

   def("bdi", takes_int); 

} 

 

In [11]: its_an_int = 3

In [12]: bid(its_an_int)
Out[12]: 'double'

In [13]: bdi(its_an_int)
Out[13]: 'int'

In [14]: bid(True)
Out[14]: 'double'

The current rule for overload resolution are simply 'first match in 
reverse order of registration'.  You could relatively easily make this 
'first match in forward order of registration'.   The library currently 
has no notion of one function being a better match than another, for a 
given set of arguments.  It looks like it would be interesting enough to 
implement, but it isn't clear what those rules would be or if the 
runtime cost would be worth it.  For instance, this situation:

   void fn_di(double, int);
   void fn_id(int, double);

   def("f", fn_di);
   def("f", fn_id);

   >>> f(3,3)

In C++, of course, that's a compile-time error.  In python, would you 
check all overloads for equally-good matches, and if so, throw some kind 
of ambiguous_function_call exception?

Maybe somebody else has more ideas on this.  I think you could tighten 
up the automatic conversion rules (like the patch below does for int and 
bool), so that one would have to clarify at the call site which function 
you're calling:

   >> f(float(3), 3)

this seems the most practical to me at the moment.  It would break a 
bunch of python code , but by adding casts, the old python code could be 
made to work with boost.python bindings pre- and post- this change.

-t

troy d. straszheim wrote:
> I have a quasi-fix for this to the library itself, (see diff below) 
> buuut it breaks a certain case:  if you have a wrapped c++ function that 
> takes a bool, and you try to pass it an int, you get a
> 
>     ArgumentError: Python argument types in
>         builtin_converters_ext.rewrap_const_reference_bool(int)
>     did not match C++ signature:
>         rewrap_const_reference_bool(bool)
> 
> so you have to call the function with an explicit cast to bool:
> 
> c++:
> 
>   void takes_a_bool(bool b) { ... };
> 
>   def("py_takesbool", takes_a_bool);
> 
> py:
> 
>   py_takesbool(bool(33))
> 
> to get things to work.   This breaks a lot of tests, I'm digging further.
> 
> 
> Index: converter/builtin_converters.cpp
> ===================================================================
> --- converter/builtin_converters.cpp    (revision 51812)
> +++ converter/builtin_converters.cpp    (working copy)
> @@ -99,8 +99,13 @@
>            if (number_methods == 0)
>                return 0;
> 
> -          return (PyInt_Check(obj) || PyLong_Check(obj))
> -              ? &number_methods->nb_int : 0;
> +          return (
> +#if PY_VERSION_HEX >= 0x02040000
> +          !PyBool_Check(obj) &&
> +#endif
> +          (PyInt_Check(obj) || PyLong_Check(obj)))
> +
> +        ? &number_methods->nb_int : 0;
>        }
>        static PyTypeObject const* get_pytype() { return &PyInt_Type;}
>    };
> @@ -135,7 +140,11 @@
>            if (number_methods == 0)
>                return 0;
> 
> -          return (PyInt_Check(obj) || PyLong_Check(obj))
> +          return (
> +#if PY_VERSION_HEX >= 0x02040000
> +          !PyBool_Check(obj) &&
> +#endif
> +          (PyInt_Check(obj) || PyLong_Check(obj)))
>                ? &py_object_identity : 0;
>        }
>        static PyTypeObject const* get_pytype() { return &PyInt_Type;}
> @@ -226,7 +235,11 @@
>    {
>        static unaryfunc* get_slot(PyObject* obj)
>        {
> +#if PY_VERSION_HEX >= 0x02040000
> +    return obj == Py_None || PyBool_Check(obj) ? &py_object_identity : 0;
> +#else
>            return obj == Py_None || PyInt_Check(obj) ? 
> &py_object_identity : 0;
> +#endif
>        }
> 
>        static bool extract(PyObject* intermediate)
> _______________________________________________
> Cplusplus-sig mailing list
> Cplusplus-sig at python.org
> http://mail.python.org/mailman/listinfo/cplusplus-sig



More information about the Cplusplus-sig mailing list