在给定源代码的情况下,如何使用CFFI调用现有的C函数?

时间:2016-06-21 05:11:24

标签: python c unit-testing python-cffi

我有一个C源/头文件,它是更大项目的一部分。我想将其作为一个单元进行测试,与实际项目无关。虽然可以通过创建一个具有不同main()的新项目在C中执行此操作,但我想看看我是否可以使用Python(3)及其框架(例如,nose)来加速构建测试,使用现有的报告框架等。

我的印象是我可以用CFFI做到这一点。这是一个示例C文件:

// magic.c
// Implementation of magic.
int add(int a, int b)
{
    return a;
}

标题:

// magic.h
// Add two numbers (where a + b is not greater than INT_MAX).
int add(int a, int b);

这是一个只是尝试编译它的脚本,所以我可以调用一些函数:

# 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()

最终,我计划在进行一系列单元测试之前将其作为设置的一部分。纯Python函数test_add()将通过add()对象调用并检查C函数ffi的结果,该对象是在测试设置中构建的。

以上脚本似乎有效;它运行时没有错误,它会创建一个_magic_tests.c文件,一个_magic_tests.cp35-win32.pyd文件和一个Release目录。我也可以import _magic_tests没有错误。

但我无法弄清楚如何通过CFFI实际调用C函数。我找不到set_source()函数的任何文档,它似乎是整个过程中不可或缺的一部分。 overview提到了很多,但reference包含零次。文档执行calling functions上有一个部分,但它引用了一些lib对象而没有显示它是如何创建的。如果我查看前面的示例,那么从lib创建了一个ffi.dlopen()对象,但我不知道如何将其应用于CFFI本身正在生成的内容。

我的大问题(即我的X problem)是:

  • CFFI是一个合理的工具,用于在跨平台(Windows 7-10,Linux,OS X)方式调用和测试C函数,如果是,如何?

我当前的方法(即我的Y problems)产生的问题是:

  • set_source()的文档在哪里?我怎样才能找出需要的论据?
  • 如何生成包含我要调用的函数的lib个对象?
  • 这是使用CFFI调用C函数的最简单方法吗?我并不特别需要或想要生成共享库或可再发行组件包;如果它必须发生,那很好,但没有必要。我可以尝试其他什么方法?

我目前的设置是:

  • 操作系统:Windows 10
  • Python:CPython 3.5.1 32位
  • Pip:8.1.2
  • CFFI:1.6.0
  • C编译器:从this MSDN post
  • 链接的Visual C ++ Build Tools 2015附带的任何内容

我正在使用Christoph Gohlke's repository中的CFFI和pycparser。

1 个答案:

答案 0 :(得分:12)

对于我的项目,我使用cffi来测试我的C代码。 IMHO cffi是一个很好的工具,可以为C代码生成python绑定,因此认为它是一个合理的工具,用于从python调用和测试C函数。但是,您的代码只会像C代码一样跨平台,因为您必须为每个平台编译绑定。

您可以在下面找到一些可以回答您问题的文档。另外,我写了一些示例代码来说明如何使用cffi。有关更大的示例,您可以在https://github.com/ntruessel/qcgc/tree/master/test找到我的项目。

你的四个例子build_magic_tests.py看起来像这样:

from cffi import FFI

ffibuilder = FFI()

# For every function that you want to have a python binding,
# specify its declaration here
ffibuilder.cdef("""
    int add(int a, int b);
                """)

# Here go the sources, most likely only includes and additional functions if necessary
ffibuilder.set_source("magic_tests",
    """
    #include "magic.h"
    """, sources=["magic.c"])

if __name__ == "__main__":
    ffibuilder.compile()

要生成magic_tests模块,您必须运行python build_magic_tests.py。生成的模块可以像这样导入和使用:

from magic_tests import ffi, lib

def run_add():
    assert 4 == lib.add(4, 5)