1

I've got a struct that looks like this:

foo.h:

struct A { 
    string a;
    string b;
};

It has the matching definition in a .pyx file, as well as a python wrapper:

lib_foo.pyx:

cdef extern from "foo.h":
    cdef struct A:
        string a
        string b

cdef class PyA:
    cdef A* thisptr
    def __cinit__(self):
        self.thisptr = <A*>malloc(sizeof(A))
    cdef A* getThis(self):
        return self.thisptr
    def bar(self):
        self.thisptr.a = "Hello" # works fine!
    def bar2(self):
        return self.thisptr.a # this too!

def bar(PyA f):
    f.getThis().a = "Hello"

def bar2(PyA a):
    return f.getThis().a

This builds fine without any problems, I get a libfoo.so out of it, which I use in a simple test script:

import libfoo
f = libfoo.PyA()
#f.bar() no problems
libfoo.bar(f) # this line and the next have unpredictable behavior!
print libfoo.bar2(f)

Over repeated runs, sometimes this will successfully print "Hello", and other times a segfault. Even stranger, the two functions bar and bar2 seem to work just fine as member functions of class PyA. I have done a bunch of cython wrapping, using just such a getThis() function in order to supply pointers to classes and structs, never had a problem until now. Could it be the strings causing the issue?

edit: setup.py

from distutils.core import setup
import distutils.util as du
from distutils.extension import Extension
from Cython.Distutils import build_ext
import os

os.environ["CC"] = "/app/gcc/4.8.2/bin/g++"
os.environ["CXX"] = "/app/gcc/4.8.2/bin/g++"
os.environ["CPP"] = "/app/gcc/4.8.2/bin/g++"
os.environ["CMAKE_CXX_COMPILER"] = "/app/gcc/4.8.2/bin/g++"

ext_modules = [
    Extension(
        name = "libfoo",
        sources = ["lib_foo.pyx"],
        include_dirs = ["/usr/local/include", "/usr/include"],
        library_dirs = ["/usr/local/lib", "/usr/lib"],
        language = "c++",
    )]

setup(
    name = "libfoo",
    cmdclass = {"build_ext" : build_ext},
    ext_modules = ext_modules
    )
2
  • Can we have complete and runnable examples? Your current ones don't compile. Commented Jun 13, 2014 at 23:25
  • alright I've included my setup.py in the edit. But it should compile just fine, what kind of compilation errors are you getting? Commented Jun 15, 2014 at 19:07

1 Answer 1

1
+50

First, you have to tell Cython that struct A has C++ class semantics (cdef cppclass), not C struct semantics (cdef struct), because it has nontrivial constructor due to embedded strings.

from libcpp.string cimport string

cdef extern from "foo.h":
    cdef cppclass A:
        string a
        string b

Then allocate with operator new, not malloc, exactly as you would do in C++.

def __cinit__(self):
    self.thisptr = new A()

Bypassing the constructor and using uninitializes instances is why you get crashes.

Don't forget to delete this object when Python wrapper is disposed:

def __dealloc__(self):
    del self.thisptr

Also, it's a good idea to provide explicit method for deterministically releasing C-level resources (independent of Python GC):

def dispose(self):  # or "close" or whatever
    del self.thisptr
    self.thisptr = NULL
Sign up to request clarification or add additional context in comments.

3 Comments

unfortunately I'm not able to change the c++ source. I have gotten this method of wrapping classes, not structs, to work. I was hoping there would be a similar feature for structs.
It has nothing to do with C++ source. You need to inform Cython that struct A has C++ semantics using cdef cppclass instead of cdef struct. Actual keyword used to declare it in C++ is irrelevant.
whoa, that did it. struct and class are clearly different things, how is it cython can interpret them as the same? I get that classes are like structs, but in the OOP sense. In any case, enjoy your bounty :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.