사용자 도구

사이트 도구


project:embeddedpythonboostpython

문서의 이전 판입니다!


Embedded Python: Boost Python

boost::python은 파이썬 확장(extended python)에 보다 초점을 맞춰져 있는 라이브러리입니다. 완전하게 임베딩을 완전히 지원하지는 않는다고 합니다1). 지원하지 않는 부분들은 직접 C API를 활용하여야 합니다. 그렇지만 그것만으로도 훨씬 더 생산성을 높일 수 있습니다. 사용할 만한 가치는 충분합니다.

Boost Python으로 개선되는 점

자동 레퍼런스 카운팅

PyObject레퍼런스 카운팅 개념은 사실 boost::shared_ptr의 개념과 매우 흡사합니다. 다만 C 언어의 한계로 인해 자동으로 객체의 생성, 소멸시 객체가 자동으로 호출되는 함수가 지원되지 않아 결국 그러한 카운팅을 프로그래머가 직접 해야 합니다. 매우 성가시고 불편합니다.

boost::python을 사용하면 boost::python::object라는 PyObject의 래퍼를 사용할 수 있습니다. 이를 통해 더이상 프로그래머가 까다로운 레퍼런스 카운팅을 신경쓰지 않아도 됩니다. 변수의 범위를 벗어나면 자동으로 카운터를 내려주고, 다른 레퍼런스에 대입되면 카운터를 올려줍니다.

자동 레퍼런스 카운팅 테스트

진짜 동작하는지 정말 확인해 보고자 다음과 같은 실험을 해 보았습니다.

  1. Py_INCREF, Py_DECREF는 매크로입니다. 헤더에 선언되어 있으므로 소스를 재컴파일하지 않아도 헤더 파일에 쓰기 권한만 있으면 접근하여 수정할 수 있습니다. 그러므로 여기 접근하여 살짝 변경해 호출이 되는 것 확인할 수 있도로 합니다.
  2. boost::python::object를 생성하여 레퍼런스 카운트를 직접 확인해 봅니다.
  3. 종료될 때 명시적으로 Py_DECREF를 호출하지 않아도 호출되는지를 확인합니다.

우선 원래의 파이썬 소스의 헤더를 살짝 변경해 보았습니다.

/* object.h */
#include <stdio.h>
#define Py_INCREF(op) do { printf("Py_INCREF\n"); (     \    // do-while 삽입 후 'Py_INCREF' 문자열 출력
    _Py_INC_REFTOTAL  _Py_REF_DEBUG_COMMA               \
    ((PyObject*)(op))->ob_refcnt++); } while(0)
 
#define Py_DECREF(op)                                   \
    do {                                                \
        if (_Py_DEC_REFTOTAL  _Py_REF_DEBUG_COMMA       \
        --((PyObject*)(op))->ob_refcnt != 0)            \
            _Py_CHECK_REFCNT(op)                        \
        else { printf("_Py_Dealloc\n");                 \    // else에 {} 삽입 후 _Py_Dealloc' 문자열 출력
        _Py_Dealloc((PyObject *)(op));  }               \
    } while (0)

이렇게 코드를 변경해 두면, 어느 시점에서 레퍼런스 카운터가 늘어나면 “Py_INCREF” 문자열이 출력될 것이고, 레퍼런스 카운터가 0이 되면 “_Py_Dealloc” 문자열이 출력될 것입니다. 그러면 소스 코드를 작성해 봅니다.

boost_pyhthon.cpp
#include <boost/python.hpp>
#include <Python.h>
#include <iostream>
 
int main(int argc, char** argv) 
{
  Py_Initialize();
  //PyObject* naive = PyString_FromString("naive_string");
  boost::python::object
    bpobj(
      boost::python::handle<>(
        PyString_FromString("boost::python handling object")));
 
  boost::python::object
    bpobj_another = bpobj;
 
  //if (naive)
  //  Py_XDECREF(naive);
  Py_Finalize();
  return 0;
}

boost::python이 생성하는 객체를 하나 만들고, 다른 객체와 자원을 공유해 본 다음 프로그램을 종료합니다. 위 코드에서 우리가 직접 관리하는 PyObject 레퍼런스는 주석 처리를 하였습니다. 결과를 비교해 볼 수 있을 것입니다. 이 코드를 실행하면 다음과 같은 결과가 나옵니다.

Py_INCREF
Py_INCREF
_Py_Dealloc

첫번째 Py_INCREF는 문자열 하나가 생성되면서 객체의 카운트가 1이 될 때 출력된 것이라 추측할 수 있습니다. 두번째 Py_INCREF는 boost::python::object끼리 대입이 되면서 레퍼런스 카운트가 올라간 것이죠. 마지막에 예상대로 객체가 해제되면서 _Py_Dealloc이 불립니다. 확인이 되었습니다.

Try - Catch를 이용한 예외 처리

C API를 사용하는 경우, 모든 값에 대해 에러 체크를 명시적으로 수행해야 했다. 이렇게 프로그래밍을 짜면 if ~ else의 철장벽이 겹겹이 쌓이게 된다. 2~3중으로 쌓인 if 문 안에서 에러가 나는 경우, 안전하게 에러 처리를 하기가 너무 어렵고, 어쩔 수 없이 goto 문에 의지하는 경우도 종종 발생한다.

boost::python에서는 try - catch 명령을 사용해 예외 처리를 할 수 있다.

if ( Py_SomeFunc != NULL ) {
  // 운이 좋다면
} else {
  // 여기서 자원 회수를 비롯한 에러 처리를 해야 한다.
}

이렇게.

try {
  ...
} catch (boost::python::error_already_set const &) {
 // 에러 처리
}

보다 편리한 객체 호출 및 리턴 값

객체 선언 값 추출.

파이썬 기본 자료형 다루기

Type Casting

List

Tuple

Dictonary

project/embeddedpythonboostpython.1393935018.txt.gz · 마지막으로 수정됨: 2014/10/09 21:23 (바깥 편집)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki