Я написал пакет Python почти полностью на C ++. Причина в том, что я хочу вручную обернуть существующую библиотеку C ++, но здесь это не имеет значения.
Этот пакет Python состоит из нескольких различных модулей расширения, каждый из которых я компилирую с помощью distutils в сценарии «setup.py». Эти модули расширения могут быть взаимосвязаны, и в этом случае я связываю их, передавая общую библиотеку конструктору Расширения. Для ясности предположим, что у меня есть два модуля Python C ++, A и B, где B использует функции, определенные в A. Они обычно компилируются в A.so и B.so. Поскольку B использует функции, определенные в A, я компилирую модуль A как обычно, а затем передаю ‘: A.so’ в качестве библиотеки ключевому слову library в конструкторе Extension для модуля B. (‘:’ Позволяет g ++ иметь дело с тем фактом, что библиотека не запускается с обычным префиксом ‘lib’.) Это прекрасно работает для связывания функций и классов.
Моя проблема заключается в следующем: я определил некоторые глобальные переменные C ++ в A. Хотя выполнение того, что я описал, позволяет B получать доступ к функциям в A, на самом деле создается впечатление, что он создает КОПИЮ любых глобальных данных, определенных в A. Это реальная проблема для меня.
Мне кажется, что проблема по существу аналогична наличию глобальных переменных в общих библиотеках, как обсуждалось Вот и в других местах. Это решение и другие, которые я нашел в Интернете, похоже, не решают проблему.
Любая помощь будет очень ценится!
Изменить: Забыл упомянуть, что, да, мои глобальные переменные объявлены как внешние.
Я предполагаю, что питонский способ состоит в том, чтобы позволить различным расширениям взаимодействовать через C API. Хотя я не очень разбираюсь в c ++, я думаю, что он не так сильно отличается от решения в C. Я бы сделал это следующим образом:
Написание C API для расширений Python C немного сложное. (Прочтите документы по Python на API Python C, если вы не знакомы с ним.)
Минимальный пример предложенного мной решения будет выглядеть так:
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)
,
Если вы можете вызывать из кода Python функции с ++, просто создайте функцию, которая будет обращаться к глобальной переменной.