Skip to main content

C++ of the Day #9 - Boost.Python 사용하기 #1

Python은 가장 인기있는 interpret 언어중의 하나입니다. Python의 장점 중 하나는 C/C++ 모듈과 쉽게 연동할 수 있다는 점입니다. 물론 손으로 일일히 wrapper를 만드는 것은 손이 많이 가고 에러를 만들수 있는 작업이나 SWIG등과 같은 도구를 사용하면 쉽게 python 모듈을 만들 수 있습니다.

Boost.Python은 이런 SWIG와 같이 python 모듈을 쉽게 만들 수 있도록 도와주는 라이브러리로 순수 C++만을 사용한다는 점이 SWIG와 다른 점입니다. 그리고 개인적으로는 Boost 라이브러리에 포함되어 있는 것들이 왠지 좀 더 믿음직스러워서... :-)

이번 글에서는 Boost.Python 문서에 나와 있는 예제를 가지고 간단하게 python 모듈을 만드는 방법에 대해서 알아보겠습니다.

Requirements

  1. 리눅스
    이 글에서는 리눅스 환경에서의 사용 방법을 설명한다.
  2. Boost.Python 라이브러리 (1.33.1)
    Boost 라이브러리를 다운로드받아 아래와 유사한 명령으로 라이브러리를 빌드한다.
    bjam -sTOOLS=gcc -with-python install

    bjam의 --prefix 옵션으로 라이브러리가 설치될 위치를 변경할 수 있다.
  3. Python 라이브러리 (2.4.3)
    Python을 다운로드 받아 빌드하여 설치한다.
    위의 경우와 유사하게 configure의 --prefix 옵션으로 설치될 위치를 변경할 수 있다.


Write C++ Code

다음과 같이 코드를 작성한다.

// greet.cpp
#include <stdexcept>

char const* greet(unsigned x)
{
  static char const* const msgs[] = { "hello", "Boost.Python", "world!" };
  if (x > 2) 
    throw std::range_error("greet: index out of range");
  return msgs[x];
}

#include <boost python.hpp>
using namespace boost::python;
BOOST_PYTHON_MODULE(hello)
{
  def("greet", greet, "return one of 3 parts of a greeting");
}

일반적으로는 greet 함수 아래쪽의 wrapper와 같은 코드는 다른 파일에 작성하는 것이 보통이나 여기서는 편의를 위해 한 파일에 작성하였다.

Make Module

여기서는 python에서 제공하는 distutils를 사용하는 방법을 설명한다.
  1. 먼저 아래와 같이 setup.py를 작성한다.
    from distutils.core import setup, Extension
    
    module1 = Extension('hello',
        include_dirs = ['<your path="">/include/boost-1_33_1'],
        libraries = ['boost_python'],
        library_dirs = ['<your path="">/lib'],
        sources = ['greet.cpp'])
    
    setup (name = 'hello',
    version = '1.0',
    description = 'This is a demo package',
    ext_modules = [module1])
    
  2. 다음과 같이 setup.py를 실행시킨다.
    python setup.py build
    
  3. ./build/lib.linux-i686-2.2 와 같은 디렉토리에 만들어진 hello.so 파일을 적당한 디렉토리에 복사한다.

Use Module

이제 hello 모듈을 일반 python 모듈과 같은 방법으로 사용할 수 있다.

>>> import hello
>>> for x in range(3):
...     print hello.greet(x)
... 
hello
Boost.Python
world!
>>> hello.greet(4)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
RuntimeError: greet: index out of range

위의 예제에서 보듯이 C++에서 작성한 예외도 그대로 python 모듈에 적용되는 것을 알 수 있다.

Conclusion

이번에는 간단한 예제 코드를 가지고 Boost.Python의 사용 방법을 알아 보았습니다. 성능이 필요한 core 모듈은 C++로 작성하고 대부분의 glue 코드나 UI 코드는 python으로 작성하는 방법은 개발의 속도를 매우 높여줄 수 있을 것입니다.
저도 아직 이런 식으로 프로젝트를 해본적은 없지만 예전에 특정 라이브러리를 위한 python 모듈을 일일히 손으로 만들어본 적이 있었는데 이 라이브러리를 그때 알았더라면 하는 생각이 드네요. ;-)

