8

I am trying to pass a struct back into my Python from a c file. Let's say I have a file pointc.c like this:

typedef struct Point {
    int x;
    int y;
} Point;

struct Point make_and_send_point(int x, int y);

struct Point make_and_send_point(int x, int y) {
    struct Point p = {x, y};
    return p;
}

Then I set-up a point.pyx file like this:

"# distutils: language = c"
# distutils: sources = pointc.c

cdef struct Point:
    int x
    int y

cdef extern from "pointc.c":
    Point make_and_send_point(int x, int y)

def make_point(int x, int y):
    return make_and_send_point(x, y) // This won't work, but compiles without the 'return' in-front of the function call

How do I get the returned struct into my Python? Is this kind of thing only possible by creating a struct in the Cython and sending by reference to a void c function?

As a reference, my setup.py is:

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

setup(ext_modules = cythonize(
      "point.pyx",
      language="c"
     )
)
4
  • This is not going to be a helpful comment, but I never really understood why does cython exist. Writing python modules in c is rather easy and better than using cython. Commented Mar 2, 2018 at 15:04
  • Fair enough, are there any resources you suggest that I look at for writing python modules directly? I am new to interfacing c with python. It was my understanding that Cython had some powerful tools, albeit somewhat of a cost to get into. Thanks for your comment! Commented Mar 2, 2018 at 15:10
  • Yes, the python official documentation. Just google writing python modules in c and choose the version that you will be targeting. That's actually a pain in the **** about python, the 2 and 3 version incompatibility sucks. Commented Mar 2, 2018 at 15:19
  • 4
    Contrasting opinion - if you're going to be interacting much with python objects, I think cython is significantly more convenient - no new APIs to learn and reference counting book-keeping, which can be subtle to get right, is taken care of. Commented Mar 2, 2018 at 16:18

2 Answers 2

8

Most typically you would write some kind of wrapper class that holds the c-level struct, for example:

# point.pyx
cdef extern from "pointc.c":
    ctypedef struct Point:
        int x
        int y
    Point make_and_send_point(int x, int y)

cdef class PyPoint:
    cdef Point p

    def __init__(self, x, y):
        self.p = make_and_send_point(x, y)

    @property
    def x(self):
       return self.p.x

    @property
    def y(self):
        return self.p.y

In usage

>>> import point
>>> p = point.PyPoint(10, 10)
>>> p.x
10
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I went about it slightly different, kind of like @DavidW points out by just getting the returned dictionary. But I like this answer, it shows how I could actually implement classes between c and python a little clearer than I was able to find online. Thanks.
5

Cython's default behaviour given a struct is to convert it to a Python dictionary, which may be good enough for you. (This only works for structs made up of simple types though).

There's a couple of reasons why this isn't working. First you should do cdef extern from from headers, not source files, otherwise you get errors about multiple definitions (I assume this is just a mistake in creating your minimal example). Second you need to put the definition of Point within your cdef extern block:

cdef extern from "pointc.h":
    cdef struct Point:
        int x
        int y

If you don't do that then Cython creates a mangled internal name for your struct (__pyx_t_5point_Point) which doesn't match the C function signature and thus it fails.

With this corrected, you get the correct default behaviour of converting structs to dicts. (This should work both ways - you can convert dicts back to structs). In the event that this isn't what you want, follow @chrisb's answer

3 Comments

Thanks, yea this is what I ended up doing initially. I made the Cython struct then passed it by reference to a void make_and_send function in the c.
By the way, the c file is fairly simple so I put all the function declarations at the top and I don't get any warning by doing cdef extern from "pointc.c". Is there documentation that says I should use headers and why?
In C terms the cdef extern from ... block includes the file. As a rule in C you should only include headers to avoid having multiple copies of the same code. This is something that you should look up from a C point of view, rather than a Cython point of view. Depending on the compiler you may not get an error/warning, so if it works then it's fine, but it might not be best practice.

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.