Using Python's CFFI and excluding system headers
Asked Answered
P

1

3

I'm trying to use Python's CFFI to develop Python bindings to a scientific model written in C. The CFFI documentation is a little sparse and I'm stuck at the cdef stage.

My process up to now has followed these steps:

  1. Preprocess the header files:

    gcc -E -gcc -std=c99 -E -P src/my_c_interface.c -I./include/ -I../shared/include/ > header.txt

    This produces a text file that includes all the C declarations included in the header files in my include/ directories. It also includes declarations for standard libraries (I'm pretty sure this is where my problem is coming from). The header.txt looks something like this (the full header.txt is here):

    Beginning with system header stuff:

    typedef float float_t;
    typedef double double_t;
    extern int __math_errhandling(void);
    extern int __fpclassifyf(float);
    extern int __fpclassifyd(double);
    extern int __fpclassifyl(long double);
    extern __inline __attribute__((__gnu_inline__)) __attribute__ ((__always_inline__)) int __inline_isfinitef(float);
    extern __inline __attribute__((__gnu_inline__)) __attribute__
    

    and ending with pieces defined in my headers:

    FILE *LOG_DEST;
    void finalize_logging(void);
    void get_current_datetime(char *cdt);
    void get_logname(const char *path, int id, char *filename);
    
  2. Use cffi to parse the preprocessed header files:

    import cffi
    
    ffi = cffi.FFI()
    
    with open('drivers/python/header.txt') as f_headers:
        ffi.cdef(f_headers.read())  # error is raised here
    
    ffi.compile()
    

    This returns the following error (full traceback is here):

    /Users/me/anaconda/lib/python3.4/site-packages/cffi/cparser.py in convert_pycparser_error(self, e, csource)
        157         else:
        158             msg = 'parse error\n%s' % (msg,)
    --> 159         raise api.CDefError(msg)
        160 
        161     def parse(self, csource, override=False, packed=False):
    
    CDefError: cannot parse "extern __inline __attribute__((__gnu_inline__)) __attribute__ ((__always_inline__)) int __inline_isfinitef(float);"
    :10:17: before: __attribute_
    

Given where I'm at, I have a few questions for those more familiar with cffi than I:

  1. Is it possible to have the preprocessor exclude system headers?
  2. Is anyone aware of any examples that are more complex then those shown in the cffi docs? Real world examples would be helpful.
  3. Looking at my example shown above, am I missing something major?
Pellagra answered 3/8, 2015 at 19:36 Comment(2)
Demos: bitbucket.org/cffi/cffi/src/default/demo (see e.g. _curses*.py). Real examples: google python cffiVernalize
Thanks. I had seen their demos and had certainly tried to goodle python cffi. What I haven't found yet is a way to programmatically create the header text that goes into ffi.cdef(...).Pellagra
V
3

This is a somewhat generic answer:

While it is possible to use the gcc -E approach and manually "trim" the result, it is not the recommended way to use CFFI. Instead, the cdef() code is usually made either incrementally (adding functions as needed) or in bulk from an edited copy of the .h file. The first approach works best when copying from man pages; the second approach is for the case where we want complete access to a single 3rd-party library.

In all cases, it is very likely that you need to edit the .h file anyway: the recommended approach is to use ffi.set_source(), and remove from the cdef() any declarations that are superfluous, replacing them with .... For example, the actual .h file may contain the declaration #define FOOBAR 42, but the value 42 should not be relied upon (e.g. it could change in the future), so the cdef() should rather receive #define FOOBAR ....

Vernalize answered 4/8, 2015 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.