Вопрос по c, interop, python – Как вы называете код Python из кода C?

35

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

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

Спасибо за ответ до сих пор -however, i'd like to focus on IPC-based approaches since I want to have my Python program in a separate process as my C program. I don't want to embed a Python interpreter. Thanks!

Каково ваше обоснование того, что вы хотите поместить программу Python в отдельный процесс и не хотите вставлять интерпретатор Python? Мне любопытно. Craig McQueen
Что ж, если он сможет найти способ просто передать строки в python, а затем вернуться к C, когда это будет сделано, то это будет намного проще, чем встраивание интерпретатора python. Простой вызов отдельного приложения на Python потребует 5 минут интеграции, если интерфейс прост (просто передавайте строки и выводите строки), и я уверен, что встраивание интерпретатора займет чуть более 5 минут. hhafez
Вот полный примерstackoverflow.com/a/46441794/5842403  где вы можете увидеть встроенный Python в C, а затем C встроенный в Systemverilog с использованием DPI. Joniale

Ваш Ответ

8   ответов
4

ий оболочки и вызывать его из с в вашем C-приложении?

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

Error: User Rate Limit Exceeded
0

это решит проблему

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

Error: User Rate Limit Exceeded
0

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

Подробности:

The Apache Thrift software framework, for scalable cross-language services development, combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages.

11

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

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

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

Error: User Rate Limit Exceeded
4

http://docs.python.org/extending/

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

Error: User Rate Limit Exceeded
1

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

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

0

Встраивание 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

Выполнение:

$ 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'
$ 
1

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded

Похожие вопросы