사용자 도구

사이트 도구


project:embeddedpythonboostpython

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판이전 판
다음 판
이전 판
project:embeddedpythonboostpython [2014/03/04 12:02] – [자동 레퍼런스 카운팅] changwooproject:embeddedpythonboostpython [2014/10/09 21:24] (현재) – 바깥 편집 127.0.0.1
줄 1: 줄 1:
 ====== Embedded Python: Boost Python ====== ====== Embedded Python: Boost Python ======
-boost::python은 파이썬 확장에 보다 초점을 맞춘 것으로 보이는 라이브러리다. 현재 boost::pyhthon은 임베딩을 완전히 지원하지는 않는다((http://www.boost.org/doc/libs/1_55_0/libs/python/doc/tutorial/doc/html/python/embedding.html)). C 언어만으로도 기능 구현은 가능하지만, C++의 보다 진보된 기능을 활용하는 것이 보다 더 낫다. 같은 역할을 하지만 훨씬 깔끔하게 코드 작이 능하다. +[[http://www.boost.org/doc/libs/1_55_0/libs/python/doc/|boost::python]]은 파이썬 확장(extended python)에 보다 초점을 맞춰져 있는 라이브러리입니다. 완전하게 임베딩을 완전히 지원하지는 않는다고 합니다(([[http://www.boost.org/doc/libs/1_55_0/libs/python/doc/tutorial/doc/html/python/embedding.html|Boost.Python - 1.55.0 <Tutorial Introduction> "Embedding" section]])). 지원하지 않는 부분들은 직접 [[http://docs.python.org/2.7/c-api/index.html|API]]를 활용하여야 합니다. 그렇지만 그것만으로도 훨씬 더 생산을 높일 수 있습니다. 사용할 만한 치는 충분합니다. 
  
-===== Boost Python으로 개선되는 점 =====+ 
 +===== Boost Python 개선되는 점 =====
 ==== 자동 레퍼런스 카운팅 ==== ==== 자동 레퍼런스 카운팅 ====
 [[http://docs.python.org/2/c-api/structures.html#PyObject|PyObject]]의 [[http://docs.python.org/2/c-api/intro.html#reference-counts|레퍼런스 카운팅]] 개념은 사실 [[http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/shared_ptr.htm|boost::shared_ptr]]의 개념과 매우 흡사합니다. 다만 C 언어의 한계로 인해 자동으로 객체의 생성, 소멸시 객체가 자동으로 호출되는 함수가 지원되지 않아 결국 그러한 카운팅을 프로그래머가 직접 해야 합니다. 매우 성가시고 불편합니다. [[http://docs.python.org/2/c-api/structures.html#PyObject|PyObject]]의 [[http://docs.python.org/2/c-api/intro.html#reference-counts|레퍼런스 카운팅]] 개념은 사실 [[http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/shared_ptr.htm|boost::shared_ptr]]의 개념과 매우 흡사합니다. 다만 C 언어의 한계로 인해 자동으로 객체의 생성, 소멸시 객체가 자동으로 호출되는 함수가 지원되지 않아 결국 그러한 카운팅을 프로그래머가 직접 해야 합니다. 매우 성가시고 불편합니다.
  
-boost::python을 사용하면 ''boost::python::object''라는 ''PyObject''의 래퍼를 사용할 수 있습니다. 이를 통해 더이상 프로그래머가 까다로운 레퍼런스 카운팅을 신경쓰지 않아도 됩니다. 변수의 범위를 벗어나면 자동으로 카운터를 내려주고, 다른 레퍼런스에 대입되면 카운터를 올려줍니다.+boost::python을 사용하면 ''boost::python::object''라는 ''PyObject''의 래퍼를 사용할 수 있습니다. 이를 통해 더이상 프로그래머가 까다로운 레퍼런스 카운팅을 신경쓰지 않아도 됩니다. 변수의 범위를 벗어나면 자동으로 카운터를 내려주고, 다른 레퍼런스에 대입되면 카운터를 올려줍니다. 이것은 일반 포인터 대 스마트 포인터의 사용에 비할 만합니다. ''boost::python::object''만으로도 코드는 무척 간결해질 수 있습니다.
  
 === 자동 레퍼런스 카운팅 테스트 === === 자동 레퍼런스 카운팅 테스트 ===
-진짜 동작하는지 정말 확인해 보고자 다음과 같은 실험을 해 보았습니다.+boost 라이브러리야 믿고 쓰면 되는데, 그래도 좀 궁금하더군요. 진짜 자동으로 레퍼런스 카운팅 되는지. 진짜 동작하는지 정말 확인해 보고자 다음과 같은 실험을 해 보았습니다. 좀 바보 같아도 돌다리도 두들겨 보라고. 8-)
   - Py_INCREF, Py_DECREF는 매크로입니다. 헤더에 선언되어 있으므로 소스를 재컴파일하지 않아도 헤더 파일에 쓰기 권한만 있으면 접근하여 수정할 수 있습니다. 그러므로 여기 접근하여 살짝 변경해 호출이 되는 것 확인할 수 있도로 합니다.   - Py_INCREF, Py_DECREF는 매크로입니다. 헤더에 선언되어 있으므로 소스를 재컴파일하지 않아도 헤더 파일에 쓰기 권한만 있으면 접근하여 수정할 수 있습니다. 그러므로 여기 접근하여 살짝 변경해 호출이 되는 것 확인할 수 있도로 합니다.
   - boost::python::object를 생성하여 레퍼런스 카운트를 직접 확인해 봅니다.   - boost::python::object를 생성하여 레퍼런스 카운트를 직접 확인해 봅니다.
줄 65: 줄 66:
  
  
-==== Try Catch를 이용한 예외 처리 ====== +==== Try Catch를 이용한 예외 처리 ====== 
-C API를 사용하는 경우, 모든 값에 대해 에러 체크를 명시적으로 수행해야 다. 이렇게 프로그래밍을 짜면 if ~ else의 철장벽이 겹겹이 쌓이게 다. 2~3중으로 쌓인 if 문 안에서 에러가 나는 경우, 안전하게 에러 처리를 하기가 너무 어렵고, 어쩔 수 없이 goto 문에 의지하는 경우도 종종 발생한다. +C API를 사용하는 경우, 모든 값에 대해 에러 체크를 명시적으로 수행해서 NULL이 리턴되는지 결과를 확인해야 합니다. 만약 어쩌다 리턴 값이 정상이 아닌 경우에 미리 선언된 자원은 해제하고 에러를 선언해야 하죠. 이 때 프로그래머는 종종 상당히 골치가 아픕니다. 이렇게 하나하나 코드의 에러를 확인하면서 짜면 필수불가결하게 if ~ else의 철장벽이 겹겹이 쌓이게 됩니다. 이렇게 높다랗게 쳐진 괄호를 뛰어넘어 메모리 해제를 하기 위해서 결국 어쩔 수 없이 goto 문에 의지하는 경우도 종종 발생한다.
- +
-boost::python에서는 try - catch 명령을 사용해 예외 처리를 할 수 있다. +
  
 <code c> <code c>
줄 78: 줄 77:
 </code> </code>
  
-렇게. +boost::python에서는 try-catch 명령을 사용해 예외 처리가 됩니다. 파썬 인터프리터에서 어떤 에러가 발생하면 ''error_already_set'' 예외가 발생합니다.
 <code c> <code c>
 try { try {
줄 87: 줄 85:
 } }
 </code> </code>
 +예외 상황이 발생했을 때 모든 ''PyObject''들이 ''boost::python::object''로 관리되었다면 더욱 좋겠죠.
 ==== 보다 편리한 객체 호출 및 리턴 값 ==== ==== 보다 편리한 객체 호출 및 리턴 값 ====
-객체 선언 +파이썬 객체를 호하기 위해 PyObject_CallObject, PyObject_CallMethod, 등의 함수보다는 ''()'' 연산자를 사용해 호출합니다. 이 덕에 객체의 함수 호출이 보다 자연스러운 C++ 코드 구문과 유사해 자연스럽습니다. 그리고 인자 목록 또한 암시적으로 타입 캐스팅이 가능해 더욱 편리합니다.
-값 추출.+
  
 +C API를 바로 이용하는 경우 객체에 대해 ''Py<//PyObjType//>_As<//TypeToExtract//>'' 스타일의 함수를 사용합니다. boost::python에서는 ''boost::python::extract''라는 템플릿 클래스를 제공하며, 이것은 파이썬 오브젝트 내부의 데이터에 대해 선언된 템플릿 타임으로의 암시적인 캐스팅을 해 줍니다.
 +
 +여기서 템플릿 타입에는 C/C++의 기본 자료형도 가능하고, boost::python이 따로 선언한 리스트나 딕셔너리를 위한 오브젝트 클래스로도 변형이 가능합니다.
 +
 +===== 예제: urllib을 C++에서 사용하기 =====
 +다음 코드는 C++에서 파이썬 'urllib'을 사용해 원격지의 자원을 로컬로 가져오는 예제입니다.
 +<code cpp get_html.cpp>
 +#include <boost/python.hpp>
 +#include <iostream>
 +#include <fstream>
 +
 +namespace bp = boost::python;
 +typedef bp::object bpobj;
 +
 +inline bpobj CreateObject(PyObject* op)
 +{
 +  if (op == NULL) throw bp::error_already_set();
 +  return bpobj(bp::handle<>(op));
 +}
 +
 +void PrintUsage(std::ostream& os)
 +{
 +  os << "< Save URL >\n";
 +  os << "get_html <url> <file_name>\n";
 +  os << "options:\n";
 +}
 +
 +int main(int argc, char** argv)
 +{
 +  if (argc < 3) {
 +    PrintUsage(std::cerr);
 +    return 1;
 +  }
 +
 +  if (Py_Initialize(), Py_IsInitialized()) {
 +    try {
 +      const char *url = argv[argc - 2];
 +      const char *file_name = argv[argc - 1];
 +
 +      bpobj urllib  = CreateObject(PyImport_ImportModule("urllib"));
 +      bpobj connobj = urllib.attr("urlopen")(url);
 +      bpobj htmlobj = connobj.attr("read")();
 +      connobj.attr("close")();
 +
 +      // save as file
 +      const char *html = bp::extract<const char*>(htmlobj);
 +      const int byte = bp::extract<const int>(htmlobj.attr("__len__")());
 +        
 +      std::ofstream os(file_name, std::ios::binary);
 +      
 +      if (os.is_open()) {
 +        os.write(html, byte);
 +        printf("%d bytes written to %s\n", byte, file_name);
 +        os.close();
 +      }
 +    }
 +    catch (bp::error_already_set const &) {
 +      PyErr_Print();
 +    }
 +    Py_Finalize();
 +  }
 +  return 0;
 +}
 +</code>
 +윈속이나 소켓이 같은 보다 저수준의 API은 파이썬 영역에서 처리되어 있습니다.
 +
 +===== 예제: finder C++ =====
 +C API 기반의 [[.:embeddedpythonusingcapi#finder C 코드]]를 boost::python을 이용해 고쳐 보았습니다.
 +
 +<code cpp boost_finder.cpp>
 +#include <boost/python.hpp>
 +#include <iostream>
 +
 +namespace bp = boost::python;
 +typedef bp::object bpobj;
 +
 +inline bpobj createObject(PyObject* op)
 +{
 +  if (op == NULL) {
 +    throw bp::error_already_set();
 +  }
 +
 +  return bpobj(bp::handle<>(op));
 +}
 +
 +void boostFinder(const char* url)
 +{
 +  // import module
 +  bpobj module = createObject(PyImport_ImportModule("scripts.finder"));
 +
 +  // get class object, instantiate the class
 +  bpobj klass = module.attr("url_finder")(url);
 +
 +  // url_finder.links
 +  bp::list links = bp::extract<bp::list>(klass.attr("links"));
 +  
 +  if (PyList_Check(links.ptr()))
 +  {
 +    typedef Py_ssize_t psz_t;
 +
 +    const psz_t linkSize = Py_SIZE(links.ptr());
 +    for (psz_t i = 0; i < linkSize; ++i)
 +    {
 +      // borrowed reference
 +      std::cout << PyString_AsString(PyList_GET_ITEM(links.ptr(), i))
 +                << '\n';
 +    }
 +  }
 +}
 +
 +int main(int argc, char **argv)
 +{
 +  if (argc < 2) {
 +    std::cerr << "specify a url.\n";
 +    return 1;
 +  }
 +
 +  if (Py_Initialize(), Py_IsInitialized())
 +  {
 +    try
 +    {
 +      PySys_SetArgv(argc, argv);
 +      boostFinder(argv[1]);
 +    }
 +    catch (bp::error_already_set const &)
 +    {
 +      PyErr_Print();
 +    }
 +    Py_Finalize();
 +  }
 +  return 0;
 +}
 +</code>
  
-===== 썬 기본 자료형 다루기 ===== +===== 참고 사트 ===== 
-==== Type Casting ==== +  * [[https://wiki.python.org/moin/boost.python|파이썬 위키]]
-==== List ==== +
-==== Tuple ==== +
-==== Dictonary ====+
project/embeddedpythonboostpython.1393934561.txt.gz · 마지막으로 수정됨: 2014/10/09 21:23 (바깥 편집)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki