为了账号安全,请及时绑定邮箱和手机立即绑定

如何拥有在Python C / C ++扩展之间共享的全局C / C ++变量?

如何拥有在Python C / C ++扩展之间共享的全局C / C ++变量?

慕村225694 2021-03-11 14:12:27
我几乎完全用C ++编写了一个Python包。这样做的原因是因为我想手动包装现有的C ++库,但这与此处无关。这个Python软件包包含许多不同的扩展模块,我用distutils在“ setup.py”脚本中对所有这些扩展模块进行了编译。这些扩展模块可以相互关联,在这种情况下,我通过将共享库传递给Extension构造函数来链接它们。为了清楚起见,假设我有两个Python C ++模块A和B,其中B使用A中定义的函数。这些函数通常编译为A.so和B.so。由于B使用A中定义的函数,因此我照常编译A模块,然后将':A.so'作为库传递给B模块的Extension构造函数中的librarys关键字。(“:”使g ++处理该库不是以通常的“ lib”前缀开头的事实。)这对于链接函数和类是很好的。我的问题如下:我在A中定义了一些全局C ++变量。虽然我所做的描述允许B访问A中的函数,但实际上似乎为A中定义的任何全局数据创建了COPY。这是一个实际的问题。为了我。在我看来,此问题本质上类似于在共享库中使用全局变量,如此处和其他地方所讨论。该解决方案以及我在网上找到的其他解决方案似乎都无法解决问题。
查看完整描述

3 回答

?
红糖糍粑

TA贡献1815条经验 获得超6个赞

我猜想pythonic的方法是让不同的扩展通过C API进行交互。尽管我对c ++不太了解,但我想它与C中的解决方案并没有太大区别。我可以通过以下方式做到这一点:

  • 在模块A中定义全局变量

  • 为模块A定义一个C API,其中包含指向全局变量的指针(如果需要,还可以包含一个宏,以方便使用)。

  • 在模块B中加载C API并通过指针访问全局变量。

编写用于python C扩展的C API涉及到一些工作。(如果您不熟悉Python C API,请阅读python文档。)我建议的解决方案的最小示例如下所示:

A.h

/* Header file for A module */


#ifndef A_MODULE_H

#define A_MODULE_H

#ifdef __cplusplus

extern "C" {

#endif


#define PyA_GET_X() (*x_ptr)

#define PyA_SET_X(x) (*x_ptr = x)


#ifdef A_MODULE

/* do nothing for this minimal example */

#else

static void **PyA_API;

#define x_ptr ((long *)PyA_API[0])


static int import_A(void)

{

    PyA_API = (void **)PyCapsule_Import("A._C_API", 0);

    return (PyA_API != NULL) ? 0 : -1;

}

#endif /* !defined(A_MODULE) */

#ifdef __cplusplus

}

#endif

#endif /* !defined(A_MODULE_H) */

A.c


#include <Python.h>

#define A_MODULE

#include "A.h"


long x = 0; /* here is the global variable */


static PyObject* 

set_x(PyObject *self, PyObject *args){

    if (!PyArg_ParseTuple(args, "l", &x)) return NULL;

    Py_RETURN_NONE;

}


static PyObject* 

get_x(PyObject *self, PyObject *args){

    return PyInt_FromLong(x);

}


static PyMethodDef methods[] = {

    {"set_x", (PyCFunction)set_x, METH_VARARGS, ""},

    {"get_x", (PyCFunction)get_x, METH_NOARGS, ""},

    {NULL, NULL, 0, NULL}

    };


#ifndef PyMODINIT_FUNC  /* declarations for DLL import/export */

#define PyMODINIT_FUNC void

#endif

PyMODINIT_FUNC initA(void){

    PyObject *m = Py_InitModule3("A", methods, "");

    static void *PyA_API[1];

    PyA_API[0] = (void *)&x;


    PyObject *c_api_object = PyCapsule_New((void *)PyA_API, "A._C_API", NULL);

    if (c_api_object != NULL) PyModule_AddObject(m, "_C_API", c_api_object);

}

B.c


#include <Python.h>

#define B_MODULE

#include "A.h"


static PyObject* 

set_x(PyObject *self, PyObject *args){

    long y;

    if (!PyArg_ParseTuple(args, "l", &y)) return NULL;

    PyA_SET_X(y);

    Py_RETURN_NONE;

}


static PyObject* 

get_x(PyObject *self, PyObject *args){

    return PyInt_FromLong(PyA_GET_X());

}


static PyMethodDef methods[] = {

    {"set_x", (PyCFunction)set_x, METH_VARARGS, ""},

    {"get_x", (PyCFunction)get_x, METH_NOARGS, ""},

    {NULL, NULL, 0, NULL}

    };


#ifndef PyMODINIT_FUNC  /* declarations for DLL import/export */

#define PyMODINIT_FUNC void

#endif

PyMODINIT_FUNC initB(void){

    import_A();

    Py_InitModule3("B", methods, "");


}

setup.py


from numpy.distutils.core import setup, Extension


setup(

    name="AB",

    ext_modules = [Extension('A', ['A.c']), Extension('B', ['B.c'])],

    )

最后,您将能够x从两个模块(C级或python)读取和修改。在python中,它看起来像:


>>> import A, B

>>> A.set_x(1)

>>> B.get_x()

    1

>>> B.set_x(2)

>>> A.get_x()

    2

要从C级访问,请使用宏PyA_GET_X()和PyA_SET_X(x)。


查看完整回答
反对 回复 2021-03-31
?
呼啦一阵风

TA贡献1802条经验 获得超6个赞

如果可以从python代码调用c ++函数,则只需创建一个将访问全局变量的函数即可。


查看完整回答
反对 回复 2021-03-31
  • 3 回答
  • 0 关注
  • 235 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信