Передача указателя C с помощью API Python / C - PullRequest
11 голосов
/ 08 декабря 2011

Я новичок в Python / C API ... Я пытаюсь добавить новую функциональность в мою C-программу, в которой я могу встроить Python в нее и одновременно расширить функциональность, чтобы встроенный интерпретатор мог выполнить скрипт, которыйбудет взаимодействовать с расширяющимся модулем Python, написанным как часть моей C-программы.Моя C программа не имеет глобальных переменных.Я хотел бы сохранить вещи таким образом;В то же время для того, чтобы представить функциональность C в python, кажется, что расширяющейся функции C по крайней мере необходим доступ к глобальным переменным для доступа к состоянию программы.Как мне обойти это?

Например, вот как я планирую встраивать, когда PYINTERFACE_Initialize вызывается из main

void PYINTERFACE_Initialize(State *ptr, FILE *scriptFile, const char* scriptFileName)
{
    Py_Initialize();
    PyObject *module = Py_InitModule("CInterface", CInterfaceMethods);
    if (PyRun_SimpleFileEx(scriptFile, scriptFileName, 1) != 0)
    {
        printf("PYINTERFACE script execution failed!\n");
    }
    **//ADD State *ptr TO module**      
}

Вот расширенная функция:

static PyObject*
CInterface_GetStateInfo(PyObject *self, PyObject *args)
{
    const char *printStr;
    double stateInfo;
    State *ptr;

    if(!PyArg_ParseTuple(args, "s", &printStr))
    {
        return NULL;
    }
    printf("Received %s\n", printStr);

    **//RETRIEVE State *ptr FROM self**      

    stateInfo = ptr->info;
    return Py_BuildValue("d", currentTime);
}

Это самый чистый способ передать государство * ptr?Я, конечно, не вижу необходимости выставлять внутреннее состояние Python.Я думал об использовании капсул, но капсулы не поддерживают такого рода поведение.

Заранее спасибо!В

Ответы [ 2 ]

11 голосов
/ 27 января 2014

Капсулы - это в основном непонятные для Python указатели на пустоты, которые вы можете передавать или связывать с модулями.Они являются «способом» решения вашей проблемы.

Вот пример, в котором используется экземпляр x, который не обязательно должен быть статическим.Сначала прикрепите указатель к вашему модулю примерно так (проверка ошибок удалена) ...

// wrap the methods to be exposed to python in a module
// i.e. this is a list of method descriptions for the module
static PyMethodDef InitializeTurkeyMethods[] = {

    // this block describes one method.. turkey.do_something()
    {"do_something", 
     turkey_do_something, // fn pointer to wrap (defined below)
     METH_VARARGS, 
     "do something .. return an int."},

    {NULL, NULL, 0, NULL} // sentinel.
};


int init(X * x) { 

    // initialize embedded python scripting .. 
    // (this method a no-op on second or later calls).
    Py_Initialize();

    // initialize the turkey python module 
    PyObject * module = Py_InitModule("turkey", InitializeTurkeyMethods);

    // Create a capsule containing the x pointer 
    PyObject * c_api_object = PyCapsule_New((void *)x, "turkey._X_C_API", NULL);

    // and add it to the module
    PyModule_AddObject(module, "_X_C_API", c_api_object);
}

Затем в функции, которую вы хотите выставить в python, чтобы вернуть этот указатель X, вы делаете что-то вроде этого(это на самом деле должно пройти, прежде чем вы начнете ссылаться на него в приведенном выше коде):

static PyObject* turkey_do_something(PyObject *self, PyObject *args) {    

    if(!PyArg_ParseTuple(args, ":turkey_do_something"))
        return NULL;

    // get the x pointer back from the capsule
    X * x = (X*)PyCapsule_Import("turkey._X_C_API", 0);    

    // call some fn on x 
    return Py_BuildValue("i", x->some_fn_that_returns_an_int());
}

Здесь "turkey._X_C_API" - это просто имя для некоторой дополнительной проверки типов - введите здесь какое-то значимое имя дляваше приложение.Turkey - это название демо-модуля, которое я придумал.

Теперь, предположив, что, и в зависимости от того, как вы экспортировали turkey_do_something fn при вызове Py_InitModule (), вы можете вызвать это так из скрипта python:

import turkey

print turkey.do_something()

Проверьте это:http://docs.python.org/2/c-api/arg.html как отформатировать кортежи и это .. http://docs.python.org/3.1/c-api/capsule.html для документа на капсулах

5 голосов
/ 07 января 2012

Предполагая, что вам нужна обратная совместимость, вы хотите использовать PyCObjects: http://docs.python.org/c-api/cobject.html

Если вы хотите использовать только Python 3, вы можете использовать PyCapsule: http://docs.python.org/c-api/capsule.html

По сути, PyCObject преобразует ваш непрозрачный указатель в PyObject, который вы можете передать внутри python, и когда вы вернетесь в C с одним, вы можете развернуть его и использовать его.

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

В вашем конкретном случае, когда вы добавляете указатель, вы просто захотите сделать это(проверка ошибок и удаление кода удалены):

PyObject *pystate = PyCObject_FromVoidPtr(State, NULL);
PyObject *dict = PyModule_GetDict(module);
PyDict_SetItemString(dict, "CStateObject", pystate);

# To retrieve it from self (assuming that it is an object)
PyObject *pystate = PyObject_GetAttrString(self, "CStateObject");
State *state = (State *)PyCObject_AsVoidPtr(pystate);
...