문제

새로운 기능을 갖춘 대형 C 프로젝트를 확장하고 싶지만 Python으로 작성하고 싶습니다. 기본적으로 C 코드에서 Python Code를 호출하고 싶습니다. 그러나 Swig와 같은 Python-> C 래퍼는 반대편을 허용합니다. 즉, C 모듈을 작성하고 Python에서 C를 호출하는 것입니다.

IPC 또는 RPC와 관련된 접근법을 고려하고 있습니다 (여러 프로세스가 마음에 들지 않습니다). 즉, 순수한 파이썬 구성 요소가 별도의 프로세스 (동일한 기계에서)에서 실행되고 C 프로젝트가 소켓 (또는 Unix 파이프)에서 쓰기/읽기를 통해 이와 통신하도록합니다. 내 파이썬 구성 요소는 소켓에 읽고 쓰기 위해 통신 할 수 있습니다. 그게 합리적인 접근 방식인가요? 더 좋은 것이 있습니까? 특별한 RPC 메커니즘처럼?

지금까지 답변 해 주셔서 감사합니다. 그러나 C 프로그램과 별도의 프로세스에서 Python 프로그램을 사용하고 싶기 때문에 IPC 기반 접근 방식에 중점을두고 싶습니다. Python 통역사를 포함시키고 싶지 않습니다. 감사!

도움이 되었습니까?

해결책

나는 추천한다 여기에 자세히 설명합니다. Python 코드의 문자열을 실행하는 방법을 설명하여 시작한 다음 Python 환경을 설정하여 C 프로그램과 상호 작용하고 C 코드에서 Python 함수를 호출하고 C 코드에서 Python 객체를 조작하는 방법에 대해 자세히 설명합니다.

편집하다: IPC의 경로를 정말로 가고 싶다면 사용하고 싶을 것입니다. 구조물 모듈 또는 더 나은 아직, protlib. 파이썬과 C 프로세스 간의 대부분의 의사 소통은 스트러크를 전달하는 것을 중심으로 진행됩니다. 소켓 위에 또는 공유 메모리.

나는 a를 만드는 것이 좋습니다 Command 명령과 그 주장을 나타내는 필드와 코드가있는 구조. 나는 당신이 성취하고자하는 것에 대해 더 많이 알지 못하고 훨씬 더 구체적인 조언을 줄 수 없지만 일반적으로 나는 다음을 추천합니다. protlib 도서관은 C와 Python 프로그램간에 의사 소통하는 데 사용하기 때문에 (면책 조항 : 저는 Protlib의 저자입니다).

다른 팁

매뉴얼의 관련 장을 참조하십시오. http://docs.python.org/extending/

본질적으로 Python 통역사를 프로그램에 포함시켜야합니다.

Python 응용 프로그램을 쉘 스크립트로 포장하고 C 응용 프로그램에서 호출하는 것을 고려 했습니까?

가장 우아한 솔루션은 아니지만 매우 간단합니다.

Python <-> C 커뮤니케이션에 IPC 접근 방식을 사용하지는 않았지만 잘 작동해야합니다. 나는 C 프로그램에 표준 포크 exec을하고 리디렉션 된 것을 사용하도록합니다. stdin 그리고 stdout 의사 소통을위한 아동 프로세스에서. 멋진 텍스트 기반 커뮤니케이션을 통해 Python 프로그램을 쉽게 개발하고 테스트 할 수 있습니다.

IPC와 함께 가기로 결정했다면 아마도 XML-RPC -크로스 플랫폼, 원하는 경우 나중에 다른 노드에 Python Server 프로젝트를 쉽게 넣을 수 있습니다. 여기 C와 Python을 포함한 많은 사람들에게 여기 Python Standard Library의 일부인 간단한 XML-RPC 서버의 경우 다른 접근 방식만큼 확장 가능하지는 않지만 유스 케이스에 적합하고 편리합니다).

그것은 모든 경우에 완벽한 IPC 접근법이 아닐 수도 있습니다 (또는 완벽한 RPC 중 하나). 그러나 편의성, 유연성, 견고성 및 광범위한 구현은 많은 사소한 결함을 능가합니다.

분명히 파이썬은 Win32 DLL에 컴파일 할 수 있어야합니다. 문제가 해결 될 것입니다.

C# 코드를 Win32 DLL으로 변환하는 방식으로 모든 개발 도구에서 사용할 수있게됩니다.

이것은 꽤 멋져 보입니다 http://thrift.apache.org/, 그것에 관한 책도 있습니다.

세부:

확장 가능한 교차 서비스 개발을위한 Apache Thrift 소프트웨어 프레임 워크는 소프트웨어 스택과 코드 생성 엔진을 결합하여 C ++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, C#, C#, C#, C ++간에 효율적이고 완벽하게 작동하는 서비스를 구축합니다. 코코아, JavaScript, Node.js, SmallTalk, Ocaml 및 Delphi 및 기타 언어.

나는 "표준"접근 방식을 사용했습니다 다른 응용 프로그램에 Python을 포함시킵니다. 그러나 복잡하고 지루합니다. 파이썬의 각 새로운 기능은 구현하기가 고통 스럽습니다.

나는 예를 보았다 c에서 pypy를 호출합니다. CFFI를 사용하여 인터페이스를 단순화하지만 파이썬이 아닌 pypy가 필요합니다. 이 예제를 먼저 읽고 이해하십시오.

Python과 함께 작동하도록 c/pypy 예제를 수정했습니다. CFFI를 사용하여 C에서 Python을 호출하는 방법은 다음과 같습니다.

내 예제는 Python에서 하나가 아닌 세 가지 기능을 구현했기 때문에 더 복잡합니다. 데이터를 앞뒤로 전달하는 추가 측면을 다루고 싶었습니다.

복잡한 부분은 이제 주소를 통과하도록 격리되었습니다. api 파이썬에. 한 번만 구현해야합니다. 그 후 파이썬에 새로운 기능을 추가하기가 쉽습니다.

인터페이스 .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;
}

인터페이스 .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'
$ 
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top