How can I get a global C / C ++ variable that is shared with Python C / C ++ extensions?

I wrote the Python package almost completely in C ++. The reason for this is because I want to manually wrap the existing C ++ library, but that doesn't matter here.

This Python package consists of several different extension modules, all of which I compile with distutils in the setup.py script file. These extension modules can be interconnected, in which case I will link them by passing the shared library to the extension constructor. To be clear, suppose I have two Python C ++ modules, A and B, where B uses the functions defined in A. They are usually compiled in A.so and B.so. Since B uses the functions defined in A, I compile module A as usual, then I pass ': A.so' as a library to the library keyword in the extension constructor for module B. (':' Allows g ++ to handle what the library does not start with the usual lib prefix.) This is great for binding functions and classes.

My problem is this: I defined some C ++ global variables in A. Performing what I described allows B to access functions in A, it actually creates a COPY of any global data defined in A. This is a real problem for me .

It seems to me that the problem is essentially similar to global variables in shared libraries, as discussed here and elsewhere. This solution and others that I found on the Internet do not seem to solve the problem.

Any help would be greatly appreciated!

Edit: Forgot to mention that yes, my global variables are declared extern.

+4
source share
2 answers

I think the Putin way is to allow various extensions to interact through the C API. Although I am not very good at C ++, I assume that this is not so different from the solution in C. I would do it like this:

  • define a global variable in module A
  • define a C API for module A, which contains a pointer to a global variable (and a macro for convenience, if you wish).
  • load API C into module B and access the global variable with a pointer.

Writing the C API for python C extensions is a bit related. (Read the python docs on the Python C API if you are not familiar with it.) A minimal example of my proposed solution would look like this:

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 "Ah" 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 "Ah" 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 can read and modify x from both modules, starting at the C or python level. In python, it will look like this:

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

for access from level C use the macros PyA_GET_X() and PyA_SET_X(x) .

+1
source

If you can call C ++ functions from python code, just create a function that will access the global variable.

0
source

Source: https://habr.com/ru/post/1485219/


All Articles