3

I am trying to interface Python to the Bullet Physics C++ library using Cython. I would like to do this at C speeds, or near C speeds with minimal variable packing and unpacking from Python. I was able to use this approach to create a Cython interface to OpenGL (though OpenGL has a much simpler, non object-oriented C API, compared to Bullet Physics). My OpenGL interfaces works well but progress has been slow on interfacing with Bullet Physics.

I have read the Cython C++ tutorial and other related posts here but so far no luck.

The organization of Bullet Physics is a bit complex, but for this example hopefully it will be enough to know that btDbvtBroadphase is a struct which is derived from the btBroadphaseInterface class. As you probably know, structs can be derived from classes (inherit from them) and there isn't too much difference between the two--in C++ at least. However, Cython seems to only allow "new" to be done on classes and not on structs. I think that leads to my first problem.

The declarations in the Bullet Physics .h files look like this:

class btBroadphaseInterface
{
    ...

and

struct  btDbvtBroadphase : btBroadphaseInterface
{
    ...

The very simple line of C++ code I'm trying to replicate efficiently in Cython is this:

btBroadphaseInterface* broadphase = new btDbvtBroadphase();

So, since in Cython I can't do a "new" on a btDbvtBroadphase struct as is done in C++, how do I instantiate this struct, derived from the class, so that all initializers are called and methods set up inside?

I have seen some references to limitations on inheritance within Cython.

I can obviously malloc() the space for the structure, but that doesn't help me much, I think. I can't see how any sort of casting would help.

I have the Cython ".pxd" stuff showing how I do the cdef extern from the .h file but I didn't think that would help clarify this question, so I'll omit that unless someone thinks it might be helpful.

I have tried many variants of cdefs with no luck. Here is what it looks like now:

cdef extern from "bullet/btBulletDynamicsCommon.h":
    cdef cppclass btBroadphaseInterface:
        pass

    cdef cppclass btDbvtBroadphase(btBroadphaseInterface):
        pass

When I try to write a constructor that will do a "new" on this struct derived from a class, like this:

cdef class PbtDbvtBroadphase:
    cdef btBroadphaseInterface *bp

    def __cinit__(self):
        self.bp=<btBroadphaseInterface *> new btDbvtBroadphase()

The compiler fails with:

Error compiling Cython file: ------------------------------------------------------------ ...

cdef class PbtDbvtBroadphase: cdef btBroadphaseInterface *bp

def __cinit__(self):
    self.bp=<btBroadphaseInterface *> new btDbvtBroadphase()

^

fun4.pyx:173:46: new operator can only be applied to a C++ class

Edit: improved title, which I had initially entered as just a string of keywords

Edit2: added the cdefs and compiler output

I'm accepting the very patient @DavidW's answer since it demonstrated that things should work. I think what was happening was that I had a stray .pxd file named with the same basename as my .pyx file, and that was being integrated into the build even though I was not using distutils but rather just cython from the command line. The .pxd was left over from an earlier test when I thought my build method might not be giving the same results as the distutils/setup.py approach. I became convinced my build method wasn't related to the problem and went back to my preferred approach (putting cdefs in the .pyx file and just compiling and linking via a simple shell script as shown in one of my comments below) but the .pxd file was still around and silently still getting pulled in by the cython compiler without my knowledge. I discovered this when I noticed that I was getting "redeclared" errors on compilation. I missed the "redeclared" which were scrolling away and only saw the errors on the new constructor attempts.

rpdrewes
  • 147
  • 11

1 Answers1

3

The easiest option is just to tell Cython it's a class:

cdef cppclass btDvbtBroadphase(btBroadphaseInterface):
    #etc

The difference between struct and class in C++ is only really the default visibility of members. From Cython's point of view it behaves the same and so it doesn't matter that what you tell Cython doesn't match the C++ code.

(It may also be worth omitting telling Cython about the inheritance. If you don't use the inheritance in Cython then it doesn't need to know. It's often easier not to reproduce the full hierarchy but only the bits you need)


Edit: the following code works for me:

header.hpp:

class btBroadphaseInterface {

};

struct btDbvtBroadphase : btBroadphaseInterface {

};

pymod.pyx:

cdef extern from "header.hpp":
    cppclass btBroadphaseInterface:
        pass

    cppclass btDbvtBroadphase(btBroadphaseInterface):
        pass

cdef class PyBroadphase:
    cdef btBroadphaseInterface* pt
    def __cinit__(self):
        self.pt = new btDbvtBroadphase()

    def __dealloc__(self):
        del self.pt

setup.py

from distutils.core import setup, Extension
from Cython.Build import cythonize

setup(ext_modules = cythonize(Extension(
           "pymod",                                # the extension name
           sources=["pymod.pyx",], # the Cython source and
                                                  # additional C++ source files
           language="c++",                        # generate and compile C++ code
      )))
DavidW
  • 29,336
  • 6
  • 55
  • 86
  • Regarding your last comment, I am not really able to change the structure of the inheritance because the library is produced by others and I am just trying to hook into it. I could make changes to it but that would become a maintainability headache. Regarding your first suggestion, it differs from what I was doing by having the btBroadphaseInterface in the parenthesis, indicating inheritance presumably. I am testing that now, and variants, but it does not seem to be helping. I will reply again once I have exhausted possibilities on that. Thanks. – rpdrewes Mar 31 '17 at 15:15
  • The last comment was "don't tell Cython about the inheritance if you don't have to". It wasn't recommending changing the C++ code – DavidW Mar 31 '17 at 15:17
  • One thing to start with, I think you meant: `cdef cppclass btDbvtBroadphase(btBroadphaseInterface):` with the Dbvt, the actual struct name. – rpdrewes Mar 31 '17 at 15:17
  • Regarding skipping the inheritance, I don't see how I could do that. The thing I want to instantiate is a *struct* which is derived from a *class*. I'm not sure how to instantiate a derived struct (with methods and so on) in Cython. – rpdrewes Mar 31 '17 at 15:20
  • I have the following: `cdef extern from "bullet/btBulletDynamicsCommon.h": cdef cppclass btDbvtBroadphase(btBroadphaseInterface): pass` (sorry, can't seem to get newlines right in comments) When I try to compile `cdef class PbtDbvtBroadphase: cdef btBroadphaseInterface *bp def __cinit__(self): self.bp= new btDbvtBroadphase() ` I get an error "fun4.pyx:172:46: new operator can only be applied to a C++ class" – rpdrewes Mar 31 '17 at 15:24
  • You're right about the name. The point about the inheritance is that Cython doesn't need to know what the struct inherits from if it doesn't use that information itself. It – DavidW Mar 31 '17 at 15:24
  • Do you also have `cdef cppclass btBroadphaseInterface`? – DavidW Mar 31 '17 at 15:24
  • Yes, I do also have `cdef cppclass btBroadphaseInterface`. I have also tried just making it `cdef class btBroadphaseInterface. No joy. Despite my cdef, Cython seems to know that btDbvtBroadphase is a struct, not a class, and refuse to allow new to be used on it, even though they are really about the same thing as I pointed out in my question and you also pointed out. – rpdrewes Mar 31 '17 at 15:29
  • See edit. I've posted complete code that does compile and run for me – DavidW Mar 31 '17 at 15:33
  • Do make sure you're compiling the file in C++ mode – DavidW Mar 31 '17 at 15:35
  • Your example works for me. I think there must be something more complicated going on the bullet .h files that is complicating matters, but I can't figure out what it might be. I am looking to that now. – rpdrewes Mar 31 '17 at 15:46
  • Yes, I am compiling the file in c++ mode. I am not using setup.py since I find that kind of awkard and I want to create an executable. I am using a simple compilation as follows, and it works on your example: "cython --cplus --embed pyxmod.pyx && g++ -O3 -I/usr/include/bullet -I/usr/include/python2.7 -o pyxmod4 pyxmod.cpp -lLinearMath -lBulletSoftBody -lBulletCollision -lBulletDynamics -lpython2.7 -lm -lutil -ldl -l GL -l GLU -l glut" – rpdrewes Mar 31 '17 at 15:48