잡동사니/개발관련

Python embedding C - C/C++에서 Python 모듈 사용하기(1)

Jayyy.H 2020. 10. 24. 00:15

Python 모듈을 C에서 import 하기

: 이 내용은 Python으로 개발된 모듈들을 C에서 자유자재로 이용하기 위해서 사용해야할 API 들과 그 전반에 대한 개발 가이드이다.

파일의 구성

: 이 Sample App은 크게 3개의 함수들로 구성하였다.

첫 번째로는, string을 인자로 받아 string을 반환하는 make_string 함수.
두 번째로는, int를 인자로 받아 int를 반환하는 make_int 함수.
마지막으로는, list 타입을 반환하는 make_list 함수이다.

파일의 구조는 다음과 같이 구성하였다.

jay@ ~/Desktop/tutorial/embedded-python $ tree .
.
├── bin
├── scripts
│   ├── __init__.py
│   └── my_module.py
└── src
    └── main.cpp

my_module.py 파일은 다음의 함수들로 구성된다.

def make_string(param):
    return "my_string: " + param

def make_int(param):
    return (1 + param)

def make_list():
    return ["1", "2", "3"]

main.cpp 파일은 다음의 함수들로 구성된다.

#include <Python.h>

void initialize(char* module_name, PyObject **pModule)
{
    Py_Initialize();

    PyObject *pName = PyUnicode_DecodeFSDefault(module_name);
    *pModule = PyImport_Import(pName);

    Py_DECREF(pName);
}

void finalize(PyObject *pModule)
{
    Py_DECREF(pModule);
    Py_FinalizeEx();
}

void make_string(PyObject *pModule, char *arg)
{
    PyObject *pFunc, *pArgs, *pValue;

    pFunc = PyObject_GetAttrString(pModule, "make_string");
    pArgs = PyTuple_New(1);

    PyTuple_SetItem(pArgs, 0, PyUnicode_FromString(arg));
    pValue = PyObject_CallObject(pFunc, pArgs);
    Py_DECREF(pArgs);

    printf("Result of call: %s \n", PyUnicode_AsUTF8(pValue));
    Py_DECREF(pValue);
    Py_DECREF(pFunc);
}


void make_int(PyObject *pModule, int arg)
{
    PyObject *pFunc, *pArgs, *pValue;

    pFunc = PyObject_GetAttrString(pModule, "make_int");
    pArgs = PyTuple_New(1);

    PyTuple_SetItem(pArgs, 0, PyLong_FromLong(arg));
    pValue = PyObject_CallObject(pFunc, pArgs);
    Py_DECREF(pArgs);

    printf("Result of call: %d \n", PyLong_AsLong(pValue));
    Py_DECREF(pValue);
    Py_DECREF(pFunc);

}

void make_list(PyObject *pModule)
{
    PyObject *pFunc, *pArgs, *pValue;

    pFunc = PyObject_GetAttrString(pModule, "make_list");

    pValue = PyObject_CallObject(pFunc, NULL);

    printf("Result of call: \n");
    for (int i = 0; i < PyList_Size(pValue); ++i)
    {
        printf("\t%dst item: %s \n", i, PyUnicode_AsUTF8(PyList_GetItem(pValue, i)));
    }
    Py_DECREF(pValue);
    Py_DECREF(pFunc);
}

int main(int argc, char *argv[])
{
    PyObject *pName, *pModule, *pFunc;
    PyObject *pArgs, *pValue;
    int i;

    initialize(argv[1], &pModule);

    make_string(pModule, "Hello World!");
    make_int(pModule, 99);
    make_list(pModule);

    finalize(pModule);

    return 0;
}

여기서 __init__.py 파일은 아무런 내용을 기입하지 않아도 상관없으므로, 실행을 위해서 단순히 파일만 만들어 놓자.

빌드 및 실행

: 빌드는 다음과 같은 명령어로 빌드할 수 있다.

jay@ ~/Desktop/tutorial/embedded-python $ gcc -o ./bin/main -I/usr/local/include/python3.7m ./src/main.cpp -L/usr/local/lib -lpython3.7m -lpthread -ldl -lutil -lm -Xlinker -export-dynamic

빌드 후 산출물을 가지고 실행하면 아래의 결과를 얻을 수 있을 것이다.


jay@ ~/Desktop/tutorial/embedded-python $ PYTHONPATH=. ./bin/main scripts.my_module

Result of call: my_string: Hello World! 

Result of call: 100 

Result of call: 

    0st item: 1 

    1st item: 2 

    2st item: 3

실행 중 발생되는 오류

  1. No module named pyfunction

ImportError: No module named pyfunction
  • $ PYTHONPATH=. ./main blah blah 을 통해 모듈의 위치를 찾는 경로에 현재 위치를 추가해 준다.
  1. reference undefined to "Py_Initialize", undefined referecne to 'log'

reference undefined to "Py_Initialize"

reference undefined to "PyRun_SimpleStringFlags"

reference undefined to "Py_Finalize"

collect2: error: ld returned 1 exit status





/Python-3.7.0/Objects/longobject.c:2287: undefined reference to `log'

/Python-3.7.0/Objects/floatobject.c:788: undefined reference to `pow'

/Python-3.7.0/Objects/floatobject.c:753: undefined reference to `floor'

/Python-3.7.0/Objects/floatobject.c:764: undefined reference to `fmod'

collect2: error: ld returned 1 exit status


  • $ python3-config --ldflags 수행 후 나타나는 결과를 gcc 옵션에 넣어준다.

  • 위의 해결방법으로 되지 않는다면, $ pkg-config --cflags --libs python3 수행 후 나타나는 결과를 gcc 옵션에 넣어준다. 본인은 위 방법으로 해결되었음.

  1. Python.h: No such file or directory

fatal error: Python.h: No such file or directory

compilation terminated.
  • $ sudo apt install libpython3-dev를 통해 Python 개발 관련 헤더파일을 설치해 준다.
  1. No module named 'BlahBlah'

ModuleNotFoundError: No module named 'BlahBlah'
  • module의 이름을 지정할 때 경로와 함께 지정해 준다.

  • 만일 ./module/my_module.py의 경로라면, module.my_module을 LOAD 하여야 한다.

Python2 에서 Python3로 변경시 유의해야 할 점

아래의 참고자료 중 Python2 to Python3 Porting Guide 를 보면 자세한 설명과 그 이유를 알 수 있으니 해당 문서를 참고하길 바랍니다.

: 추후 업데이트 예정

 

 

참고자료

[Ofiicial] Embedding Python Application

Python/C API Reference Manual

Python/C API Unicode Objects and Codecs

Python/C API List Objects

Python2 to Python3 Porting Guide

NLTK install guide

stackoverflow - No module name

stackoverflow - Python.h: No such file or directory