Reputation: 9169
I am trying to call a C function in Cython and the header looks like this:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <ctype.h>
#include <unistd.h>
#include <math.h>
#include <apriltag.h>
#include <tag36h11.h>
#include <common/getopt.h>
#include <common/image_u8.h>
#include <common/image_u8x4.h>
#include <common/pjpeg.h>
#include <common/zarray.h>
apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
As you can see, I want to return an array of structs, which are type defined to be apriltag_detection_t
. According to the documentation, in order to be able to use this in Cython, I must define some sort of pxd
file which is essentially a copy of header.
However, apriltag_detection_t
is a type that is already defined in apriltag.h
. Furthermore, apriltag_detection_t
has members that are already defined in apriltag.h
. Do I have to recursively redefine all of these types (by hand) in the Cython file before being able to use this library? Where would I write them?
Thanks!
UPDATE 6
Finally at the step of wrapping a function!
from libc.stdint cimport uint8_t
cdef extern from "<apriltag.h>":
cdef struct apriltag_detection:
int id
double c[2]
double p[4][2]
ctypedef apriltag_detection apriltag_detection_t
cdef extern from "tag36h11_detector/tag36h11_detector.h":
apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
def detect(width, height, frame):
return scan_frame(width, height, frame)
tag36h11_detector.pyx:15:21: Cannot convert 'apriltag_detection_t *' to Python object
apriltag_detection_t*
is meant to be an array of structs
UPDATE 5 This seems to have worked.
from libc.stdint cimport uint8_t
cdef extern from "<apriltag.h>":
cdef struct apriltag_detection:
int id
double c[2]
double p[4][2]
ctypedef apriltag_detection apriltag_detection_t
cdef extern from "tag36h11_detector/tag36h11_detector.h":
apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
UPDATE 4 Solved the previous issues by importing the necessary types.
from libc.stdint cimport uint8_t
cdef extern from "apriltag.h":
cdef struct apriltag_detection:
int id
double c[2]
double p[4][2]
ctypedef apriltag_detection apriltag_detection_t
cdef extern from "tag36h11_detector.h":
apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
tag36h11_detector.c:533:10: fatal error: 'apriltag.h' file not found
I'm not sure where this is coming from because my header file, as provided in the original post, required <apriltag.h>
and not "apriltag.h"
. This is what my setup.py
looks like.
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(ext_modules=cythonize(Extension(\
name='tag36h11_detector', \
sources=["tag36h11_detector.pyx", \
"tag36h11_detector/tag36h11_detector.c"], \
include_path=["/usr/local/include/apriltag"], \
libraries=["apriltag"])))
UPDATE 3
cdef extern from "apriltag.h":
cdef struct apriltag_detection:
int id
double c[2]
double p[4][2]
ctypedef apriltag_detection apriltag_detection_t
cdef extern from "tag36h11_detector.h":
apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
tag36h11_detector.pyx:10:60: 'uint8_t' is not a type identifier
UPDATE 2
This is my current code and the following is the compilation error
// tag36h11_detector.pyx
cdef extern from "apriltag.h":
ctypedef apriltag_detection_t:
int id
double c[2]
double p[4][2]
cdef extern from "tag36h11_detector.h":
apriltag_detection_t* scan_frame(int width, int height, uint8_t* data);
// apriltag.h
...
typedef struct apriltag_detector apriltag_detector_t;
...
tag36h11_detector.pyx:2:33: Syntax error in ctypedef statement
UPDATE 1
So I'm trying to interface with the above header file with Python (which I wrote and implemented) with types defined in apriltag.h
(from a library).
cdef extern from "apriltag.h":
struct apriltag_detection:
int id
double c[2]
double p[4][2]
cdef extern from "tag36h11_detector.h":
struct apriltag_detection* scan_frame(int width, int height, uint8_t* data);
When I try compiling the above, I get
tag36h11_detector.pyx:8:29: Syntax error in struct or union definition
Upvotes: 4
Views: 2511
Reputation: 34377
This is basically covered in this part of cython documentation, which says that you only need to import parts, which you will be using in your cython code.
For example let's take a look at the following C interface:
#struct.h
struct Needed{
int a;
};
struct NotNeeded{
int b;
};
struct Combined{
struct Needed needed;
struct NotNeeded notneeded;
};
struct Combined create(void);
You would like to call the function create
and use the value a
from the Needed
struct, that means you have to import struct Needed
and parts of struct Combined
but not NotNeeded
in your cython code:
#struct_import.pyx
cdef extern from "struct.h":
struct Needed: # use "ctypedef struct Needed" if defined with typedef in h-file!
int a
struct Combined: #NotNeeded is missing!
Needed needed
Combined create()
def get_needed():
return create().needed.a #should be 42!
Now, using setup.py
(its content can be seen further below, the same goes for the content of struct.c
to) we get the expected result:
[] python setup.py build_ext --inplace
[] python -c "python -c "import struct_import as si; print si.get_needed()"
42
If you use cython to glue some C code together, it is possible, even less is necessary. In oour example, if we had a C-function which would extract the needed value from the Combined
struct:
#struct.h
...
int extract(struct Combined combined);//returns combined.needed.a
We could use it as follows in the pyx-file:
#struct_import.pyx
cdef extern from "struct.h":
struct Combined:
pass #nothing imported!
Combined create()
int extract(Combined combined)
def get_needed():
return extract(create()) #should be 42!
And despite we didn't import the Needed
struct at all, it works as good as the first version.
So if it becomes a chore to import all those structs, one could extend the c interface to make it unnecessary.
To make the example complete, here the missing setup.py and struct.c files:
#setup.py:
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(ext_modules=cythonize(Extension(
name='struct_import',
sources = ["struct_import.pyx", "struct.c"]
)))
and
//struct.c
#include "struct.h"
struct Combined create(){
struct Combined res;
res.needed.a=42;
res.notneeded.b=21;
return res;
}
int extract(struct Combined combined){
return combined.needed.a;
}
Upvotes: 6