[capi-sig] Announcing project PyBindGen

Gustavo Carneiro gjcarneiro at gmail.com
Tue Sep 25 14:21:34 CEST 2007


On 25/09/2007, Stefan Behnel <python_capi at behnel.de> wrote:
>
>
> Gustavo Carneiro wrote:
> > On 25/09/2007, *Stefan Behnel* <python_capi at behnel.de
> > <mailto:python_capi at behnel.de>> wrote:
> >
> > I don't understand what "exact API mapping" means.
>
> What I meant was: is the C(++) API mapped directly to Python or can you
> write
> abstraction code in between?


Generally it requires a direct mapping.  Why would you want to write code in
the middle?  Although recently I added support for adding "user functions"
to make them appear as methods of objects.

Anyway that's not the point.  The PyBindGen design is not set in stone...

> But yes, so far you have to write all this by hand.  Although would love
> > to have automatic header file scanning, one day... unfortunately I have
> > not had enough time for that, and would rather focus on the code
> > generation for now.  Lots of neat things could be done later on top of
> > the pybindgen python module interface...
>
> But that's what most wrapper generators are there for: move the work from
> the
> developer into a generator. If you have to write code for every
> function/method/class you wrap, I think you're better of with
> Pyrex/Cython, as
> Pyrex code is readable and meaningful Python, not just a wrapper writer
> script.


Come on, I wouldn't say this interface is unreadable.  It's stuff like this:

    mod = Module('foo')
    Foo = CppClass('Foo', automatic_type_narrowing=True)
    mod.add_class(Foo)
    Foo.add_static_attribute(ReturnValue.new('int'), 'instance_count')
    Foo.add_constructor(
        CppConstructor([Parameter.new('std::string', 'datum')]))
    Foo.add_constructor(CppConstructor([]))
    Foo.add_method(CppMethod(ReturnValue.new('std::string'), 'get_datum',
[]))

Not that hard to write this, is it?

> I don't  like having to learn a new language for this.  Just like
> > Boost.Python users are comfortable with C++ and avoid having to learn a
> > new language, PyBindGen users are comfortable with Python and don't have
> > to learn a new language.
>
> Pyrex is *almost* Python, no new language to learn. And Cython is even
> closer,
> as it features a couple of Python 2.5 and Python 3k features.
>
>
> > Besides, pyrex/cython C++ support is poor or nonexistent.  Although I
> > acknowledge that if you wrap pure C libraries then this point is moot.
>
> There are a couple of patches that improve the support. Admittedly, Pyrex
> does
> not target C++ libraries, so OO support is mostly missing. But nothing
> keeps
> you from implementing an object interface against C++ classes. I bet the
> Cython project would be happy to include it.
>
>
> > Finally, have you ever looked at the code generated by pyrex?
>
> Yes, definitely, it's pretty readable, well optimised (with Cython, that
> is)
> and straight forward (ok, loops are ugly and over-optimised - but *very*
> fast). And I totally like the Cython feature of keeping the surrounding
> Cython
> code inside a C comment. That way, you immediately know what you are
> looking at.


For instance, I don't find this very readable:

static PyObject *__pyx_f_4spam_4Spam_get_amount(PyObject *__pyx_v_self,
PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_f_4spam_4Spam_get_amount(PyObject *__pyx_v_self,
PyObject *__pyx_args, PyObject *__pyx_kwds) {
  PyObject *__pyx_r;
  PyObject *__pyx_1 = 0;
  static char *__pyx_argnames[] = {0};
  if (unlikely(!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "",
__pyx_argnames))) return 0;
  Py_INCREF(__pyx_v_self);

  /* "/Users/robert/sage/pyrex/cython-0.9.6.3/Demos/spam.pyx":16
 *
 *   def get_amount(self):
 *     return self.amount             # <<<<<<<<<<<<<<
 *
 *   def set_amount(self, new_amount):
 */
  __pyx_1 = PyInt_FromLong(((struct __pyx_obj_4spam_Spam
*)__pyx_v_self)->amount); if (unlikely(!__pyx_1)) {__pyx_filename =
__pyx_f[0]; __pyx_lineno = 16; goto __pyx_L1;}
  __pyx_r = __pyx_1;
  __pyx_1 = 0;
  goto __pyx_L0;

  __pyx_r = Py_None; Py_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1:;
  Py_XDECREF(__pyx_1);
  __Pyx_AddTraceback("spam.Spam.get_amount");
  __pyx_r = 0;
  __pyx_L0:;
  Py_DECREF(__pyx_v_self);
  return __pyx_r;
}


Compared with this code generated by pybindgen:

static PyObject *
_wrap_fooinvoke_some_object_get_prefix()
{
    PyObject *py_retval;
    std::string retval;

    retval = invoke_some_object_get_prefix();
    py_retval = Py_BuildValue("s#", retval.c_str(), retval.size());
    return py_retval;
}

Pybindgen generated code is almost as clean as the code you would write by
hand.  To me that counts a lot.  I know some people don't care what happens
underneath the tools they use as long as it works.  I am not that kind of
person.

-- 
Gustavo J. A. M. Carneiro
INESC Porto, Telecommunications and Multimedia Unit
"The universe is always one step beyond logic." -- Frank Herbert


More information about the capi-sig mailing list