[C++-sig] Boost.Python v2: float <==> int, anything ==> str

David Abrahams dave at boost-consulting.com
Mon Sep 2 04:26:07 CEST 2002

Currently, Boost.Python v2 registers from_python converters for the
following categories of builtin C++ target types:

  integer (e.g. signed char,      use the source type's
  unsigned short, int, long...)   __int__() method, if any

  floating (e.g. float,           use the source type's
  double, long double)            __float__ method, if any

  long (e.g. long long,           use the source type's
  unsigned long long)             __long__ method, if any

  boolean (bool)                  checks PyObject_IsTrue

  string (e.g. const char*,       use the source type's
  std::string, char...)           __str__() method, if any

[The rules are actually slightly more sophisticated: see
libs/python/src/converter/builtin_converters.cpp for details]

These facts lead to some unpleasant truths. For example, if you overload
these two functions, only one of them can be called using the built-in
python types as arguments:

  void f(int);        // case 1
  void f(double);

The reason is that Python int has a __float__ method, and Python float has
an __int__ method. Whichever one you def() first will be called, whether
you pass a Python int or a float. Perhaps just as bad is the fact that you
can pass almost anything to this function:

  void g(char const*);   // case 2

Of course the reason is that almost everything has a __str__ method.

After some generous prompting from David Beazly (the man behind SWIG) about
overload resolution, I think I'm ready to change these rules. Our
conversation convinced me that although a more-sophisticated conversion
mechanism which uses the closest matching signature is probably "more
correct", simply making the conversion rules a bit less liberal will cover
99% of all cases.

There are two different problems illustrated above:

- case 1: a cycle in the convertibility graph between floats and ints. It
seems as though many systems (including SWIG and Jython) solve this problem
by breaking the cycle so that an int can be converted to a float, but not
vice-versa. I think the intention is that conversions should not be lossy,
though of course there's no guarantee that you can convert from int to
float without at least losing precision.

- case 2: just plain "too liberal". It would be better to get an error than
silent misbehavior if you pass an integer where a string is expected.

The current scheme gets us some nice generality: any numeric type is likely
to have a __float__ method, so a user who implements a rational number
class in Python doesn't have to do anything special to allow it to be
passed where a C++ double is expected. However, IMO, the cost for this
generality is simply too high.

So, I propose to change the built-in conversion rules for the C++ builtin
types as follows (note that users can explicitly add new conversions to any
of these target types):

  integer/long  require Python int or Python long (or subtype)
  floating      use the source type's __float__ method, if any
  boolean       require Python int (or subtype)
  string        require Python str (or subtype)

It looks like we can actually get away with keeping the same rule for
floating types, though please speak up if you see some actual danger here.
I'm also interested in opinions on whether Python unicode should be
convertible to C++ string types.



           David Abrahams * Boost Consulting
dave at boost-consulting.com * http://www.boost-consulting.com

More information about the Cplusplus-sig mailing list