How can I reference #defines in a C file from python?
Asked Answered
S

5

11

I have a C file that has a bunch of #defines for bits that I'd like to reference from python. There's enough of them that I'd rather not copy them into my python code, instead is there an accepted method to reference them directly from python?

Note: I know I can just open the header file and parse it, that would be simple, but if there's a more pythonic way, I'd like to use it.

Edit:

These are very simple #defines that define the meanings of bits in a mask, for example:

#define FOO_A 0x3
#define FOO_B 0x5
Sarnoff answered 27/8, 2012 at 18:22 Comment(3)
possible duplicate of Reliably converting C preprocessor macros to python codeSharanshard
I think this is a duplicate to #2654829Sharanshard
@MattiLyra - Perhaps. But seeing as that question currently has no answers, this isn't a particularly helpful point.Unshaped
O
7

Running under the assumption that the C .h file contains only #defines (and therefore has nothing external to link against), then the following would work with swig 2.0 (http://www.swig.org/) and python 2.7 (tested). Suppose the file containing just defines is named just_defines.h as above:

#define FOO_A 0x3
#define FOO_B 0x5

Then:

swig -python -module just just_defines.h ## generates just_defines.py and just_defines_wrap.c
gcc -c -fpic just_defines_wrap.c -I/usr/include/python2.7 -I. ## creates just_defines_wrap.o
gcc -shared just_defines_wrap.o -o _just.so ## create _just.so, goes with just_defines.py

Usage:

$ python 
Python 2.7.3 (default, Aug  1 2012, 05:16:07) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import just
>>> dir(just)
['FOO_A', 'FOO_B', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_just', '_newclass', '_object', '_swig_getattr', '_swig_property', '_swig_repr', '_swig_setattr', '_swig_setattr_nondynamic']
>>> just.FOO_A
3
>>> just.FOO_B
5
>>> 

If the .h file also contains entry points, then you need to link against some library (or more) to resolve those entry points. That makes the solution a little more complicated since you may have to hunt down the correct libs. But for a "just defines case" you don't have to worry about this.

Ous answered 27/8, 2012 at 18:53 Comment(1)
This method worked great! I need to decide if it's worth the time to write some simple C to interface with this header though. It might just be simpler for what I need to parse the C header by hand.Sarnoff
T
7

You might have some luck with the h2py.py script found in the Tools/scripts directory of the Python source tarball. While it can't handle complex preprocessor macros, it might be sufficient for your needs.

Here is a description of the functionality from the top of the script:

Read #define's and translate to Python code. Handle #include statements. Handle #define macros with one argument. Anything that isn't recognized or doesn't translate into valid Python is ignored.

Without filename arguments, acts as a filter. If one or more filenames are given, output is written to corresponding filenames in the local directory, translated to all uppercase, with the extension replaced by ".py".

By passing one or more options of the form "-i regular_expression" you can specify additional strings to be ignored. This is useful e.g. to ignore casts to u_long: simply specify "-i '(u_long)'".

Theophany answered 28/8, 2012 at 4:38 Comment(0)
U
1

#defines are macros, that have no meaning whatsoever outside of your C compiler's preprocessor. As such, they are the bane of multi-language programmers everywhere. (For example, see this Ada question: Setting the license for modules in the linux kernel from two weeks ago).

Short of running your source code through the C-preprocessor, there really is no good way to deal with them. I typically just figure out what they evalutate to (in complex cases, often there's no better way to do this than to actually compile and run the damn code!), and hard-code that value into my program.

The (well one of the) annoying parts is that the C preprocessor is considered by C coders to be a very simple little thing that they often use without even giving a second thought to. As a result, they tend to be shocked that it causes big problems for interoperability, while we can deal with most other problems C throws at us fairly easily.

In the simple case shown above, by far the easiest way to handle it would be to encode the same two values in constants in your python program somewhere. If keeping up with changes is a big deal, it probably wouldn't be too much trouble to write a Python program to parse those values out of the file. However, you'd have to realise that your C code would only re-evaluate those values on a compile, while your python program would do it whenever it runs (and thus should probably only be run when the C code is also compiled).

Unshaped answered 27/8, 2012 at 18:29 Comment(1)
I think for many people the simple method you suggested will work, either parsing the header or leading the #defines into an editor and doing search/replace to translate them into python consts, depending on how often the header might change.Sarnoff
A
1

If you're writing an extension module, use http://docs.python.org/3/c-api/module.html#PyModule_AddIntMacro

Ascus answered 4/3, 2014 at 22:12 Comment(0)
J
0

I had almost exactly this same problem so wrote a Python script to parse the C file. It's intended to be renamed to match your c file (but with .py instead of .h) and imported as a Python module.

Code: https://gist.github.com/camlee/3bf869a5bf39ac5954fdaabbe6a3f437

Example:

configuration.h

#define VERBOSE 3
#define DEBUG 1
#ifdef DEBUG
    #define DEBUG_FILE "debug.log"
#else
    #define NOT_DEBUGGING 1
#endif

Using from Python:

>>> import configuration
>>> print("The verbosity level is %s" % configuration.VERBOSE)
The verbosity level is 3
>>> configuration.DEBUG_FILE
'"debug.log"'
>>> configuration.NOT_DEBUGGING is None
True
Jacobine answered 13/9, 2016 at 5:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.