Проще, чем вы делаете это. Вам не нужно ничего извлекать из базового класса напрямую. Просто сделай:
PyObject* result = PyObject_CallMethod(expression, "get_source", NULL);
if (result == NULL) {
// Exception occurred, return your own failure status here
}
// result is a PyObject* (in this case, it should be a PyUnicode_Object)
PyObject_CallMethod
принимает объект для вызова метода, строку в стиле C для имени метода и строку формата + varargs для аргументов. Если аргументы не нужны, строка формата может быть NULL
.
Полученный PyObject*
не очень полезен для кода C ++ (он имеет время выполнения, определяемое 1, 2 или 4 байтовыми символами, в зависимости от используемых порядковых номеров, поэтому прямое копирование памяти из него в std::string
или std::wstring
выиграно не работает), , но PyUnicode_AsUTF8AndSize
можно использовать для получения версии в кодировке UTF-8 и длины , которую можно использовать для эффективного построения std::string
с эквивалентными данными.
Если производительность имеет значение, вы можете явно указать PyObject*
, представляющий "get_source"
во время загрузки модуля, например, с глобальным, как:
PyObject *get_source_name;
, который инициализируется в PyMODINIT_FUNC
модуля с:
get_source_name = PyUnicode_InternFromString("get_source");
Как только вы это сделаете, вы можете использовать более эффективный PyObject_CallMethodObjArgs
с:
PyObject* result = PyObject_CallMethodObjArgs(expression, get_source_name, NULL);
Экономия в значительной степени заключается в том, что вы избегаете создания уровня Python str
из C char*
снова и снова, а с помощью PyUnicode_InternFromString
для создания строки вы используете интернированную строку, что делает поиск более эффективный (так как имя get_source
само автоматически интернируется, когда def
-ed в интерпретаторе, никакого фактического сравнения содержимого памяти не происходит; он понимает, что обе строки являются интернированными, и просто проверяет, указывают ли они на та же память или нет).