====== Embedded Python ====== ===== 개괄 ===== 파이썬이 다른 언어와 어떻게 잘 어울릴 수 있는지 증명하는 과정? 파이썬으로도 좋은 GUI를 만들 수 있겠지만, 시스템쪽으로 Qt library를 사용하기로 결정하였고, 이것으로 사용하면 더욱 좋은 그래픽 인터페이스를 만들 수 있겠다는 생각을 하였다. 그러나 네트워크 및 인증 모듈 등을 도입하는 과정에서 기존의 C/C++도 나쁘지는 않지만, 파이썬의 기능이 워낙 막강하고 편리하기 때문에 일단 파이썬을 핵심 모듈로 사용하고, 그 이외의 모듈은 Qt를 사용하는 방법을 채택하였다. 이 방법이 아름다운 이유는, 핵심 라이브러리는 여러 시스템, MacOS, Windows, Linux 등에 포팅된다. C/C++로 GUI를 일단 생성하기로 마음먹었지만, 차후 C# 내지는 다른 언어로도 작성될 수 있다고 생각한다. 그런데 이 핵심 모듈을 여러 언어로 작성하기보다, 파이썬으로만 고수한다면 어떨까? 핵심 모듈과 다른 모듈과의 인터페이스만 잘 맞추어주면 되는 것 아닐까. 파이썬 자체의 생산성이 워낙 높기 때문에 핵심 모듈이 점차 다른 언어로 뒷받침된다 하더라도 일단은 파이썬으로도 충분히 커버 가능할 것이다. 이런 이유로 embedded python을 계속 생각하게 되었다. 파이썬을 타 언어와 접목하는 방법에는 두 가지 요령이 있다. 다른 언어가 파이썬의 모듈을 실행하는 것, 반대로 파이썬에서 타 언어의 모듈을 실행하는 방법이다. 전자가 현재 문서에서 말하는 '내장된 파이썬(embedded python)'이고, 후자는 파이썬이 다른 언어로 '확장'되므로 '확장된 파이썬(extended python)'이라 칭한다. 이 문서에서는 내장된 파이썬에 주목하고 이것을 사용하기 위해 어떻게 해야 하는지에 대해 실질적으로 접근해 보도록 한다. 전반적인 사항에 대해서는 생략하고, 주로 파이썬에서 제공하는 C 라이브러리를 직접 사용하는 법과, boost library를 이용하여 보다 편리하게 사용하는 법에 대해 논하도록 하자. 물론 파이썬은 C/C++ 뿐 아니라 C#, Java 등과도 어울릴 수 있으나 문서를 참고하면 일단 C API에 대해서만 서술하고 있다. 일단 C API를 직접 활용하는 방법과, 그리고 boost::python 사용하여 보다 쉽게 코드를 작성하는 방법에 대해 기록하고자 한다. 설명하려는 사람이 가진 마법의 단어가 있다. "매뉴얼을 보세요." 물론 나도 모든 것을 설명할 수 없기에 결국에는 이 마법의 단어를 사용할 수 밖에는 없겠지만, 최소한 매뉴얼을 보고 언제 어디서든 방법을 따라할 만큼의 디테일한 설명, 다시 말해 매뉴얼이 다 하지 못한 이야기 정도는 할 것이다. 이 문서가 설명하고자 하는 embedded python의 목표는 다음과 같다. - 파이썬으로 작성한 모듈의 변수, 함수, 클래스를 외부 프로그램이 임의로 생성할 수 있도록 한다. - 생성된 객체에 값을 입력하고, 접근하며, 출력을 받아올 수 있도록 한다. ===== C 라이브러리를 직접 활용하기 ====== ==== C 프로그램에서 파이썬 부리기: Hello, World ==== 다음과 같은 코드를 작성합니다. 무지하게 간단하지만, 천천히 시작하도록 하지요. #include int main(int argc, char** argv) { Py_SetProgramName(argv[0]); Py_Initialize(); PyRun_SimpleString("print \'Hello, World!\'\n"); Py_Finalize(); return 0; } ==== 유닉스 상에서 컴파일하기 ==== [[http://docs.python.org/2/extending/embedding.html#compiling-and-linking-under-unix-like-systems|문서]]를 참고하면 이렇게 작성한 코드는 컴파일을 해야 합니다. 하지만 라이브러리 링크 정보와 헤더 정보를 주지 않으면 에러가 나게 되겠죠. 문서는 이 라이브러리 링크 정보와 헤더 정보를 쉽게 알아내는 법에 대해 설명합니다. 파이썬 버전이 2.7인 경우, *''python2.7-config --cflags'': 헤더 정보 및 컴파일 정보를 출력합니다. *''python2.7-config --ldflags'': 링크 정보를 출력합니다. 그러므로 이 정보를 이용하면 다음과 같이 위 코드를 컴파일 할 수 있습니다. gcc `python2.7-config --cflags` `python2.7-config --ldflags` -o hello_py hello_py.c 그런데 이렇게 매번 컴파일하기 힘드니, Makefile을 따로 만들어 두겠습니다. CC = /usr/bin/gcc CONFIG = /opt/local/bin/python2.7-config CFLAGS = `$(CONFIG) --cflags` LDFLAGS = `$(CONFIG) --ldflags` TAR = hello_py.out # 여기에 .out 파일을 추가하세요 OBJ = $(TAR:.out=.o) SRC = $(TAR:.out=.c) all: $(TAR) .o.out: $(OBJ) $(CC) $(LDFLAGS) -o $@ $< .c.o: %.c $(CC) $(CFLAGS) -c $< clean: rm -fr $(TAR) $(OBJ) ''TAR = hello_py.out'' 이 적힌 줄에 공백으로 계속 실행 파일의 이름을 입력하면 그에 맞게 소스가 컴파일됩니다. 이렇게 만들어진 바이너리 파일을 실행하면 다음과 같은 결과가 나올 것입니다. Hello, World! ==== 첫번째 실행 결과에 대해 ==== 결과는 성공적이지만, 왠지 석연치 않습니다. ''PyRun_SimpleString()'' 함수는 이름 그대로 너무 간단합니다. 우리의 목표에는 미치지 못하는 결과입니다. ==== 두번째 코드: 함수 호출 ==== 두번째는 파이썬의 함수를 호출해 볼 것입니다. 치사하게 인자가 없는 함수를 부르진 않을게요. 이것도 문서에 있는 예제로, 다음과 같은 역할을 하도록 프로그램되어 있습니다. def multiply(a,b): print "Will compute", a, "times", b c = 0 for i in range(0, a): c = c + b return c 우리는 이 파이썬 함수를 C 코드에서 부를 것입니다. 그러나 이전처럼 뭔가 소스 코드가 하드코딩 되어 있다든지 하는 법 없이 동작하게 할 것입니다. 문서에서는 긴 소스를 죽 나열해두고 나중에 설명을 한 반면, 여기서는 어떻게 프로그램이 흘러가는지에 대해 중점을 맞추고 서술하겟습니다. 그 편이 더 이해하기 좋습니다. === Py_SetProgramName() === Py_Initialize() 함수가 호출되기 전에 호출해야 하는 함수입니다. 파이썬이 실행 중 가지게 되는 자신의 이름입니다. 그러니까 이 함수로 전달된 값은 파이썬의 ''sys.argv[0]''와 동일한 것입니다. 이 함수는 선택적이어서 굳이 호출하지 않아도 됩니다. 이 경우 이름은 'python'이 된다고 합니다. === Py_Initialize(), Py_Finalize() === 파이썬 API 사용 전후에 **반드시** 불려야 하는 함수입니다. === 파이썬 코드로부터 어떤 일들이 일어나는지 먼저 유추해보기 === 우선 multiply.py를 부르는 파이썬 스크립트와, 그것을 실행했을 때 일어나는 과정에 대해 간단히 생각해보죠. 이걸 우선 유념해두고 코드를 읽는 편이 낫다고 생각합니다. 그럼 간단하게 multiply.py를 사용하는 파이썬 스크립트를 생각해 보죠. import multiply if __name__ == '__main__': print multiply.multiply(4, 5) 이 코드는 다음과 같이 동작합니다. - multiply 모듈(multply.py 파일)을 로드합니다. - 그러면 파이썬은 자신이 알고 있는 경로에서 multiply.py라는 파일을 찾아서 import를 시도합니다. - 파일을 찾으면 성공적으로 모듈을 로딩할 것이고, 아니면 에러를 낼 것입니다. - if 블록을 수행합니다. 파이썬은 명시적인 main() 함수가 없으므로 보통 이렇게 그 스크립트를 실행합니다. multiply 모듈의 함수 multiply를 호출합니다. 두 개의 정수 인자 4와 5도 같이 함수에 넘깁니다. - 함수는 정의된 동작을 수행하고 정수를 하나 넘깁니다. 그리고 그것을 print문을 이용하여 출력합니다. - 에러가 없다면 정상적으로 종료될 것입니다. ==== C 코드에서 파이썬 함수 호출 ==== 상당히 간단한 함수 호출이지만, 상당히 번거로운 작업들이 많습니다. 이 번거로운 작업들의 패턴은 크게 이렇게 생각할 수 있습니다. * C의 데이터를 파이썬의 객체형으로 표현하는 일 * C에서 파이썬의 모듈과 객체를 찾아 그것을 참조하는 일 * 참조된 객체를 호출하는 일 * 객체가 반환한 결과를 파이썬으로부터 C로 표현하는 일 * 기타 여러 제어를 위한 일 === C의 데이터를 파이썬의 객체형으로 표현 === 우선 C의 데이터를 파이썬의 객체형으로 표현하는 법에 대해 설명합니다. C에서 파이썬의 문자열을 생성하는 함수는 [[http://docs.python.org/2/c-api/string.html#PyString_FromString|PyString_FromString()]]입니다. 리턴 형은 ''PyObject*''인데 모든 파이썬의 데이터는 이 ''PyObject''입니다. 파이썬의 어떤 자료형이라도 ''PyObject''로 표현되므로, 항상 주의하여야 합니다. char *str = NULL; PyObject* pString; str = ...; pString = PyString_FromString(str); 위 코드에서 ''PyString_FromString()''의 반환을 참조하는 ''pString''과 다른 API를 활용하여 마치 파이썬에서 문자열 함수를 사용하는 것과 유사하게 사용할 수 있습니다. 그러나 모든 기능이 제공되는 것은 아닙니다. 문서의 [[http://docs.python.org/2/c-api/concrete.html|Concrete Objects Layer]]를 참고하면 더 많은 자료형에 대해 객체 생성을 할 수 있습니다. === 파이썬 모듈을 찾아 import 하기 === [[http://docs.python.org/2/c-api/import.html|Importing Modules]]의 문서를 참고합니다. 한 예는 [[http://docs.python.org/2/c-api/import.html#PyImport_Import|PyImport_Import()]]입니다. 모듈 이름을 넘기면 모튤을 로딩합니다. 반환값은 그 모듈을 참조합니다. 이 때 모듈 이름은 C의 문자열이 아니라, 파이썬의 문자열입니다. PyObject *pModuleName; PyObject *pSysModule; pModuleName = PyString_FromString("sys"); pSysModule = PyImport_Import(pModuleName); === 파이썬의 변수/함수/클래스를 참조하고 호출하기 === 파이썬의 변수, 함수, 클래스는 모두 모듈의 '속성(attribute)'입니다. 그러므로 해당 모듈에서 변수, 함수, 클래스 등을 참조하려면, '[[http://docs.python.org/2/c-api/object.html#object-protocol|Object Protocol]]' 섹션의 [[http://docs.python.org/2/c-api/object.html#PyObject_GetAttrString|PyObject_GetAttrString()]]함수를 이용합니다. 이 외에도 여러 함수가 있으므로 문서를 한 번 꼼꼼하게 읽어 둘 필요가 있습니다. PyObject *pVersion; PyObject *pFunction; PyObject *pReturn; pVersion = PyObject_GetAttrString(pSysModule, "version"); pFunction = PyObject_GetAttrString(pSysModule, "getfilesystemencoding"); pReturn = PyObject_CallObject(pFunction, NULL); class sampleClass: def __init__(self, arg1, arg2, arg3): self.arg1 = arg1 self.arg2 = arg2 self.arg3 = arg3 ... 위와 같은 파이썬 클래스가 정의되어 있습니다. 이것을 인스턴스화 하는 법은 아래와 같습니다. PyObject *pSampleClass; PyObject *pInstance; PyObject *pArgList; pSampleClass = PyObject_GetAttrString(pSampleClass, "SampleClass"); pArgList = Py_BuildValue("sid", arg1, arg2, arg3); /* string, integer, double */ pInstance = PyObject_CallObject(pSampleClass, pArgList); [[http://docs.python.org/2/c-api/object.html#PyObject_CallObject|PyObject_CallObject()]]는 호출할 수 있는 (callable) 오브젝트를 호출하고, 값을 반환받습니다. ''getfilesystemencoding''는 호출 가능하므로 pReturn에 값이 반환될 것입니다. 반환된 값을 C에서 어떻게 끄집어 내야 할지는 좀 더 후에 서술하겠습니다. === 반환된 값을 C로 끄집어내기 === ''PyObject''의 데이터를 C로 가져오는 함수는 Py**[XXX]**_As**[YYY]** 스타일로 정의되어 있습니다. 그래서 오브젝트가 실수형인 경우 실수에서 double 타입으로 가져올 때는 [[http://docs.python.org/2/c-api/float.html#PyFloat_AsDouble|PyFloat_AsDouble()]]을 사용하고, 정수형인 경우 [[http://docs.python.org/2/c-api/int.html#PyInt_AsLong|PyInt_AsLong()]]을 사용하는 형태를 취하고 있습니다. int intReturn; float floatReturn; Py_complex complexReturn; pIntReturn = PyInt_AsLong(pyIntObject); pFloatReturn = (float)PyFloat_AsDouble(pyFloatObject); pComplexReturn = PyComplex_AsCComplex(pyComplexObject); 리스트나 딕셔너리에서 값을 가져올 때는 ''PyList_Size(), PyList_GetItem(), PyList_SetItem(), PyDict_Size(), PyDict_GetItem(), PyDict_SetItem()''을 사용합니다. 사용은 매우 직관적입니다. === 레퍼런스 카운팅 === 파이썬에서는 메모리 관리를 할 필요가 없습니다. 내부적으로 메모리 관리를 해 주지요. 그러나 이 편리한 기능은 C API에서까지 사용할 수는 없습니다. 그러므로 직접 우리가 생성된 PyObject 마다 레퍼런스 카운팅을 해 주어야 합니다. ''Py_INCREF()'', ''Py_DECREF()'', 함수들은 다 그런 레퍼런트 카운팅을 위해 존재합니다! 한편 [[http://docs.python.org/2/c-api/list.html#PyList_SetItem|PyList_SetItem()]], [[http://docs.python.org/2/c-api/tuple.html#PyTuple_SetItem|PyTuple_SetItem()]]이 두 함수는 레퍼런스 카운팅을 자기가 차지합니다. === 두번째 예제 코드 === 이제 소스 코드를 볼께요. 간단한 함수 호출 하나를 하는데 좀 많이 난리를 쳐야 합니다. [[http://docs.python.org/2/extending/embedding.html#pure-embedding|원래의 소스 코드]]와는 달리 메모리 NULL 체크들은 생략했습니다. 물론 실전에서는 해서는 안 될 방법이지만, 핵심적은 흐름을 보다 명확히 보기 위해서입니다. 위 Makefile에 ''TAR'' 변수에 ''call.out''을 추가해주세요. #include int main(int argc, char** argv) { int i; PyObject *pModule, *pFunc; PyObject *pArgs, *pValue; if (argc < 3) { fprintf(stderr, "Usage: call.out pythonfile function [args]\n"); return 1; } Py_SetProgramName(argv[0]); Py_Initialize(); /* modified part */ PyRun_SimpleString( "import sys\n" "sys.path.append('.')\n"); pModule = PyImport_ImportModule(argv[1]); pFunc = PyObject_GetAttrString(pModule, argv[2]); pArgs = PyTuple_New(argc - 3); for(i = 0; i < argc - 3; ++i) { pValue = PyInt_FromLong(atoi(argv[i+3])); PyTuple_SetItem(pArgs, i, pValue); } pValue = PyObject_CallObject(pFunc, pArgs); printf("Result of call: %ld\n", PyInt_AsLong(pValue)); Py_DECREF(pArgs); Py_DECREF(pValue); Py_DECREF(pFunc); Py_DECREF(pModule); Py_Finalize(); return 0; } if 블록을 다 제거하여 훨씬 보기는 편합니다. 원래 예제 코드와 다른 점은 바로 이 부분입니다. PyRun_SimpleString("import sys\n" "sys.path.append('.')\n"); pModule = PyImport_ImportModule(argv[1]); 처음 ''PyRun_SimpleString()''은 현재 디렉토리도 모듈 검색 범위에 넣도록 하기 위해 추가된 것입니다. 아마 ''multiply.py'' 파일은 대개 이 소스 코드와 같은 경로에 두고 있을 것입니다. 그런데 이 코드를 넣지 않으면 아마 에러가 날 수도 있습니다. 그리고 원래 소스는 ''PyObject *pName''을 선언하여 argv[1]을 파이썬의 문자열 형태로 만듭니다. 그리고 ''Py_DECREF()''를 호출하지요. 그렇지만 char* 형태를 입력으로 받는 ''PyImport_ImportModule()''이 있으므로 이것으로 갈음할 수 있습니다. make ./call.out multiply multiply 3 5 Will compute 3 times 5 Result of call: 15 ==== 세번째 예제 ==== 세번째 예제는 조금 더 다양하게 가 보도록 하죠. urllib을 이용해 웹페이지의 소스 코드를 가져와, 외부 링크 즉, ''http...''로 시작되는 URL을 가져오도록 합니다. #!/usr/bin/python import urllib import re href_expr = re.compile(r' 이 모듈은 이렇게 사용하겠죠. import count_href if __name__ == '__main__': counter = count_href.count_href() counter.count('http://www.google.com/en') counter.count('http://www.daum.net/') counter.count('http://www.naver.com/') result = counter.get_result() for item in result: print "URL: " + item["url"] print len(item["links"]) #for link in item["links"]: # print link 이 모듈의 클래스를 C 코드에서 가져오도록 합니다. #include #include void extract(PyObject* instance) { PyObject *returned_object; Py_ssize_t i, list_size; returned_object = PyObject_CallMethod(instance, "get_result", NULL); /* returned_object: list of dicts. every dict has keys: url, links links is also list */ list_size = PyList_Size(returned_object); for(i = 0; i < list_size; ++i) { PyObject *dict_item, *url, *links; Py_ssize_t links_size, j; dict_item = PyList_GetItem(returned_object, i); assert(dict_item != NULL); url = PyDict_GetItemString(dict_item, "url"); assert(url != NULL); links = PyDict_GetItemString(dict_item, "links"); assert(links != NULL); links_size = PyList_Size(links); printf("url: %s\t", PyString_AsString(url)); printf("%d entries\n", (int)links_size); for(j = 0; j < links_size; ++j) { PyObject *entry_item = PyList_GetItem(links, j); assert(entry_item != NULL); printf("%s\n", PyString_AsString(entry_item)); } Py_DECREF(returned_object); } } int main(int argc, char** argv) { int i; PyObject *module, *class, *instance; int size; if(argc < 2) { fprintf(stderr, "Usage: count_href.out [URLS...]\n"); return 1; } Py_Initialize(); PyRun_SimpleString( "import sys\n" "sys.path.append('.')\n"); module = PyImport_ImportModule("count_href"); assert(module != NULL); class = PyObject_GetAttrString(module, "count_href"); assert(class != NULL); instance = PyObject_CallObject(class, NULL); assert(instance != NULL); for(i = 1; i < argc; ++i) { printf("count_href: %s\n", argv[i]); PyObject_CallMethod(instance, "count", "s", argv[i]); } extract(instance); Py_Finalize(); return 0; } ==== 정리 ==== * 프로그램 시작: Py_SetProgramName(선택적), Py_Initialize * 모듈 불러오기 * PyImport_Import: PyObject* 를 사용 * PyImport_ImportModule: char* 를 사용 * 주의! 모듈 경로에 현재 디렉토리가 포함되지 않음. * 해결책 - setenv("PYTHONPATH", ".", 1) - PyRun_SimpleString("import sys\n" "sys.path.append('.')\n"); * 모듈에서 객체(클래스, 함수) 불러오기 * PyObject_GetAttrString * 호출 가능한(callable) 객체 부르기 * PyObject_Call: callable에 tuple arg, dictionary kw 인자를 넘김 * PyObject_CallObject: tuple args 인자를 넘김 * PyObject_CallFunction: C fomat을 사용하여 호출 * PyObject_CallMethod: object의 method를 호출 * PyObject_CallFunctionObjArgs: CallFunction과 동일하나 PyObject*를 인자로 넘김 * PyObject_CallMethodObjArgs: CallMethod와 동일하나 PyObject*를 인자로 넘김 * 값 가져오기 * Int, Long, Float, Complex, String: Py[python_type]_As[c_type] * Dictionary, List: PyDict_GetItem, PyList_GetItem * 값 설정하기 * Int, Long, Float, Complex, String: Py[python_type]_From[c_type] * Dictionary, List: PyDict_SetItem, PyList_SetItem * 레퍼런스 설정하기 * Py_INCREF, Py_DECREF: 레퍼런스 카운트 증가, 감소 * Py_CLEAR: 레퍼런스 카운트를 없앰 * 프로그램 종료: Py_Finalize ===== boost.python 사용하기 ===== C API를 활용하여 원하는 기능을 구현할 수도 있지만, 간단한 데이터를 가져오는데도 상당히 코드가 많이 필요합니다. API를 간단히 파악하는 정도로 두고, 보다 간결하고 편리하게 쓸 수 있는 라이브러리를 찾아야 할 것 같습니다. [[https://wiki.python.org/moin/IntegratingPythonWithOtherLanguages | 파이썬 위키]]에 다른 언어와 파이썬을 연동하는 방법이 잘 나와 있습니다. 여기서는 C++의 대표적인 라이브러리인 [[http://www.boost.org/| Boost C++ Libraries]]를 사용하도록 하겠습니다. CC = /usr/bin/g++ PYTHON_CONFIG = /opt/local/bin/python2.7-config BOOST_INC_PATH = /opt/local/include BOOST_LIB_PATH = /opt/local/lib CFLAGS = `$(PYTHON_CONFIG) --cflags` -I $(BOOST_INC_PATH) LDFLAGS = `$(PYTHON_CONFIG) --ldflags` -L $(BOOST_LIB_PATH) -lboost_python-mt TAR = test.out hello_py.out # 여기에 .out 파일을 추가하세요 OBJ = $(TAR:.out=.o) SRC = $(TAR:.out=.cpp) all: $(TAR) .o.out: $(OBJ) $(CC) $(LDFLAGS) -o $@ $< .cpp.o: %.cpp $(CC) $(CFLAGS) -c $< clean: rm -rf $(TAR) $(OBJ) ==== 첫번째 boost.python 예제 ==== #include int main(int argc, char** argv) { Py_Initialize(); boost::python::exec("print \'Hello, World!\'\n"); Py_Finalize(); return 0; } 현재는 별다른 것이 없어 보이네요 ==== 두번째 boost.python 예제 ==== #include #include namespace bp = boost::python; void workaround_for_local_modules() { PyRun_SimpleString( "import sys\n" "sys.path.append('.')\n"); } int main(int argc, char** argv) { if (argc < 3) { fprintf(stderr,"Usage: call pythonfile funcname [args]\n"); return 1; } Py_Initialize(); workaround_for_local_modules(); try { bp::object module_name; bp::object module; bp::object func; module_name = bp::object(bp::handle<>(PyString_FromString(argv[1]))); module = bp::object(bp::handle<>(PyImport_Import(module_name.ptr()))); func = module.attr(argv[2]); if(func && PyCallable_Check(func.ptr())) { bp::object tuple; tuple = bp::object(bp::handle<>(PyTuple_New(argc - 3))); for(int i = 0; i < argc - 3; ++i) { PyObject* arg; arg = PyInt_FromLong(atoi(argv[i+3])); PyTuple_SetItem(tuple.ptr(), i, arg); } bp::object valueObj; int value; valueObj = bp::object(bp::handle<>(PyObject_CallObject(func.ptr(), tuple.ptr()))); value = bp::extract(valueObj); std::cout << value << std::endl; } } catch(bp::error_already_set const &) { PyErr_Print(); } Py_Finalize(); return EXIT_SUCCESS; } ''PyObject''가 ''boost::python::object''로 래핑되어 있습니다. 또한 ''boost::python::handle<>''을 이용하면 ''PyObject''의 레퍼런스를 관리해 줍니다. ''boost::python''의 대부분의 기능은 extending에 초점이 맞춰져 있고 embedding에는 그다지 많은 기능을 제공하지 않는 편이므로 상당히 많은 부분에서 C API를 그대로 가져와 사용해야 합니다. ==== 세번째 boost.python 예제 ==== ===== 참고 사이트 ===== * [[http://docs.python.org/2/extending/embedding.html|Embedding Python in Another Application]] * [[https://wiki.python.org/moin/IntegratingPythonWithOtherLanguages | Integrating Python With Other Languages]] * [[http://www.boost.org/doc/libs/1_55_0/libs/python/doc/index.html | Boost.Python Index]]