用Cython封装C++库

24 投票
3 回答
18419 浏览
提问于 2025-04-15 18:16

我刚接触Cython,想用它来包装一个C/C++的静态库。我做了一个简单的例子,如下所示。

Test.h:

#ifndef TEST_H
#define TEST_H

int add(int a, int b);
int multipy(int a, int b);

#endif

Test.cpp:

#include "test.h"
int add(int a, int b)
{
    return a+b;

}

int multipy(int a, int b)
{
    return a*b;
} 

然后我用g++来编译和构建它。

g++ -c test.cpp -o libtest.o
ar rcs libtest.a libtest.o

现在我得到了一个叫做 libtest.a 的静态库。

Test.pyx:

cdef extern from "test.h":
        int add(int a,int b)
        int multipy(int a,int b)

print add(2,3)

Setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("test",
                     ["test.pyx"],
                     language='c++',
                     include_dirs=[r'.'],
                     library_dirs=[r'.'],
                     libraries=['libtest']
                     )]

setup(
  name = 'test',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

然后我调用了:

python setup.py build_ext --compiler=mingw32 --inplace

输出结果是:

running build_ext
cythoning test.pyx to test.cpp
building 'test' extension
creating build
creating build\temp.win32-2.6
creating build\temp.win32-2.6\Release
C:\Program Files\pythonxy\mingw\bin\gcc.exe -mno-cygwin -mdll -O -Wall -I. -IC:\
Python26\include -IC:\Python26\PC -c test.cpp -o build\temp.win32-2.6\Release\test.o
writing build\temp.win32-2.6\Release\test.def
C:\Program Files\pythonxy\mingw\bin\g++.exe -mno-cygwin -mdll -static --entry _D
llMain@12 --output-lib build\temp.win32-2.6\Release\libtest.a --def build\temp.w
in32-2.6\Release\test.def -s build\temp.win32-2.6\Release\test.o -L. -LC:\Python
26\libs -LC:\Python26\PCbuild -ltest -lpython26 -lmsvcr90 -o test.pyd
g++: build\temp.win32-2.6\Release\libtest.a: No such file or directory
error: command 'g++' failed with exit status 1

我还尝试用 libraries=['test'] 来代替 libraries=['libtest'],但得到了同样的错误。

有没有什么线索可以帮助我解决这个问题?

3 个回答

2

我觉得你可以通过指定正确的 library_dirs 来解决这个具体的问题(也就是你放置 libtest.a 的地方——显然它没有被找到),但是我觉得你会遇到另一个问题——你的入口点没有正确声明为 extern "C",所以函数的名字会被 C++ 编译器“搞乱”(你可以看看从 libtest.a 导出的名字,你会发现的!),这样除了 C++ 以外的其他语言(包括 C、Cython 等)就会很难调用这些函数。解决办法就是把它们声明为 extern "C"

5

你的 Test.pyx 文件没有按照你预期的那样工作。print add(2,3) 这一行 不会 调用 add() 这个 C++ 函数;你需要明确地创建一个包装函数来实现这一点。Cython 不会自动为你创建包装函数。

像下面这样的代码可能是你想要的:

cdef extern from "test.h":
        int _add "add"(int a,int b)
        int _multiply "multiply"(int a,int b)

def add(a, b):
    return _add(a, b)

def multiply(a, b):
    return _multiply(a, b)

print add(2, 3)

你可以查看 Cython 的 文档,获取更多详细信息。

29

如果你的C++代码只是被包装器使用,另一种选择是让设置程序编译你的.cpp文件,像这样:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("test",
                     ["test.pyx", "test.cpp"],
                     language='c++',
                     )]

setup(
  name = 'test',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

要链接到一个静态库,你需要在你的Extension中使用extra_objects这个参数:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [Extension("test",
                     ["test.pyx"],
                     language='c++',
                     extra_objects=["libtest.a"],
                     )]

setup(
  name = 'test',
  cmdclass = {'build_ext': build_ext},
  ext_modules = ext_modules
)

撰写回答