在C++中嵌入Python函数

4
我正在尝试使用Cython从Python生成C代码,但似乎存在一些名称混淆问题。我首先将代码从Python转换为C代码,然后使用gcc将代码编译为.so文件。我想要使用Cython而不是C/Python API的原因是因为我以后将在更复杂的类上使用它,希望将其作为库以提高速度等(我很难找到从Python到C++的人,因为通常情况下都是反过来)。以下是我尝试执行该代码时拥有的所有代码(但失败了)。任何意见都将不胜感激。谢谢!
#hello.pyx
def say_hello():
    print "Hello World!"

#generate the c code
cython -a hello.pyx

#creates the shared library
gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.6 -o libhello.so hello.c

//temp.cpp
#include <iostream>
extern "C" {
void say_hello();
};

using namespace std;

int main(){
    say_hello();
    return 1;
};

#attempt to compile (this is where it fails)
g++ -I/usr/include/python2.6/ -lpython2.6 -L./ -lhello temp.cpp -o temp

这是错误信息:

/tmp/ccpKHOMl.o: In function main: temp.cpp:(.text+0x5): undefined reference to say_hello' /tmp/ccpKHOMl.o: 
In function __static_initialization_and_destruction_0(int, int): 
  temp.cpp:(.text+0x33): undefined reference to std::ios_base::Init::Init()  
  temp.cpp:(.text+0x38): undefined reference to std::ios_base::Init::~Init() 
collect2: ld returned 1 exit status 

它是如何失败的?你得到了什么错误? - Blender
也许你可以在失败的步骤中包含你收到的错误信息。 - Greg Hewgill
/tmp/ccpKHOMl.o:在函数“main”中: temp.cpp:(.text+0x5):对“say_hello”的引用未定义 /tmp/ccpKHOMl.o:在函数“__static_initialization_and_destruction_0(int, int)”中: temp.cpp:(.text+0x33):对“std::ios_base::Init::Init()”的引用未定义 temp.cpp:(.text+0x38):对“std::ios_base::Init::~Init()”的引用未定义 collect2: ld 返回 1 退出状态 - Andrew
3个回答

3
你无法通过这种方式获得所需的互操作性。如果你打开并检查hello.c文件,你不会在其中找到"static int say_hello"。Cython旨在让Python使用C库,而不是让C库使用Python。
你可以在文档中查看这里,但不幸的是,这种支持仍然适用于“负责”的Python解释器,而你要寻找的是相反的情况。

http://docs.python.org/release/2.5.4/ext/callingPython.html

还有关于“将Python嵌入到另一个应用程序中”的入门指南。

http://docs.python.org/2/extending/embedding.html

我不知道你的要求是什么,但在某些情况下,你可以成功地将数据写入文件,调用一个Python程序进行处理,然后从另一个文件中解析结果。虽然这样有点丑陋且比在内存中保留数据慢,但在许多情况下完全可行。

这就是我认为存在名称混淆问题的原因。也许编译器有一个标志可以避免这种情况。不过还是感谢您的回答。 - Andrew
我正在处理非常高频的数据,因此写入文件的速度太慢了。所有的计算都必须在内存中完成。 - Andrew
明白了。如果是这种情况,你可能也没有时间通过套接字、TCP等进行RPC。很遗憾,似乎没有办法使它工作,即使它能够工作,速度也可能太慢了。 - Mike Sandford
实际上,所有东西都将通过一个UDP套接字进行RPC,但我们正在通过一个10G端口运行,因此数据进入非常快。Python接收器实际上无法跟上数据的到来。因此,解决方案是使用C ++重新编码接收器,并将我的Python编译为共享动态库。问题在于来自Cython的名称混淆。我现在正在考虑仅使用Python / C API,但这不像拥有机器代码本身那样有效。 - Andrew

1
我遇到了类似的问题。虽然不完全相同,但可能有关联。
我在这里发布了我的问题:通过dlsym cython传播异常。对你来说有趣的部分是“public”关键字。
#hello.pyx
cdef public say_hello():
    print "Hello World!"

这将创建一个类似于这样的函数

# (in the generated C file hello.c)
__PYX_EXTERN_C DL_IMPORT(...) say_hello(...);

编辑:我已经添加了一个可工作的temp.cpp文件:

#include "Python.h"
#include <iostream>

#include "hello.h"

using namespace std;

int main(){
    Py_Initialize();
    inithello();
    say_hello();
    Py_Finalize();
    return 1;
};

编译使用以下命令:

g++ -I/usr/include/python2.6/ -lpython2.6 -L./ -lhello temp.cpp -c -o temp.o
g++ temp.o -L. -lhello -lpython2.6 -o temp

有趣的是,它在一步链接时会抱怨未定义的引用。

执行后将成功打印“Hello world”。

注意:Py_Initialize()和inithello()是必需的,否则您的代码将崩溃。我无法在不包括“Python.h”和初始化部分(即仅使用extern "C" { void sayhello(); },如您所述)的情况下使其正常工作。它在链接时失败。解决方案可以是使用dlsym并动态加载函数,就像我在我的问题中演示的那样。但可能存在另一种解决方案,您可以尝试成功导出此方法(在hello.h头文件中):

__PYX_EXTERN_C DL_IMPORT(int) say_hello(void);


0
如果您有CMake,我建议您看一下我的项目,其中我使用CMake来生成和链接基于Cython的文件。

https://github.com/CarloNicolini/cymake

你可能需要编辑一些CMakeLists.txt文件来找到正确的cython安装


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接