Как вы называете код Python из кода C? - PullRequest
37 голосов
/ 29 июня 2009

Я хочу расширить большой C-проект новыми функциональными возможностями, но я действительно хочу написать его на Python. По сути, я хочу вызвать Python-код из C-кода. Однако обертки Python-> C, такие как SWIG, допускают OPPOSITE, то есть написание модулей C и вызов C из Python.

Я рассматриваю подход с участием IPC или RPC (я не против иметь несколько процессов); то есть мой чистый компонент Python запускается в отдельном процессе (на той же машине), а мой C-проект связывается с ним посредством записи / чтения из сокета (или канала unix). Мой компонент Python может читать / писать в сокет для связи. Это разумный подход? Есть ли что-то лучше? Как какой-то особенный механизм RPC?

Спасибо за ответ до сих пор - однако я хотел бы сосредоточиться на подходах, основанных на IPC, так как я хочу, чтобы моя программа Python находилась в отдельном процессе в качестве моей программы на Си. Я не хочу вставлять интерпретатор Python. Спасибо!

Ответы [ 8 ]

11 голосов
/ 29 июня 2009

Я рекомендую подходы, подробно описанные здесь .Он начинается с объяснения того, как выполнять строки кода Python, а затем подробно рассказывает, как настроить среду Python для взаимодействия с вашей программой C, вызывать функции Python из вашего кода C, управлять объектами Python из вашего кода C и т. Д.

РЕДАКТИРОВАТЬ : Если вы действительно хотите идти по маршруту IPC, тогда вы захотите использовать модуль struct или еще лучше, protlib ,Большая часть связи между процессами Python и C вращается вокруг передачи структур туда и обратно, либо через сокет или через разделяемую память .

Я рекомендую создать Commandструктура с полями и кодами для представления команд и их аргументов.Я не могу дать более конкретный совет, не зная больше о том, чего вы хотите достичь, но в целом я рекомендую библиотеку protlib , поскольку именно она используется для связи между программами на C и Python (отказ от ответственности: Iавтор протлиба).

4 голосов
/ 29 июня 2009

Рассматривали ли вы просто оборачивать свое приложение на python в скрипт оболочки и вызывать его из с помощью приложения C?

Не самое элегантное решение, но оно очень простое.

4 голосов
/ 29 июня 2009

См. Соответствующую главу в руководстве: http://docs.python.org/extending/

По сути, вам придется встроить интерпретатор python в вашу программу.

1 голос
/ 29 июня 2009

