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)