Using existing header files as cdef

450 views
Skip to first unread message

Oren Tirosh

unread,
Dec 30, 2020, 10:19:08 AM12/30/20
to python-cffi
I have successfully used some existing header files as cffi cdefs by preprocessing them.

The first step is to use the gcc preprocessor with the -dD argument in order to process all directives except #defines. Next, I use the source line number annotation in the preprocessor output to remove lines from includes, leaving just lines from the original file. Finally, I drop any macros with arguments, macros without a body and replace the body to  '...'  for macros that do not evaluate as integer literals. This last step sometimes requires a bit of manual tweaking for excluding macros that are not integers. Another type of tweaking that is sometimes necessary is using -D defines on the preprocessor command line to fix things like missing typedefs (e.g. time_t).

This saves me a lot of work and makes it easier to synchronize with header files that are  modified by other projects.

How about adding something like this to cffi itself?

def cffi_preprocess(source, exclude=(), **kw):
    import subprocess
    from ast import literal_eval
    result = []
    curfile = None
    args = list(preprocess)
    for define in kw.items():
        args.append('-D%s=%s' % define)
    p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    stdout, _ = p.communicate(source)
    for line in stdout.splitlines():
        if line.startswith('# '):
            curfile = line.split()[2]
            continue
        if curfile != '"<stdin>"':
            continue
        if line.startswith('#undef'):
            continue
        if line.startswith('#define'):
            items = line.split(None, 2)
            if len(items) != 3:
                continue
            define, macro, expansion = items
            if '(' in macro:
                continue
            if macro in exclude:
                continue
            try:
                value = literal_eval(expansion)
                if not isinstance(value, (int, long)):
                    continue
            except (SyntaxError, ValueError):
                value = '...'
            result.append('#define %s %s' % (macro, value))
            continue
        result.append(line)
    return '\n'.join(result)

Daniel Holth

unread,
Jan 5, 2021, 11:06:42 AM1/5/21
to python-cffi
That's neat. I do something similar in my wrappers but I didn't know about the -dD option. It's also usually necessary to use the preprocessor to remove attributes '-D"__attribute__(x)"=', or export / calling conventions -D DECLSPEC= -D SDLCALL=


--
-- python-cffi: To unsubscribe from this group, send email to python-cffi...@googlegroups.com. For more options, visit this group at https://groups.google.com/d/forum/python-cffi?hl=en
---
You received this message because you are subscribed to the Google Groups "python-cffi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-cffi...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/python-cffi/3e1ee85c-04b2-46ff-971d-e43c9288d600n%40googlegroups.com.

jean-mich...@csiro.au

unread,
Jan 12, 2021, 6:15:04 AM1/12/21
to python-cffi
Hi,

As I just refactored some codegen material along a similar technique to produce cffi-friendly cdefs.h and structs.h I though I' should pitch in the UR. Using an R package (legacy as I started with Rcpp codegen). I am interested in reusable interop code generation as I managed an extensive scientific modelling C++ codebase with C APIs. Evolved a fairly reusable, admittedly idiosyncratic codebase for this codegen.

Cheers
Reply all
Reply to author
Forward
0 new messages