Если бы я решил пойти с IPC, я бы, вероятно, разорился с XML-RPC - кроссплатформенным, позволяющим легко перенести проект сервера Python на другой узел позже, если вы хотите, имеет много превосходных реализаций (см. здесь для многих, включая C и Python, и здесь для простого XML-RPC-сервера, входящего в стандартную библиотеку Python - не так хорошо масштабируемого, как другие подходы, но, вероятно, хорошо и удобно для вашего случая использования.

Это может быть не идеальный подход IPC для всех случаев (или даже идеальный подход RPC, во что бы то ни стало!), Но удобство, гибкость, надежность и широкий диапазон реализаций перевешивают множество мелких дефектов, на мой взгляд мнение.

1 голос
/ 29 июня 2009

Я не использовал подход IPC для связи Python <-> C, но он должен работать довольно хорошо. Я хотел бы, чтобы программа на C выполняла стандартный fork-exec и использовала перенаправленные stdin и stdout в дочернем процессе для связи. Приятное текстовое общение позволит очень легко разрабатывать и тестировать программу Python.

0 голосов
/ 23 ноября 2015

Я использовал «стандартный» подход Встраивание Python в другое приложение . Но это сложно / утомительно. Каждая новая функция в Python является болезненной для реализации.

Я видел пример Вызов PyPy из C . Он использует CFFI для упрощения интерфейса, но требует PyPy, а не Python. Сначала прочтите и поймите этот пример, по крайней мере, на высоком уровне.

Я изменил пример C / PyPy для работы с Python. Вот как вызвать Python из C с использованием CFFI.

Мой пример более сложный, потому что я реализовал три функции в Python вместо одной. Я хотел охватить дополнительные аспекты передачи данных туда и обратно.

Сложная часть теперь изолирована для передачи адреса api в Python. Это должно быть реализовано только один раз. После этого легко добавить новые функции в Python.

interface.h

// These are the three functions that I implemented in Python.
// Any additional function would be added here.
struct API {
    double (*add_numbers)(double x, double y);
    char* (*dump_buffer)(char *buffer, int buffer_size);
    int (*release_object)(char *obj);
};

test_cffi.c

//
// Calling Python from C.
// Based on Calling PyPy from C:
// http://doc.pypy.org/en/latest/embedding.html#more-complete-example
//

#include <stdio.h>
#include <assert.h>

#include "Python.h"

#include "interface.h"

struct API api;   /* global var */

int main(int argc, char *argv[])
{
    int rc;

    // Start Python interpreter and initialize "api" in interface.py using 
    // old style "Embedding Python in Another Application":
    // https://docs.python.org/2/extending/embedding.html#embedding-python-in-another-application
    PyObject *pName, *pModule, *py_results;
    PyObject *fill_api;
#define PYVERIFY(exp) if ((exp) == 0) { fprintf(stderr, "%s[%d]: ", __FILE__, __LINE__); PyErr_Print(); exit(1); }

    Py_SetProgramName(argv[0]);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString(
            "import sys;"
            "sys.path.insert(0, '.')" );

    PYVERIFY( pName = PyString_FromString("interface") )
    PYVERIFY( pModule = PyImport_Import(pName) )
    Py_DECREF(pName);
    PYVERIFY( fill_api = PyObject_GetAttrString(pModule, "fill_api") )

    // "k" = [unsigned long],
    // see https://docs.python.org/2/c-api/arg.html#c.Py_BuildValue
    PYVERIFY( py_results = PyObject_CallFunction(fill_api, "k", &api) )
    assert(py_results == Py_None);

    // Call Python function from C using cffi.
    printf("sum: %f\n", api.add_numbers(12.3, 45.6));

    // More complex example.
    char buffer[20];
    char * result = api.dump_buffer(buffer, sizeof buffer);
    assert(result != 0);
    printf("buffer: %s\n", result);

    // Let Python perform garbage collection on result now.
    rc = api.release_object(result);
    assert(rc == 0);

    // Close Python interpreter.
    Py_Finalize();

    return 0;
}

interface.py

import cffi
import sys
import traceback

ffi = cffi.FFI()
ffi.cdef(file('interface.h').read())

# Hold references to objects to prevent garbage collection.
noGCDict = {}

# Add two numbers.
# This function was copied from the PyPy example.
@ffi.callback("double (double, double)")
def add_numbers(x, y):
    return x + y

# Convert input buffer to repr(buffer).
@ffi.callback("char *(char*, int)")
def dump_buffer(buffer, buffer_len):
    try:
        # First attempt to access data in buffer.
        # Using the ffi/lib objects:
        # http://cffi.readthedocs.org/en/latest/using.html#using-the-ffi-lib-objects
        # One char at time, Looks inefficient.
        #data = ''.join([buffer[i] for i in xrange(buffer_len)])

        # Second attempt.
        # FFI Interface:
        # http://cffi.readthedocs.org/en/latest/using.html#ffi-interface
        # Works but doc says "str() gives inconsistent results".
        #data = str( ffi.buffer(buffer, buffer_len) )

        # Convert C buffer to Python str.
        # Doc says [:] is recommended instead of str().
        data = ffi.buffer(buffer, buffer_len)[:]

        # The goal is to return repr(data)
        # but it has to be converted to a C buffer.
        result = ffi.new('char []', repr(data))

        # Save reference to data so it's not freed until released by C program.
        noGCDict[ffi.addressof(result)] = result

        return result
    except:
        print >>sys.stderr, traceback.format_exc()
        return ffi.NULL

# Release object so that Python can reclaim the memory.
@ffi.callback("int (char*)")
def release_object(ptr):
    try:
        del noGCDict[ptr]
        return 0
    except:
        print >>sys.stderr, traceback.format_exc()
        return 1

def fill_api(ptr):
    global api
    api = ffi.cast("struct API*", ptr)

    api.add_numbers = add_numbers
    api.dump_buffer = dump_buffer
    api.release_object = release_object

Компиляция:

gcc -o test_cffi test_cffi.c -I/home/jmudd/pgsql-native/Python-2.7.10.install/include/python2.7 -L/home/jmudd/pgsql-native/Python-2.7.10.install/lib -lpython2.7

Execute:

$ test_cffi
sum: 57.900000
buffer: 'T\x9e\x04\x08\xa8\x93\xff\xbf]\x86\x04\x08\x00\x00\x00\x00\x00\x00\x00\x00'
$ 
0 голосов
/ 12 сентября 2015

Это выглядит довольно мило http://thrift.apache.org/, есть даже книга об этом.

Детали:

Программная среда Apache Thrift для масштабируемого кросс-языка разработка сервисов, объединяет программный стек с генерацией кода двигатель для создания услуг, которые работают эффективно и без проблем между C ++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C #, Какао, JavaScript, Node.js, Smalltalk, OCaml и Delphi и другие языки.

0 голосов
/ 25 октября 2009

видимо Python нужно уметь скомпилировать в win32 dll, это решит проблему

Таким образом, что преобразование кода C # в Win32 DLL сделает его пригодным для использования любым инструментом разработки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...