I have a C source/header file that are part of a bigger project. I would like to test this as a unit, independent of the real project. While it would be possible to do this in C by creating a new project with a different main()
, I would like to see if I can use Python (3) and its frameworks (eg. nose) to accelerate the construction of tests, use existing reporting frameworks, etc.
I was under the impression that I could do this with CFFI. Here's a sample C file:
// magic.c
// Implementation of magic.
int add(int a, int b)
{
return a;
}
The header:
// magic.h
// Add two numbers (where a + b is not greater than INT_MAX).
int add(int a, int b);
Here's a script that just tries to compile it so I can call some functions:
# cffi_test.py
import cffi
INCLUDE_DIRS = ('.',)
SOURCES = ('magic.c',)
ffi = cffi.FFI()
ffi.set_source(
'_magic_tests',
'#include "magic.h"',
include_dirs = INCLUDE_DIRS,
sources = SOURCES,
libraries = [],
)
ffi.compile()
Ultimately I plan to have this be part of the setup before a set of unit tests eg. a pure Python function test_add()
will call and check the result of the C function add()
via the ffi
object, which is constructed in the test setup.
The above script seems to work; it runs without error, it creates a _magic_tests.c
file, a _magic_tests.cp35-win32.pyd
file, and a Release
directory. I can also import _magic_tests
without an error.
But I can't figure out how to actually call a C function via CFFI. I can't find any documentation for the set_source()
function, and it seems pretty integral to the whole process. The overview mentions it a lot, but the reference contains zero occurrences of it. The docs do have a section on calling functions, but it refers to some lib
object without showing how it's created. If I look at the previous example there's a lib
object created from ffi.dlopen()
, but I don't see how to apply that to something that CFFI itself is producing.
My big question (ie. my X problem) is:
- Is CFFI a reasonable tool to use for calling and testing C functions in a cross platform (Windows 7-10, Linux, OS X) way, and if it is, how?
The questions arising from my current approach (ie. my Y problems) are:
- Where is the documentation for
set_source()
? How can I find out what arguments it takes? - How do I produce
lib
objects that contain the functions I want to call? - Is this the easiest way to use CFFI to call a C function? I do not particularly need or want a shared library or redistributable package to be produced; if it has to happen, that's fine, but it's not necessary. What other approaches could I try?
My current setup is:
- OS: Windows 10
- Python: CPython 3.5.1 32 bit
- Pip: 8.1.2
- CFFI: 1.6.0
- C compiler: whatever comes with Visual C++ Build Tools 2015, linked from this MSDN post
I am using CFFI and pycparser from Christoph Gohlke's repository.
ffibuilder.cdef()
call; that is, I'd be declaring the function twice, and there's a risk it could get out of sync or introduce an error. Do you think there's a way to reduce that duplication? – Clubbable