Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C++에서 Python 객체를 사용하는 방법: Cython으로 간단히 구현하기

C++에서 Python 객체를 다루는 방법

C나 C++ 코드 안에서 Python 객체를 직접 생성하고 조작해야 하는 경우가 있습니다. 이럴 때 Cython을 활용하면 복잡한 C API 호출 없이도 손쉽게 Python 객체를 래핑(wrapping)하고 임베딩(embedding)할 수 있습니다.

아래 예제는 간단한 Python 객체를 정의하고, 이를 C 코드에서 호출하는 전체 과정을 보여줍니다. 여기서는 .c 파일 기준으로 설명하지만, C++도 거의 동일한 절차를 따릅니다(컴파일 시 --cplus 옵션만 추가하면 됩니다).

1. Cython으로 Python 클래스 작성하기

먼저 pycls.pyx 파일에 데이터를 저장하고 출력하는 간단한 클래스를 작성합니다.

class PyClass(object):
    def __init__(self):
        self.data = []
    def add(self, val):
        self.data.append(val)
    def __str__(self):
        return "Data: " + str(self.data)

cdef public object createPyClass():
    return PyClass()

cdef public void addData(object p, int val):
    p.add(val)

cdef public char* printCls(object p):
    return bytes(str(p), encoding='utf-8')

여기서 핵심은 public 키워드입니다. 이 키워드를 붙인 함수는 Cython이 컴파일될 때 .h 헤더 파일에 함수 선언을 자동으로 생성해 주기 때문에, 외부 C/C++ 코드에서 바로 호출할 수 있습니다.

  • createPyClass() — Python 객체를 새로 생성해 반환합니다.
  • addData() — 해당 객체의 add() 메서드를 호출해 값을 추가합니다.
  • printCls() — 객체의 문자열 표현을 UTF-8로 인코딩된 C 문자열(char*)로 반환합니다.

2. Cython 컴파일로 소스 및 헤더 파일 생성

다음 명령어로 pyx 파일을 컴파일하면 소스 코드(.c)와 함수 선언(.h)이 포함된 두 개의 파일이 생성됩니다.

cython pycls.pyx        # C용
cython --cplus pycls.pyx  # C++용

3. C 메인 프로그램 작성하기

이제 Python 인터프리터를 초기화하고 위에서 만든 함수들을 호출하는 main.c 파일을 작성합니다.

#include "Python.h"  // Python.h는 반드시 가장 먼저 포함해야 합니다.
#include "pycls.h"   // Cython이 생성한 헤더 파일.

int main(int argc, char *argv[]){
    Py_Initialize();   // Python 인터프리터 초기화
    PyInit_pycls();    // 모듈 초기화 (Python 2에서는 initpycls() 사용)

    PyObject *obj = createPyClass();
    for(int i=0; i<10; i++){
        addData(obj, i);
    }
    printf("%s\n", printCls(obj));

    Py_Finalize();
    return 0;
}

Py_Initialize()로 Python 환경을 시작하고, PyInit_pycls()로 모듈을 등록한 뒤, 일반적인 C 함수를 호출하듯 Python 객체를 다룰 수 있습니다.

4. 컴파일 및 실행 결과 확인

컴파일에 필요한 플래그는 python3.5-config(또는 Python 2의 경우 python-config)에서 얻을 수 있습니다.

gcc pycls.c main.c -L$(python3.5-config --cflags) -I$(python3.5-config --ldflags) -std=c99

컴파일이 완료되면 실행 파일을 실행하여 Python 객체와 상호작용하는 결과를 확인할 수 있습니다.

./a.out
Data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

정리

이 예제에서 핵심 역할을 한 것은 Cython과 public 키워드입니다. public 키워드 덕분에 .h 헤더 파일이 자동 생성되어 C/C++ 쪽에서 Python 함수를 손쉽게 호출할 수 있었습니다.

만약 헤더 파일 자동 생성 없이 진행하고 싶다면, Cython으로 일반 Python 확장 모듈만 컴파일한 뒤, 헤더 선언과 초기화 같은 보일러플레이트 코드를 직접 작성하는 방법도 가능합니다. 다만 public 옵션을 사용하는 편이 유지보수 측면에서 훨씬 간편합니다.