질문을 하나 받았다. Python/C API 관련 질문인데 이게 조금 복잡하다.
C/C++에서 파이썬을 사용하고 싶으면 Python/C API를 사용해 파이썬을 프로그램에 포함시키면 된다. 아래 예제에서
#include "Python.h" int main(int argc, char *argv[]) { Py_Initialize();
PyRun_SimpleString("from time import time,ctime\n" "print('Today is', ctime(time()))\n"); Py_Finalize(); } |
현재 시간을 가져오는 프로그램을 파이썬의 time 모듈을 이용해서 구현한 것이다.
Python/C API 중 상위 레이어 함수인 PyRun_simpleString을 사용해 쉽게 파이썬을 C/C++에서 실행 시킬 수 있다.
그렇다면 PyRun_SimpleString의 첫 번째 인수인 파이썬 구문에서 C/C++ 함수를 호출 할 수 있을 까? 물론 가능하다. 이를 위해서 파이썬 확장 모듈이라는 것이 있다.
파이썬에서 사용할 모듈 -여기서는 spam-을 만들고 컴파일 하면 shared library가 만들어 지고 이를 파이썬이 설치되어 있는 디렉터리에 옮겨 놓으면 된다.
그리고 아래와 같이 사용하면 된다.
PyRun_SimpleString("import spam\n" "print('String Length is : ', spam.strlen('abc'))\n"); |
하지만 so를 매번 옮겨 놓을 수도 없는 노릇이고, 원하는 것은 한 소스코드 안에서 파이썬과 C/C++이 서로 통신하듯이 쓰는 것이기 때문에 이 방법은 문제가 있다.
일단 C/C++의 함수를 파이썬에서 사용하게 하려면 함수들을 파이썬 확장 모듈로 만들어야 한다.
여기까지는 순조로웠는데, 만든 모듈을 파이썬에서 임포트 할 수 없었다.
문제는 PyRun_SimpleString안의 파이썬구문이 실행 될 때, 우리가 만든 spam 모듈이 파이썬 built-in 네임스페이스에 포함 되지 않는 다는 것이다.
참고할 예제도 없어서, 주말 내내 Python Document를 읽어보았다.
그래서 찾아낸 함수가 PyImport_ExtendInittab, PyImport_AppendInittab이다.
(http://docs.python.org/py3k/c-api/import.html?highlight=pyimport_#PyImport_ExtendInittab)
해당 모듈을 build-in 에 추가 해주는 고마운 함수이다. Importing Modules(http://docs.python.org/py3k/c-api/import.html)를 보면 모듈을 C API에서 임포팅 하는 함수들이 나온다. PyImport_AppendInittab으로 예제를 만들고 테스트 해봤다.
#include "Python.h"
static PyObject * spam_strlen(PyObject *self, PyObject *args) { const char* str=NULL; int len;
if (!PyArg_ParseTuple(args, "s", &str)) return NULL;
printf("argument = %s\n", str); len = strlen(str);
return Py_BuildValue("i", len); }
static PyMethodDef SpamMethods[] = { {"strlen", spam_strlen, METH_VARARGS, "count a string length."}, {NULL, NULL, 0, NULL} };
static struct PyModuleDef spammodule = { PyModuleDef_HEAD_INIT, "spam", "It is test module.", -1, SpamMethods };
static PyObject* PyInit_spam(void) { return PyModule_Create(&spammodule); }
int main(int argc, char *argv[]) {
PyImport_AppendInittab("spam", &PyInit_spam);
Py_Initialize();
PyRun_SimpleString( "import spam\n" "print('String Length is : ', spam.strlen('abc'))\n" );
Py_Finalize(); return 0; } |
PyImport_AppendInittab에서 built-in에 포함될 모듈의 이름과 만든 모듈 포인터를 넣어준다.
한가지 특이한 점이 PyInit_spam처럼 모듈을 바로 입력 받지 않고 함수 포인터를 받는 다는 것이다.
PyImport_AppendInittab는 Py_Initialize()를 호출하기 전에 호출 되야 한다.
PyRun_SimpleString안의 파이썬 구문에서 spam 모듈이 정상적으로 임포트되고, 함수도 호출 되는 것을 알 수 있다.
Tip.
위 테스트는 리눅스에서 파이썬 3.1에서 진행했다. 위 파일을 컴파일 할 때, 파이썬의 헤더 및 라이브러리 파일이 필요한데 아래처럼 간단하게 구할 수 있다.
cflags, ldflags 알아내는 법
$ /opt/bin/python3.2-config --cflags
$ /opt/bin/python3.2-config --ldflags