Cython代码可以编译为dll,以便C ++应用程序可以调用它吗? [英] Can Cython code be compiled to a dll so C++ application can call it?

查看:122
本文介绍了Cython代码可以编译为dll,以便C ++应用程序可以调用它吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个C ++程序,它具有某种插件结构:当程序启动时,它将在插件文件夹中寻找具有某些导出功能签名的dll,例如:

I have a C++ program and it has sort of plugin structure: when program starts up, it's looking for dll in the plugin folder with certain exported function signatures, such as:

void InitPlugin(FuncTable* funcTable);

然后,程序将调用dll中的函数进行初始化,并将函数指针传递给dll。从那时起,该dll可以与该程序进行通讯了。

Then the program will call the function in the dll to initialize and pass function pointers to the dll. From that time on, the dll can talk to the program.

我知道Cython可以让您在Python中调用C函数,但是我不确定是否可以编写Cython代码并将其编译为dll,以便我的C ++程序可以对其进行初始化。

I know Cython let you call C function in Python, but I'm not sure can I write a Cython code and compile it to a dll so my C++ program can initialize with it. An example code would be great.

推荐答案

在dll中使用cython-module与在嵌入式python解释器中使用cython模块

Using cython-module in a dll is not unlike using a cython-module in an embeded python interpreter.

第一步是将 cdef 函数,应在具有 public 的外部C代码中使用,例如:

The first step would be to mark cdef-function which should be used from external C-code with public, for example:

#cyfun.pyx:

#doesn't need python interpreter
cdef public int double_me(int me):
    return 2*me;

#needs initialized python interpreter
cdef public void print_me(int me):
    print("I'm", me);

cyfun.c cyfun.h 可以使用

cython -3 cyfun.pyx

这些文件将用于构建dll。

These files will be used for building of the dll.

dll将需要一个函数来初始化python解释器,而另一个函数将其完成,该函数应在 double_me print_me (好, double_me 也可以在没有解释器的情况下使用,但这是一个实现细节)。

The dll will need one function to initialize the python interpreter and another to finalize it, which should be called only once before double_me and print_me can be used (Ok, double_me would work also without interpreter, but this is an implementation detail).

dll的头文件如下所示:

The header-file for the dll would look like following:

//cyfun_dll.h
#ifdef BUILDING_DLL
    #define DLL_PUBLIC __declspec(dllexport) 
#else
    #define DLL_PUBLIC __declspec(dllimport) 
#endif

//return 0 if everything ok
DLL_PUBLIC int cyfun_init();
DLL_PUBLIC void cyfun_finalize();

DLL_PUBLIC int cyfun_double_me(int me);
DLL_PUBLIC void cyfun_print_me(int me);

因此有必要的init / finalize函数,并且符号通过 DLL_PUBLIC (需要完成此操作,请参见此 SO-post ),以便可以使用它

So there are the necessary init/finalize-functions and the symbols are exported via DLL_PUBLIC (which needs to be done see this SO-post) so it can be used outside of the dll.

实现遵循 cyfun_dll.c -file:

The implementation follows in cyfun_dll.c-file:

//cyfun_dll.c
#define BUILDING_DLL
#include "cyfun_dll.h"

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "cyfun.h"

DLL_PUBLIC int cyfun_init(){
  int status=PyImport_AppendInittab("cyfun", PyInit_cyfun);
  if(status==-1){
    return -1;//error
  } 
  Py_Initialize();
  PyObject *module = PyImport_ImportModule("cyfun");

  if(module==NULL){
     Py_Finalize();
     return -1;//error
  }
  return 0;   
}


DLL_PUBLIC void cyfun_finalize(){
   Py_Finalize();
}

DLL_PUBLIC int cyfun_double_me(int me){
    return double_me(me);
}

DLL_PUBLIC void cyfun_print_me(int me){
    print_me(me);
}

值得注意的细节:


  1. 我们定义 BUILDING_DLL ,因此 DLL_PUBLIC 变为 __ declspec(dllexport )

  2. 我们使用cython从 cyfun中生成的 cyfun.h 。 pyx

  3. cyfun_init 初始化python解释器并导入内置模块 cyfun 。有点复杂的代码是因为默认为Cython-0.29 PEP-489。可以在此SO-post 中找到更多信息。

  1. we define BUILDING_DLL so DLL_PUBLIC becomes __declspec(dllexport).
  2. we use cyfun.h generated by cython from cyfun.pyx.
  3. cyfun_init inizializes python interpreter and imports the built-in module cyfun. The somewhat complicated code is because since Cython-0.29 PEP-489 is default. More information can be found in this SO-post.

  1. cyfun_double_me 仅包装 double_me ,因此它在dll外部变得可见。

  1. cyfun_double_me just wraps double_me so it becomes visible outside of the dll.


现在我们可以

:: set up tool chain
call "<path_to_vcvarsall>\vcvarsall.bat" x64

:: build cyfun.c generated by cython
cl  /Tccyfun.c /Focyfun.obj /c <other_coptions> -I<path_to_python_include> 

:: build dll-wrapper
cl  /Tccyfun_dll.c /Focyfun_dll.obj /c <other_coptions> -I<path_to_python_include>

:: link both obj-files into a dll
link  cyfun.obj cyfun_dll.obj /OUT:cyfun.dll /IMPLIB:cyfun.lib /DLL <other_loptions> -L<path_to_python_dll>

该dll现在已构建,但是以下细节值得注意:

The dll is now built, but the following details are noteworthy:


  1. < other_coptions> < other_loptions>可能因安装而异。一种简单的方法是查看它们是运行 cythonize some_file.pyx`并检查日志。

  2. 我们不需要传递python-dll,因为它将自动链接,但是我们需要设置正确的库路径。

  3. 我们对python-dll有依赖性,因此以后必须在可以找到它的地方。

  1. <other_coptions> and <other_loptions> can vary from installation to installation. An easy way is to see them is to runcythonize some_file.pyx` and to inspect the log.
  2. we don't need to pass python-dll, because it will be linked automatically, but we need to set the right library-path.
  3. we have the dependency on the python-dll, so later on it must be somewhere where it can be found.






根据您的任务从这里出发,我们用一个简单的main测试我们的dll:


Were you go from here depends on your task, we test our dll with a simple main:

//test.c
#include "cyfun_dll.h"

int main(){
   if(0!=cyfun_init()){
      return -1;
   }
   cyfun_print_me(cyfun_double_me(2));
   cyfun_finalize();
   return 0;
}

可以通过

...
:: build main-program
cl  /Tctest.c /Focytest.obj /c <other_coptions> -I<path_to_python_include>

:: link the exe
link test.obj cyfun.lib /OUT:test_prog.exe <other_loptions> -L<path_to_python_dll>

现在调用 test_prog.exe 会导致预期的输出为 I'm 4。

And now calling test_prog.exe leads to the expected output "I'm 4".

根据您的安装,必须考虑以下事项:

Depending on your installation, following things must be considered:


  • test_prog.exe 取决于 pythonX.Y.dll ,该文件应位于路径,以便可以找到它(最简单的方法是将其复制到exe旁边)

  • 嵌入的python解释器需要安装,请参见和/或 SO-帖子。

  • test_prog.exe depends on pythonX.Y.dll which should be somewhere in the path so it can be found (the easiest way is to copy it next to the exe)
  • The embeded python interpreter needs an installation, see this and/or this SO-posts.

IIRC,初始化,然后完成并初始化Python并不是一个好主意再次解释器(这可能在某些情况下适用,但不是全部,请参见例如)-解释器应初始化只能存活一次,直到程序结束。

IIRC, it is not a great idea to initialize, then to finalize and then to initialize the Python-interpreter again (that might work for some scenarios, but not all , see for example this) - the interpreter should be initialized only once and stay alive until the programs ends.

因此,如果您的C / C ++程序已经具有初始化的Python解释器,那么提供仅导入模块 cyfun 的函数将很有意义。 c $ c>并且不初始化。在这种情况下,我将定义 CYTHON_PEP489_MULTI_PHASE_INIT = 0 ,因为 PyImport_AppendInittab 必须在 Py_Initialize ,在加载dll时可能已经为时已晚。

So if your C/C++-program already has an initialized Python-interpreter it would make sense to offer a function which only imports the module cyfun and doesn't initialize. In this case I would define CYTHON_PEP489_MULTI_PHASE_INIT=0, because PyImport_AppendInittab must be called before Py_Initialize, which might be already too late when the dll is loaded.

这篇关于Cython代码可以编译为dll,以便C ++应用程序可以调用它吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