Должен ли я использовать Py_INCREF для любого из моих объектов PyObject в этом блоке? Также правильно ли я Py_DECREFing мои объекты? - PullRequest
0 голосов
/ 27 апреля 2018

Всякий раз, когда я вызываю эту функцию, использование памяти увеличивается за один вызов, поэтому я думаю, что здесь есть некоторая утечка памяти.

PyObject *pScript, *pModule, *pFunc, *pValue;
PyObject *pArgs = NULL;
long ret = 1;

// Initialize python, set system path and load the module
pScript = SetPyObjectString(PYTHON_SCRIPT_NAME);
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('"PYTHON_SCRIPT_PATH"')");
pModule = PyImport_Import(pScript);
Py_XDECREF(pScript);

if (pModule != NULL) {
    // Get function object from python module
    pFunc = PyObject_GetAttrString(pModule, operation.c_str());

    if (pFunc && PyCallable_Check(pFunc)) {
        // Create argument(s) as Python tuples
        if (operation == UPDATE_KEY) {
            // If operation is Update key, create two arguments - key and value
            pArgs = PyTuple_New(2);
        }
        else {
            pArgs = PyTuple_New(1);
        }

        pValue = SetPyObjectString(key.c_str());
        // Set argument(s) with key/value strings
        PyTuple_SetItem(pArgs, 0, pValue);
        if (operation == UPDATE_KEY) {
            // If operation is Update key, set two arguments - key and value
            pValue = SetPyObjectString(value.c_str());
            PyTuple_SetItem(pArgs, 1, pValue);
        }

        // Call the function using function object and arguments
        pValue = PyObject_CallObject(pFunc, pArgs);
        Py_XDECREF(pArgs);

        if (pValue != NULL) {
            // Parse the return values
            ret = PyLong_AsLong(PyList_GetItem(pValue, 0));
            value = GetPyObjectString(PyList_GetItem(pValue, 1));
        }
        else {
            ERROR("Function call to %s failed", operation.c_str());
        }

        Py_XDECREF(pValue);
        Py_XDECREF(pFunc);
    }
    else {
        ERROR("Cannot find function in python module");
    }
    Py_XDECREF(pModule);
}
else {
    ERROR("Failed to load python module");
}

У меня возникает утечка памяти, когда этот фрагмент кода C ++ в моем коде вызывает скрипт Python, и я хочу знать, почему. Я думаю, что я делаю что-то не так с моими Py_DECREFs. Любая помощь будет высоко ценится.

1 Ответ

0 голосов
/ 27 апреля 2018

Я заметил один недостающий дефиф с первого взгляда:


pFunc = PyObject_GetAttrString(pModule, operation.c_str());

if (pFunc && PyCallable_Check(pFunc)) {
    // ...
    Py_XDECREF(pFunc);
}

Это приведет к утечке любого не вызываемого атрибута, соответствующего operation.


Два переназначения pValue ... Я думаю, это нормально, потому что PyTuple_SetItem украдет ссылку на каждое из исходных значений.


Для этой строки, о которой вы спрашивали:

value = GetPyObjectString(PyList_GetItem(pValue, 1));

PyList_GetItem возвращает заимствованную ссылку, поэтому тот факт, что вы не расшифровали ее, является правильным.

Но я нигде не вижу декларации для value или GetPyObjectString, поэтому я понятия не имею, что делает эта часть. Может быть, это просто получение заимствованного буфера из PyUnicodeObject * и копирование его в какой-либо строковый тип C ++ wstring или UTF-32, или, возможно, утечка объекта Python или скопированного буфера, или возврат необработанного буфера C, который вы просто утечка здесь или ... кто знает?


Но я бы, конечно, не поверил, что какой-то парень в Интернете нашел их всех на быстром сканировании. Научитесь пользоваться отладчиком памяти.

Или: вы используете C ++. RAII - это почти весь смысл использования C ++ - другими словами, вместо использования необработанных значений PyObject *, вы можете использовать умный указатель, который автоматически расшифровывает данные. Или, что еще лучше, используйте готовую библиотеку, например PyCXX .

...