2009-06-28 3 views
32

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

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

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

+5

Что вы Обоснование для желающих поставить программу на Python в отдельном процессе, и не желая, чтобы встроить интерпретатор Python? Мне любопытно. –

+1

Ну, если он может получить путь с помощью простых строк на python, а затем вернуться на C, когда это будет сделано, это будет намного проще, чем внедрение интерпретатора python. Простое вызов отдельного приложения python потребует 5 минут интеграции, если интерфейс прост (просто передайте строки и строки), и я уверен, что вложение интерпретатора займет немного больше 5 минут – hhafez

+0

Вот полный пример https: // переполнение стека.com/a/46441794/5842403 где вы можете увидеть встроенный Python в C, а затем C встроенный в Systemverilog с использованием DPI. – Joniale

ответ

10

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

EDIT: Если вы действительно хотите пойти по маршруту IPC, то вы захотите использовать the struct module или еще лучше, protlib. Большая часть коммуникации между процессом Python и C вращается вокруг передаваемых структур назад и вперед, либо over a socket, либо через shared memory.

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

+1

Я сделал это раньше, и все получилось хорошо. У меня было несколько процессов C, которые передавались путем отправки структур через сокет и хотели также разрешить процессы python. Написание протокола было тривиально в python, и я смог написать скрипт Python, который запускался как часть сборки для автоматического создания кода Python для упаковки/распаковки C-структур путем анализа файлов .H. Только проблема связана со всей упаковкой/распаковкой строк, производительность - это не то, что вы получите с помощью родного C, действующего непосредственно на двоичное представление структур, просто memcopy и отбрасывание сырых данных из сокета. – bdk

4

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

По сути вы будете иметь, чтобы встроить интерпретатор питона в вашу программу.

4

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

Не самое изящное решение, но это очень просто.

+0

Действительно, если он хочет запустить Python в отдельном процессе и поговорить с ним по stdin/stdio, это самое лучшее решение. – Crashworks

1

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

+0

Возможно ли использование fork-exec в Windows? Не уверен, что такое платформа опроса. –

+0

Существует (только) документированный способ сделать fork-exec под Windows, но я бы этого не делал. Он упомянул сокеты UNIX, поэтому я предполагаю, что цель не Windows. Думаю, это будет больше похоже на икру и трубку под Windows. –

1

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

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

0

по-видимому, Python нужно, чтобы иметь возможность компилировать в DLL win32, это позволит решить проблему

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

+1

Это относится только к платформе win32: -p Как насчет Unix? – Pharaun

0

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

Детали:

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

0

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

Я видел пример Calling PyPy from 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 

Compile:

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' 
$ 
Смежные вопросы