Comments

  1. luabind(http://luabind.sourceforge.net)는 boost.python스타일로 lua를 C++에서 쉽게 쓸 수 있도록 해주는 라이브러리랍니다. :)

    이런 류의 시도가 많이 이루어 지는 듯.

    ps. SWIG와 비교해보면 boost.python이 훨씬 편한 것 같네요. :)

    ReplyDelete
  2. Lua라니요.. ㅠㅠ 요새 스크립트 언어가 유행하면서 python에 ruby에 개인적으로 요새는 또 lisp을 보고 있는데 또 lua라니요. 흑흑... 요놈은 그냥 모른척 할랍니다.
    이것저것 보다보니 인지도니 문법이니 따지다 보니까 개인적으로 사용할 메인 스크립트 언어는 역시 python인듯 합니다. Lisp은 한번쯤 공부해두어야 할 것 같은 언어인 것 같고 ruby도 재밌는 문법이 많은데 왠지 python이 RISC라면 ruby는 CISC같은 느낌이랄까요?
    그리고 Boost.Python을 이용한 pyste라는 프로젝트도 boost에 있습니다. 이건 export할 함수나 클래스 이름만 적어주면 알아서 Boost.Python 코드를 만들어주는 code generator랍니다. 이 프로젝트가 완성되면 진짜 SWIG보다 편하다고 말할 수 있게 되겠지요. ;-)

    ReplyDelete
  3. c++로 웹사이트를 scraping 하는 응용 프로그램을 개발중입니다.
    소스를 하나씩 뜯어서 하다..이건 아니다 싶어
    쓸만한 parser를 찾다가 pyhon의 BeautifulSoup 를 알게 되었습니다.
    BeautifulSoup을 c++ 에서 사용할려면 어떻게 해야 하나여?
    boost.python과의 관계는 어떻게 되는거져?
    도움 부탁드립니다.

    ReplyDelete
  4. boost.python은 애석하게도 C++에서의 python embedding은 지원하지 않습니다. 원하시는 내용은 아마 http://docs.python.org/ext/embedding.html 에서 찾을 수 있을 것 같습니다.

    웹사이트를 스크래핑하여 파일로 저장하는 코드를 python으로 작성하고 C++에서는 그 python 코드를 실행시켜 만들어진 파일을 사용하게 하는 방법 정도가 제일 간단하지 않을까 싶네요. :-)

    문법이 정확히 정의된 경우(BNF등으로) boost.spirit을 사용할 수도 있겠으나 html 같은 경우는 좀 힘들 것 같습니다. :-|

    ReplyDelete

Post a Comment

Popular posts from this blog

1의 개수 세기 - 해답

벌써 어제 말한 내일이 되었는데 답을 주신 분이 아무도 없어서 좀 뻘쭘하네요. :-P 그리고 어제 문제에 O(1)이라고 적었는데 엄밀히 얘기하자면 O(log 10 n)이라고 적었어야 했네요. 죄송합니다. ... 문제를 잠시 생각해보면 1~n까지의 수들 중 1의 개수를 얻기 위해서는 해당 숫자 n의 각 자리의 1의 개수가 모두 몇개나 될지를 구해서 더하면 된다는 사실을 알 수 있습니다. 예를 들어 13이라는 수를 생각해 보면 1~13까지의 수에서 1의 자리에는 1이 모두 몇개나 되는지와 10의 자리에는 모두 몇개나 되는지를 구해 이 값을 더하면 됩니다. 먼저 1의 자리를 생각해 보면 1, 11의 두 개가 있으며 10의 자리의 경우, 10, 11, 12, 13의 네 개가 있습니다. 따라서 2+4=6이라는 값을 구할 수 있습니다. 이번엔 234라는 수에서 10의 자리를 예로 들어 살펴 보겠습니다. 1~234라는 수들 중 10의 자리에 1이 들어가는 수는 10, 11, ..., 19, 110, 111, ... 119, 210, 211, ..., 219들로 모두 30개가 있음을 알 수 있습니다. 이 규칙들을 보면 해당 자리수의 1의 개수를 구하는 공식을 만들 수 있습니다. 234의 10의 자리에 해당하는 1의 개수는 ((234/100)+1)*10이 됩니다. 여기서 +1은 해당 자리수의 수가 0이 아닌 경우에만 더해집니다. 예를 들어 204라면 ((204/100)+0)*10으로 30개가 아닌 20개가 됩니다. 이런 방식으로 234의 각 자리수의 1의 개수를 구하면 1의 자리에 해당하는 1의 개수는 ((234/10)+1)*1=24개가 되고 100의 자리에 해당하는 개수는 ((234/1000)+1)*100=100이 됩니다. 이들 세 수를 모두 합하면 24+30+100=154개가 됩니다. 한가지 추가로 생각해야 할 점은 제일 큰 자리의 수가 1인 경우 위의 공식이 아닌 다른 공식이 필요하다는 점입니다. 예를 들어 123에서 100의 자리에 해당하는 1의 개수는 ((123/1...

CodeHighlighter plugin test page.

This post is for testing CodeHighlighter plugin which uses GeSHi as a fontifier engine. ((Those code blocks are acquired from Google Code Search .)) ((For more supported languages, go CodeHighlighter plugin or GeSHi homepage.)) C++ (<pre lang="cpp" lineno="1">) class nsScannerBufferList { public: /** * Buffer objects are directly followed by a data segment. The start * of the data segment is determined by increment the |this| pointer * by 1 unit. */ class Buffer : public PRCList { public: Buffer() { ++index_; } PHP (<pre lang="php" lineno="4">) for ($i = 0; $i $value = ord( $utf8_string[ $i ] ); if ( $value < 128 ) { // ASCII $unicode .= chr($value); } else { if ( count( $values ) == 0 ) { $num_octets = ( $value } $values[] = $value; Lisp (<pre lang="lisp">) ;;; Assignment (define-caller-pattern setq ((:star var fo...

C++ of the Day #43 - SQLite3 C++ wrapper #1

The Definitive Guide to SQLite 를 읽다가 공부 겸 해서 C++ wrapper를 만들어 보았습니다. 최대한 C++ 냄새(?)가 나도록 만들어 보았습니다. :-) ((SQLite는 복잡한 관리가 필요없이 사용가능한, 파일이나 메모리 기반의, 라이브러리로 제공되는, 약 250kb 용량의, 대부분의 SQL92문을 지원하는, open source RDB입니다.)) 이 wrapper를 사용하기 위해서는 (당연하게도!) sqlite3 와 (당연하게도?) boost 라이브러리가 필요합니다. 사용 예들을 살펴보는 것으로 설명을 대신합니다. 이번 글에서는 다음과 같은 contacts 테이블이 test.db에 존재한다고 가정합니다. CREATE TABLE contacts ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, phone TEXT NOT NULL, UNIQUE(name, phone) ); Command 먼저 test.db 파일을 사용하기 위해 다음과 같이 파일 이름을 주어 connection 객체를 생성합니다. 생성과 동시에 test.db와 연결이 이루어집니다. ((생성자외에 open() 함수를 사용할 수도 있습니다.)) sqlite3pp::connection conn("test.db"); 다음은 contacts 테이블에 정보를 추가하는 가장 간단한 방법입니다. connection 클래스에서 제공하는 execute 함수를 사용합니다. ((executef 함수를 사용하면 printf와 같은 문법을 사용하여 query문을 작성할 수 있습니다.)) conn.execute("INSERT INTO contacts (name, phone) VALUES ('user', '1234')"); 위와 동일한 작업을 parameterized query를 사용하여 할 수도 있습니다. ((step()함수가 실제 query문을 수행하는 함수입니다. ...