[C++-sig] V2: wrapping int/double bug(?)

David Abrahams david.abrahams at rcn.com
Tue May 14 14:26:56 CEST 2002


----- Original Message -----
From: "Pearu Peterson" <pearu at cens.ioc.ee>

> Yes, there is. As I mentioned in my previous message, the library that I
> am wrapping is a symbolic algebra library where floats are inexact and
> ints are exact numbers, roughly speaking. Trivial example: if I'd used
> only double argument constructor:
>
<snip>

Thanks for the nice explanation. It always helps to understand the domain
my users are operating in.

> > I ask because in general it would seem
> > like a bad idea to implement different semantics for f(4) and f(4.0),
and
> > doubles tend to be able to represent all the values of an integer.
>
> It depends on the application, I guess. In numerical applications
> nobody represents integers by doubles for various reasons (efficiency,
> accuracy, etc).

I understand why you need this distinction in your application, but for
most I think efficiency of int->float conversion is a misplaced concern at
the Python/C++ boundary> I guess that on 64-bit machines an int doesn't fit
in the mantissa of a double.

> I understand this can be feasible for a number of
> applications that hardly deal with numerics or if they do then only with
> floating point numerics. And therefore they don't care if int becomes
> float.

Yeah, it's a pretty common model.

> > > but I don't see how your suggestion would solve
> > > the problem in hand. That is, if a wrapped class A defines methods
> > >
> > >  .foo(int)
> > >  .foo(float)
> > >
> > > and a class B has both __int__ and __float__ methods, then in Python
> > >
> > >  A().foo(B())
> > >
> > > how can you tell which method, __int__ or __float__, should be called
> > when
> > > doing conversion? E.g. take B=int and then take B=float.
> >
> > I assume you mean the cases where B() returns int or float.
>
> No. I mean that B() is an instance of the class B that represents either
> int or float

Oh, OK. But that case is easy.

> , or can be transformed to one.

As long as it can't be transformed into both, also easy. Even then, it's
easy because you just signal an ambiguity.

> Take, for example, B is
> Rational, or an arbitrary precision number.
>
> > That's easy: Python int would correspond most-closely to C++ int, and
> > Python float would correspond most-closely to C++ double.
>
> I agree. But BPL does not seem to agree with that ;-)

Yes; think of it as your next project ;-)

> > > In order to do it properly, at some point the responsible mechanism
in
> > BPL
> > > should check whether an instance B() has more an "int nature" or more
a
> > > "float nature". This is easy, in principle, if B is int or float (or
> > > a subclass of int or float) but not obvious at all if B is, for
example,
> > > str
> >
> > neither the built-in function str() nor built-in strings have __int__
or
> > __float__ methods, so in that case there would be no match at all.
>
> And yet
>
> >>> float("2.3")
> 2.2999999999999998
> >>> int("2")
> 2

Yes, that's the float/int types doing the work.

> so I didn't look in to details of how these numbers are constructed.
> str was a bad example. Forget it.

Done.

> > > or an user defined class that defines both __int__ and __float__
> > > methods.
> >
> > In that case I would consider the call ambiguous.
>
> Which is exactly the case for Python int and float:
>
> >>> int.__int__
> <slot wrapper '__int__' of 'int' objects>
> >>> int.__float__
> <slot wrapper '__float__' of 'int' objects>
> >>> float.__float__
> <slot wrapper '__float__' of 'float' objects>
> >>> float.__int__
> <slot wrapper '__int__' of 'float' objects>

When converting to integral argument types, I would use a rule which gave
priority to classes derived from Python int as arguments.


> > > And that approach is unacceptable because it would mean that all
methods
> > > (that I would like to use from Python) of A, must be re-wrapped as
> > > well.
> >
> > No, I don't think so. You only need to wrap them once in
> > class_<A,A_wrapper>. It's only constructors which need to be duplicated
in
> > A_wrapper.
>
> I tried that, but it didn't work out.

<snip>

> It is not clear to me how to include
>
>   class numeric_wrapper: public GiNaC::numeric {
>     numeric_wrapper(PyObject*);
>   }

You need an extra PyObject* argument in the constructor. The first one is
the self pointer, and you can ignore it if you like.

> into this tree. If I remember correctly then
>
>     .add(boost::python::class_<GiNaC::numeric,
>          boost::python::bases<GiNaC::basic, numeric_wrapper> >("numeric")
>          .def_init(boost::python::args<const numeric &>())
>          ...
>
> didn't work because the constructor of numeric_wrapper was never called -

Well, it also doesn't work because numeric_wrapper is not a base of
numeric, but a derived class!

You should follow the formula I described above:

    .add(boost::python::class_<GinaC::numeric, GiNaC::numeric_wrapper
                                >("numeric")
            .def_init(boost::python::args<PyObject*>())


See the documentation for class.hpp if you need an explanation of what's
happening here.


> I meant that I ended up with implementing methods like
>
>   basic_add_basic
>   basic_add_pyobj
>   basic_add_ex
>   basic_add_numeric
>   ex_add_basic
>   ex_add_pyobj
>   ex_add_ex
>   ex_add_numeric
>   numeric_add_basic    /* Though numeric is derived from basic, its
>   numeric_add_numeric     arithmetics is done in a different route */
>   numeric_add_ex
>   numeric_add_py
>
> Similarly for mul,div,sub,pow,etc.

Yikes. I am planning to implement simplified operator exposure this week; I
hope that helps you out.

> And then I noticed that the size of the wrapper was becoming close to the
> size of the library itself, which stopped me and made me doubt if
> this approach is a good one.

Yeah, if you have to mess around with PyObject*, the library isn't doing
you much good is it?

> > > Now, the question is whether it is possible to introduce additional
> > > constructors to library classes without deriving a new class for
that?
> < snip >
> > > What do you think? Would the above be possible in principle?
> >
> > In principle. my_A_init has to build and install a Holder; it can't
just
> > return a new A copy... at least, that's the way the library works now.
>
> Thanks for the hint. I'll try to figure out that if nothing else works.
>
> still-not-convinced-that-bpl-cannot-wrap-real-libraries'ly yours

Me neither, fortunately ;-)

-Dave






More information about the Cplusplus-sig mailing list