Вызов метода класса Python из C ++, если задан инициализированный класс как PyObject - PullRequest
0 голосов
/ 22 марта 2019

У меня есть функция в c ++, которая получает инициализированный класс как PyObject. Класс Python:

class Expression:
    def __init__(self, obj):
        self.obj = obj

    def get_source(self):
        #Check if the object whose source is being obtained is a function.
        if inspect.isfunction(self.obj):
            source = inspect.getsourcelines(self.obj)[0][1:]
            ls = len(source[0]) - len(source[0].lstrip())
            source = [line[ls:] for line in source]
            #get rid of comments from the source
            source = [item for item in source if item.lstrip()[0] != '#']
            source = ''.join(source)
            return source
        else:
            raise Exception("Expression object is not a function.")

C ++ получает это:

Expression(somefunctogetsource)

Из c ++, как мне вызвать метод get_source объекта выражения? До сих пор я читал документы на Python C-API и пробовал что-то вроде этого:

PyObject* baseClass = (PyObject*)expression->ob_type;
PyObject* func = PyObject_GetAttrString(baseClass, "get_source");
PyObject* result = PyObject_CallFunctionObjArgs(func, expression, NULL);

И преобразовать результат в строку, но это не сработает.

1 Ответ

1 голос
/ 22 марта 2019

Проще, чем вы делаете это. Вам не нужно ничего извлекать из базового класса напрямую. Просто сделай:

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 в интерпретаторе, никакого фактического сравнения содержимого памяти не происходит; он понимает, что обе строки являются интернированными, и просто проверяет, указывают ли они на та же память или нет).

...