簡體   English   中英

如何在Python C / C ++擴展之間共享一個全局C / C ++變量?

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

我幾乎完全用C ++編寫了一個Python包。 這樣做的原因是因為我想手動包裝現有的C ++庫,但這與此無關。

這個Python包由許多不同的擴展模塊組成,所有這些模塊都是在'setup.py'腳本中使用distutils編譯的。 這些擴展模塊可以是相互關聯的,在這種情況下,我通過將共享庫傳遞給Extension構造函數來鏈接它們。 要清楚,假設我有兩個Python C ++模塊,A和B,其中B使用A中定義的函數。這些通常編譯成A.so和B.so. 由於B使用A中定義的函數,因此我像往常一樣編譯A模塊,然后將':A.so'作為庫傳遞給B模塊的Extension構造函數中的libraries關鍵字。 (':'讓g ++處理這個事實,即庫不以通常的'lib'前綴開頭。)這適用於鏈接函數和類。

我的問題如下:我在A中定義了一些全局C ++變量。雖然我所描述的內容允許B訪問A中的函數,但它實際上似乎創建了A中定義的任何全局數據的COPY。這是一個真正的問題為了我。

在我看來,該問題基本上類似於在共享庫中具有全局變量,如此和其他地方所討論的。 該解決方案以及我在網上找到的其他解決方案似乎無法解決問題。

任何幫助將非常感謝!

編輯:忘記提及,是的,我的全局變量被聲明為extern。

我想pythonic的方法是讓不同的擴展通過C API進行交互。 雖然我對c ++不是很有經驗,但我猜它與C中的解決方案沒那么不同。我會按照以下方式進行:

  • 在模塊A中定義全局變量
  • 為模塊A定義一個C API,它包含一個指向全局變量的指針(如果你願意,還可以使用宏)。
  • 在模塊B中加載C API並通過指針訪問全局變量。

編寫用於python C擴展的C API有點涉及。 (閱讀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'])],
    )

最后,您將能夠從C級或python中讀取和修改兩個模塊中的x 在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)

如果你可以從python代碼調用c ++函數,只需創建一個訪問全局變量的函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM