2010-08-27

boost.python: how to pass a python object to C++ world and how to return a C++ created object to the python interpreter

This is a programming language story. If you are not interested in such theme, see you other time...

I usually use ruby for scripting, however, in industry python is quite widely used. When I was a student, I was just interested in programming languages, yet I just interested in them for a few weeks and I did not use most of them. But, recently I even did not look into, I feel I become old... This time, I will try what so called ''python.'' (In my Japanese Web, this is ''皆のすなる python というものを,'' this is beginning of Tosa-nikki by Kino Turayuki, established around 935. Sadly, I can not translate this well by my poor English skill.)

First I read a book, Learning Python by Marc Lutz. I took around two weeks, it is fun and I find python interesting. Then this week, I started to implement a program.

There are a lot of introduction web pages of python, so, it is not worth to add something similar to the net, the readers will be also bored such blog entry. Therefore, I will go into a bit detail. I will write about boost.python. This week, I am developing a python binding of a C++ library using boost.python.

boost.python is a library that you can call python interpreter from C++ or you can call C++ function from python interpreter. I would like to extend python with binding a C++ library.

The documentation of boost.python is quite well, but the examples are a bit less. Although, not so many people use boost.python compared with usual python users. I think the documentation is well done considering that. I hope the following my code would fill this gap a bit.

The code (passobj_mod.cpp) is an example how to pass a python dictionary from the interpreter to C++ code, and also pass a C++ created object to the python interpreter. I find boost.python itself is amazingly well designed and developed. I can easily implement these kind of task, though I took a day to find how to do that.

---
/// passobj_mod.cpp: How to pass the python dict to the C++ fucntion/method.
///
/// Copyright (C) 2010 Shitohichi Umaya
///
/// test for using C++ class from python: pass a string or a dict to a
/// C++ method

#include <boost/python.hpp>
#include <boost/python/object.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/list.hpp>
#include <boost/python/dict.hpp>
#include <boost/python/str.hpp>

#include <stdexcept>
#include <iostream>

/// using namespace only for example
using namespace boost::python;

/// C++ object which python can instantiate
class PassObj {
public:
    /// constructor
    PassObj()
    {
        // empty
    }

    /// pass a python object, but this should be a python dictionary.
    /// \param[in] pydict a dictionary
    void pass_dict(object pydict) const {
        extract< dict > cppdict_ext(pydict);
        if(!cppdict_ext.check()){
            throw std::runtime_error(
                "PassObj::pass_dict: type error: not a python dict.");
        }

        dict cppdict = cppdict_ext();
        list keylist = cppdict.keys();

        // careful with boost name. there already have a conflict.
        int const len = boost::python::len(keylist);
        std::cout << "len(keylist) = " << len << std::endl;
        for(int i = 0; i < len; ++i){
            // operator[] is in python::boost::object
            std::string keystr = extract< std::string >(str(keylist[i]));
            std::string valstr = extract< std::string >(str(cppdict[keylist[i]]));
            std::cout << "key:[" << keystr << "]->[" << valstr << "]" << std::endl;
        }
    }

    /// pass a python object, but this should be a python string.
    /// \param[in] pydict a string
    void pass_string(object pystr) const {
        extract< std::string > cppstr_ext(pystr);
        if(!cppstr_ext.check()){
            throw std::runtime_error(
                "PassObj::pass_str: type error: not a python string.");
        }
        std::string cppstr = cppstr_ext();
        std::cout << "passed string: " << cppstr << std::endl;
    }


    /// return a dict object. Does this works?
    /// \return a dict object
    object return_dict() const {
        dict cppdict;
        cppdict["this"] = "work?";
        cppdict["no"]   = "idea";
        cppdict["number"]   = 1;

        return cppdict;
    }

    /// return a dict object. Does this works?
    /// \return a dict object
    object return_string() const {
        return str("Incredible, this works.");
    }

private:

};

/// importing module name is 'passobj_mod'
BOOST_PYTHON_MODULE(passobj_mod)
{
    class_<PassObj>("passobj")
        .def("pass_dict",
             &PassObj::pass_dict,
             "pass python dict object to c++ method")
        .def("pass_string",
             &PassObj::pass_string,
             "pass python string to c++ method")
        .def("return_dict",
             &PassObj::return_dict,
             "return C++ created dict to python")
        .def("return_string",
             &PassObj::return_string,
             "return C++ created str to python")
        ;
}

---
#
# test_passobj_mod.py
# test pass object module, python side implementation
# Copyright (C) 2010 Shitohichi Umaya
#
import passobj_mod

pobj = passobj_mod.passobj()
print dir(pobj)
pobj.pass_string('This is python string, can you hear me?')
pdict = {'pythondict': 1, 'foo': 'bar', 'Bach': 'Goldberg Variation' }
pobj.pass_dict(pdict)
dict_from_cpp = pobj.return_dict()
str_from_cpp  = pobj.return_string()
---
#! /bin/sh -x
# build.sh
# Copyright (C) 2010 Shitohichi Umaya
#
PYTHON_INCLUDE=`python-config --includes`
MOD_CPP_SOURCE_BASE=passobj_mod

g++ ${PYTHON_INCLUDE} -DPIC -shared -fPIC ${MOD_CPP_SOURCE_BASE}.cpp -o ${MOD_CPP_SOURCE_BASE}.so -lboost_python
---

test_passobj_mod.py is a test example to call the methods of PassObj in passobj_mod.cpp. build.sh is a sh script to build the python module passobj_mod.so. If you create the files (with chmod) and type:

  # build.sh
  # python test_passobj_mod.py

then, I hope you can try that. I tested this on Ubuntu Linux 9.04.

This is my first python script except the examples in the book. There is no class, no def. However, I still find a fun to write a program and it is always rewarding a program runs as I expected.

No comments: