简体   繁体   English

如何在Python C / C ++扩展之间共享一个全局C / C ++变量?

[英]How can I have a global C/C++ variable that is shared between Python C/C++ extensions?

I have written a Python package almost completely in C++. 我几乎完全用C ++编写了一个Python包。 The reason for doing so is because I want to manually wrap an existing C++ library, but that is irrelevant here. 这样做的原因是因为我想手动包装现有的C ++库,但这与此无关。

This Python package consists of a number of different extension modules, all of which I compile with distutils in a 'setup.py' script. 这个Python包由许多不同的扩展模块组成,所有这些模块都是在'setup.py'脚本中使用distutils编译的。 These extension modules can be interrelated, in which case I link them by passing the shared library to the Extension constructor. 这些扩展模块可以是相互关联的,在这种情况下,我通过将共享库传递给Extension构造函数来链接它们。 To be clear, suppose I have two Python C++ modules, A and B, where B uses functions defined in A. These normally compile into A.so and B.so. 要清楚,假设我有两个Python C ++模块,A和B,其中B使用A中定义的函数。这些通常编译成A.so和B.so. Since B uses functions defined in A, I compile the A module as usual then I pass ':A.so' as a library to the libraries keyword in the Extension constructor for the B module. 由于B使用A中定义的函数,因此我像往常一样编译A模块,然后将':A.so'作为库传递给B模块的Extension构造函数中的libraries关键字。 (The ':' lets g++ deal with the fact that the library does not start with the usual 'lib' prefix.) This works fine for linking functions and classes. (':'让g ++处理这个事实,即库不以通常的'lib'前缀开头。)这适用于链接函数和类。

My problem is as follows: I have defined some global C++ variables in A. While doing what I have described allows B to access functions in A, it actually seems to create a COPY of any global data defined in A. This is a real problem for me. 我的问题如下:我在A中定义了一些全局C ++变量。虽然我所描述的内容允许B访问A中的函数,但它实际上似乎创建了A中定义的任何全局数据的COPY。这是一个真正的问题为了我。

It seems to me that the issue is essentially similar to having global variables across shared libraries, as discussed here and elsewhere. 在我看来,该问题基本上类似于在共享库中具有全局变量,如此和其他地方所讨论的。 That solution, and others I have found online, do not seem to solve the problem. 该解决方案以及我在网上找到的其他解决方案似乎无法解决问题。

Any help would be very appreciated! 任何帮助将非常感谢!

Edit: Forgot to mention that, yes, my global variables are declared as extern. 编辑:忘记提及,是的,我的全局变量被声明为extern。

I guess the pythonic way is to let different extensions interact via a C API. 我想pythonic的方法是让不同的扩展通过C API进行交互。 Although I am not very experienced with c++ I guess it is not so much different from the solution in C. I would do it the following way: 虽然我对c ++不是很有经验,但我猜它与C中的解决方案没那么不同。我会按照以下方式进行:

  • define the global variable in module A 在模块A中定义全局变量
  • define a C API for module A, which contains a pointer to the global variable (and a macro for convenience if you wish). 为模块A定义一个C API,它包含一个指向全局变量的指针(如果你愿意,还可以使用宏)。
  • load the C API in module B and access the global variable via the pointer. 在模块B中加载C API并通过指针访问全局变量。

Writing C APIs for python C extensions is a bit involved. 编写用于python C扩展的C API有点涉及。 (Read python docs on Python C API, if your are not familiar with it.) The minimal example of my proposed solution would look like this: (阅读Python C API上的python文档,如果您不熟悉它。)我提出的解决方案的最小示例如下所示:

Ah

/* 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) */

Ac

#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);
}

Bc

#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'])],
    )

and finally you will be able to read and modify x from both modules, either from C level or python. 最后,您将能够从C级或python中读取和修改两个模块中的x In python this will look like: 在python中,这将看起来像:

>>> import A, B
>>> A.set_x(1)
>>> B.get_x()
    1
>>> B.set_x(2)
>>> A.get_x()
    2

for access from C level use macros PyA_GET_X() and PyA_SET_X(x) . 从C级访问使用宏PyA_GET_X()PyA_SET_X(x)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM